Compare commits
1 Commits
revert/aut
...
feat/expor
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c11b592543 |
93
.github/workflows/codeql-analysis.yml
vendored
93
.github/workflows/codeql-analysis.yml
vendored
@@ -1,63 +1,72 @@
|
|||||||
name: "CodeQL analysis"
|
# For most projects, this workflow file will not need changing; you simply need
|
||||||
|
# to commit it to your repository.
|
||||||
|
#
|
||||||
|
# You may wish to alter this file to override the set of languages analyzed,
|
||||||
|
# or to provide custom queries or build logic.
|
||||||
|
#
|
||||||
|
# ******** NOTE ********
|
||||||
|
# We have attempted to detect the languages in your repository. Please check
|
||||||
|
# the `language` matrix defined below to confirm you have the correct set of
|
||||||
|
# supported CodeQL languages.
|
||||||
|
#
|
||||||
|
name: "CodeQL"
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [main]
|
branches: [ main ]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [main]
|
# The branches below must be a subset of the branches above
|
||||||
|
branches: [ main ]
|
||||||
schedule:
|
schedule:
|
||||||
# ┌───────────── minute (0 - 59)
|
- cron: '39 7 * * 2'
|
||||||
# │ ┌───────────── hour (0 - 23)
|
|
||||||
# │ │ ┌───────────── day of the month (1 - 31)
|
|
||||||
# │ │ │ ┌───────────── month (1 - 12 or JAN-DEC)
|
|
||||||
# │ │ │ │ ┌───────────── day of the week (0 - 6 or SUN-SAT)
|
|
||||||
# │ │ │ │ │
|
|
||||||
# │ │ │ │ │
|
|
||||||
# │ │ │ │ │
|
|
||||||
# * * * * *
|
|
||||||
- cron: '30 1 * * 0'
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
analyze:
|
analyze:
|
||||||
name: Analyze
|
name: Analyze
|
||||||
# CodeQL runs on ubuntu-latest, windows-latest, and macos-latest
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
# required for all workflows
|
|
||||||
security-events: write
|
|
||||||
|
|
||||||
# only required for workflows in private repositories
|
|
||||||
actions: read
|
actions: read
|
||||||
contents: read
|
contents: read
|
||||||
|
security-events: write
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
language: [ 'javascript' ]
|
||||||
|
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
||||||
|
# Learn more about CodeQL language support at https://git.io/codeql-language-support
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@v2
|
uses: github/codeql-action/init@v2
|
||||||
with:
|
with:
|
||||||
# Run extended queries including queries using machine learning
|
# Run extended queries including queries using machine learning
|
||||||
queries: security-extended
|
queries: security-extended
|
||||||
languages: ${{ matrix.language }}
|
languages: ${{ matrix.language }}
|
||||||
|
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||||
|
# By default, queries listed here will override any specified in a config file.
|
||||||
|
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||||
|
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||||
|
|
||||||
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java).
|
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||||
# If this step fails, then you should remove it and run the build manually (see below).
|
# If this step fails, then you should remove it and run the build manually (see below)
|
||||||
- name: Autobuild
|
- name: Autobuild
|
||||||
uses: github/codeql-action/autobuild@v2
|
uses: github/codeql-action/autobuild@v2
|
||||||
|
|
||||||
# ℹ️ Command-line programs to run using the OS shell.
|
# ℹ️ Command-line programs to run using the OS shell.
|
||||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
# 📚 https://git.io/JvXDl
|
||||||
|
|
||||||
# ✏️ If the Autobuild fails above, remove it and uncomment the following
|
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
||||||
# three lines and modify them (or add more) to build your code if your
|
# and modify them (or add more) to build your code if your project
|
||||||
# project uses a compiled language
|
# uses a compiled language
|
||||||
|
|
||||||
#- run: |
|
#- run: |
|
||||||
# make bootstrap
|
# make bootstrap
|
||||||
# make release
|
# make release
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@v2
|
uses: github/codeql-action/analyze@v2
|
||||||
|
|||||||
41
.github/workflows/deploy-netlify-ui.yml
vendored
41
.github/workflows/deploy-netlify-ui.yml
vendored
@@ -1,41 +0,0 @@
|
|||||||
name: Deploy to Netlify (ui)
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [main]
|
|
||||||
# run this workflow only if an update is made to the ui package
|
|
||||||
paths:
|
|
||||||
- "packages/hoppscotch-ui/**"
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
deploy:
|
|
||||||
name: Deploy
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Setup environment
|
|
||||||
run: mv .env.example .env
|
|
||||||
|
|
||||||
- name: Setup pnpm
|
|
||||||
uses: pnpm/action-setup@v2.2.4
|
|
||||||
with:
|
|
||||||
version: 7
|
|
||||||
run_install: true
|
|
||||||
|
|
||||||
- name: Setup node
|
|
||||||
uses: actions/setup-node@v3
|
|
||||||
with:
|
|
||||||
node-version: ${{ matrix.node }}
|
|
||||||
cache: pnpm
|
|
||||||
|
|
||||||
- name: Build site
|
|
||||||
run: pnpm run generate-ui
|
|
||||||
|
|
||||||
# Deploy the ui site with netlify-cli
|
|
||||||
- name: Deploy to Netlify (ui)
|
|
||||||
run: npx netlify-cli deploy --dir=packages/hoppscotch-ui/.histoire/dist --prod
|
|
||||||
env:
|
|
||||||
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_UI_SITE_ID }}
|
|
||||||
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
|
||||||
@@ -1,33 +1,27 @@
|
|||||||
name: Deploy to Netlify (production)
|
name: Deploy to Netlify
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [main]
|
branches: [main]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
deploy:
|
build:
|
||||||
name: Deploy
|
name: Push build files to Netlify
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout Repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Setup environment
|
- name: Setup Environment
|
||||||
run: mv .env.example .env
|
run: mv .env.example .env
|
||||||
|
|
||||||
- name: Setup pnpm
|
- name: Setup and run pnpm install
|
||||||
uses: pnpm/action-setup@v2.2.4
|
uses: pnpm/action-setup@v2.2.4
|
||||||
with:
|
with:
|
||||||
version: 7
|
version: 7
|
||||||
run_install: true
|
run_install: true
|
||||||
|
|
||||||
- name: Setup node
|
- name: Build Site
|
||||||
uses: actions/setup-node@v3
|
|
||||||
with:
|
|
||||||
node-version: ${{ matrix.node }}
|
|
||||||
cache: pnpm
|
|
||||||
|
|
||||||
- name: Build site
|
|
||||||
env:
|
env:
|
||||||
VITE_SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
|
VITE_SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
|
||||||
VITE_SENTRY_ENVIRONMENT: production
|
VITE_SENTRY_ENVIRONMENT: production
|
||||||
@@ -41,7 +35,7 @@ jobs:
|
|||||||
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_PRODUCTION_SITE_ID }}
|
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_PRODUCTION_SITE_ID }}
|
||||||
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
||||||
|
|
||||||
- name: Create Sentry release
|
- name: Create Sentry Release
|
||||||
uses: getsentry/action-release@v1
|
uses: getsentry/action-release@v1
|
||||||
env:
|
env:
|
||||||
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||||
60
.github/workflows/deploy-preview-netlify.yml
vendored
Normal file
60
.github/workflows/deploy-preview-netlify.yml
vendored
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
name: Deploy to Preview Netlify
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Push build files to Netlify
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout Repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Setup and run pnpm install
|
||||||
|
uses: pnpm/action-setup@v2.2.4
|
||||||
|
env:
|
||||||
|
VITE_BACKEND_GQL_URL: ${{ secrets.STAGING_BACKEND_GQL_URL }}
|
||||||
|
with:
|
||||||
|
version: 7
|
||||||
|
run_install: true
|
||||||
|
|
||||||
|
- name: Build Site
|
||||||
|
env:
|
||||||
|
VITE_GA_ID: ${{ secrets.STAGING_GA_ID }}
|
||||||
|
VITE_GTM_ID: ${{ secrets.STAGING_GTM_ID }}
|
||||||
|
VITE_API_KEY: ${{ secrets.STAGING_FB_API_KEY }}
|
||||||
|
VITE_AUTH_DOMAIN: ${{ secrets.STAGING_FB_AUTH_DOMAIN }}
|
||||||
|
VITE_DATABASE_URL: ${{ secrets.STAGING_FB_DATABASE_URL }}
|
||||||
|
VITE_PROJECT_ID: ${{ secrets.STAGING_FB_PROJECT_ID }}
|
||||||
|
VITE_STORAGE_BUCKET: ${{ secrets.STAGING_FB_STORAGE_BUCKET }}
|
||||||
|
VITE_MESSAGING_SENDER_ID: ${{ secrets.STAGING_FB_MESSAGING_SENDER_ID }}
|
||||||
|
VITE_APP_ID: ${{ secrets.STAGING_FB_APP_ID }}
|
||||||
|
VITE_BASE_URL: ${{ secrets.STAGING_BASE_URL }}
|
||||||
|
VITE_BACKEND_GQL_URL: ${{ secrets.STAGING_BACKEND_GQL_URL }}
|
||||||
|
VITE_BACKEND_WS_URL: ${{ secrets.STAGING_BACKEND_WS_URL }}
|
||||||
|
VITE_SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
|
||||||
|
VITE_SENTRY_RELEASE_TAG: ${{ github.sha }}
|
||||||
|
VITE_SENTRY_ENVIRONMENT: staging
|
||||||
|
run: pnpm run generate
|
||||||
|
|
||||||
|
# Deploy the preview site with netlify-cli
|
||||||
|
- name: Deploy to Netlify (preview)
|
||||||
|
run: npx netlify-cli deploy --dir=packages/hoppscotch-web/dist --alias=preview
|
||||||
|
env:
|
||||||
|
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_STAGING_SITE_ID }}
|
||||||
|
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
||||||
|
|
||||||
|
- name: Create Sentry Release
|
||||||
|
uses: getsentry/action-release@v1
|
||||||
|
env:
|
||||||
|
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||||
|
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
|
||||||
|
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
|
||||||
|
with:
|
||||||
|
environment: preview
|
||||||
|
ignore_missing: true
|
||||||
|
ignore_empty: true
|
||||||
|
version: ${{ github.sha }}
|
||||||
@@ -1,15 +1,15 @@
|
|||||||
name: Deploy to Firebase (production)
|
name: Deploy to Live Channel
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [main]
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
deploy:
|
deploy_live_website:
|
||||||
name: Deploy
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout Repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Deploy to Firebase (production)
|
- name: Deploy to Firebase (production)
|
||||||
@@ -1,20 +1,19 @@
|
|||||||
name: Deploy to Netlify (staging)
|
name: Deploy to Staging Netlify
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [staging]
|
# TODO: Migrate to staging branch only
|
||||||
pull_request:
|
branches: [main]
|
||||||
branches: [staging]
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
deploy:
|
build:
|
||||||
name: Deploy
|
name: Push build files to Netlify
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout Repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Setup pnpm
|
- name: Setup and run pnpm install
|
||||||
uses: pnpm/action-setup@v2.2.4
|
uses: pnpm/action-setup@v2.2.4
|
||||||
env:
|
env:
|
||||||
VITE_BACKEND_GQL_URL: ${{ secrets.STAGING_BACKEND_GQL_URL }}
|
VITE_BACKEND_GQL_URL: ${{ secrets.STAGING_BACKEND_GQL_URL }}
|
||||||
@@ -22,13 +21,7 @@ jobs:
|
|||||||
version: 7
|
version: 7
|
||||||
run_install: true
|
run_install: true
|
||||||
|
|
||||||
- name: Setup node
|
- name: Build Site
|
||||||
uses: actions/setup-node@v3
|
|
||||||
with:
|
|
||||||
node-version: ${{ matrix.node }}
|
|
||||||
cache: pnpm
|
|
||||||
|
|
||||||
- name: Build site
|
|
||||||
env:
|
env:
|
||||||
VITE_GA_ID: ${{ secrets.STAGING_GA_ID }}
|
VITE_GA_ID: ${{ secrets.STAGING_GA_ID }}
|
||||||
VITE_GTM_ID: ${{ secrets.STAGING_GTM_ID }}
|
VITE_GTM_ID: ${{ secrets.STAGING_GTM_ID }}
|
||||||
@@ -54,7 +47,7 @@ jobs:
|
|||||||
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_STAGING_SITE_ID }}
|
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_STAGING_SITE_ID }}
|
||||||
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
||||||
|
|
||||||
- name: Create Sentry release
|
- name: Create Sentry Release
|
||||||
uses: getsentry/action-release@v1
|
uses: getsentry/action-release@v1
|
||||||
env:
|
env:
|
||||||
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||||
17
.github/workflows/publish-docker.yml
vendored
17
.github/workflows/publish-docker.yml
vendored
@@ -7,20 +7,11 @@ on:
|
|||||||
types: [published]
|
types: [published]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
publish:
|
push_to_registry:
|
||||||
name: Publish
|
name: Push Docker image to Docker Hub
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Maximize build space
|
- name: Check out the repo
|
||||||
uses: easimon/maximize-build-space@master
|
|
||||||
with:
|
|
||||||
root-reserve-mb: 8192
|
|
||||||
swap-size-mb: 18432
|
|
||||||
remove-dotnet: 'true'
|
|
||||||
remove-android: 'true'
|
|
||||||
remove-haskell: 'true'
|
|
||||||
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
@@ -50,6 +41,6 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
push: true
|
push: true
|
||||||
platforms: linux/amd64,linux/arm64
|
platforms: linux/amd64,linux/arm64/v8,linux/arm/v7
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
|||||||
17
.github/workflows/tests.yml
vendored
17
.github/workflows/tests.yml
vendored
@@ -2,13 +2,12 @@ name: Node.js CI
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [main, staging]
|
branches: [main]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [main, staging]
|
branches: [main]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
build:
|
||||||
name: Test
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
@@ -16,22 +15,22 @@ jobs:
|
|||||||
node-version: ["lts/*"]
|
node-version: ["lts/*"]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout Repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Setup environment
|
- name: Setup Environment
|
||||||
run: mv .env.example .env
|
run: mv .env.example .env
|
||||||
|
|
||||||
- name: Setup pnpm
|
- name: Setup and run pnpm install
|
||||||
uses: pnpm/action-setup@v2.2.4
|
uses: pnpm/action-setup@v2.2.4
|
||||||
with:
|
with:
|
||||||
version: 7
|
version: 7
|
||||||
run_install: true
|
run_install: true
|
||||||
|
|
||||||
- name: Setup node
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node }}
|
node-version: ${{ matrix.node-version }}
|
||||||
cache: pnpm
|
cache: pnpm
|
||||||
|
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
|
|||||||
27
CODEOWNERS
27
CODEOWNERS
@@ -1,27 +0,0 @@
|
|||||||
# CODEOWNERS is prioritized from bottom to top
|
|
||||||
|
|
||||||
# If none of the below matched
|
|
||||||
* @AndrewBastin @liyasthomas
|
|
||||||
|
|
||||||
# Packages
|
|
||||||
/packages/codemirror-lang-graphql/ @AndrewBastin
|
|
||||||
/packages/hoppscotch-cli/ @aitchnyu
|
|
||||||
/packages/hoppscotch-common/ @amk-dev @AndrewBastin
|
|
||||||
/packages/hoppscotch-data/ @AndrewBastin
|
|
||||||
/packages/hoppscotch-js-sandbox/ @aitchnyu
|
|
||||||
/packages/hoppscotch-ui/ @anwarulislam
|
|
||||||
/packages/hoppscotch-web/ @amk-dev @AndrewBastin
|
|
||||||
|
|
||||||
# Sections within Hoppscotch Common
|
|
||||||
/packages/hoppscotch-common/src/components @anwarulislam
|
|
||||||
/packages/hoppscotch-common/src/components/collections @nivedin @amk-dev
|
|
||||||
/packages/hoppscotch-common/src/components/environments @nivedin @amk-dev
|
|
||||||
/packages/hoppscotch-common/src/composables @amk-dev
|
|
||||||
/packages/hoppscotch-common/src/modules @AndrewBastin @amk-dev
|
|
||||||
/packages/hoppscotch-common/src/pages @AndrewBastin @amk-dev
|
|
||||||
/packages/hoppscotch-common/src/newstore @AndrewBastin @amk-dev
|
|
||||||
|
|
||||||
README.md @liyasthomas
|
|
||||||
|
|
||||||
# The lockfile has no owner
|
|
||||||
pnpm-lock.yaml
|
|
||||||
@@ -36,14 +36,14 @@
|
|||||||
<p>
|
<p>
|
||||||
<a href="https://hoppscotch.io/#gh-light-mode-only" target="_blank">
|
<a href="https://hoppscotch.io/#gh-light-mode-only" target="_blank">
|
||||||
<img
|
<img
|
||||||
src="./packages/hoppscotch-common/public/images/banner-light.png"
|
src="./packages/hoppscotch-app/public/images/banner-light.png"
|
||||||
alt="Hoppscotch"
|
alt="Hoppscotch"
|
||||||
width="100%"
|
width="100%"
|
||||||
/>
|
/>
|
||||||
</a>
|
</a>
|
||||||
<a href="https://hoppscotch.io/#gh-dark-mode-only" target="_blank">
|
<a href="https://hoppscotch.io/#gh-dark-mode-only" target="_blank">
|
||||||
<img
|
<img
|
||||||
src="./packages/hoppscotch-common/public/images/banner-dark.png"
|
src="./packages/hoppscotch-app/public/images/banner-dark.png"
|
||||||
alt="Hoppscotch"
|
alt="Hoppscotch"
|
||||||
width="100%"
|
width="100%"
|
||||||
/>
|
/>
|
||||||
@@ -317,7 +317,7 @@ docker run --rm --name hoppscotch -p 3000:3000 hoppscotch/hoppscotch:latest
|
|||||||
3. Install dependencies by running `pnpm install` within the directory that you cloned (probably `hoppscotch`).
|
3. Install dependencies by running `pnpm install` within the directory that you cloned (probably `hoppscotch`).
|
||||||
4. Update [`.env.example`](https://github.com/hoppscotch/hoppscotch/blob/main/.env.example) file found in the root of repository with your own keys and rename it to `.env`.
|
4. Update [`.env.example`](https://github.com/hoppscotch/hoppscotch/blob/main/.env.example) file found in the root of repository with your own keys and rename it to `.env`.
|
||||||
5. Build the release files with `pnpm run generate`.
|
5. Build the release files with `pnpm run generate`.
|
||||||
6. Find the built project in `packages/hoppscotch-web/dist`. Host these files on any [static hosting servers](https://www.pluralsight.com/blog/software-development/where-to-host-your-jamstack-site).
|
6. Find the built project in `packages/hoppscotch-app/dist`. Host these files on any [static hosting servers](https://www.pluralsight.com/blog/software-development/where-to-host-your-jamstack-site).
|
||||||
|
|
||||||
## **Contributing**
|
## **Contributing**
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
[[headers]]
|
[[headers]]
|
||||||
for = "/*"
|
for = "/*"
|
||||||
[headers.values]
|
[headers.values]
|
||||||
X-Frame-Options = "SAMEORIGIN"
|
X-Frame-Options = "DENY"
|
||||||
X-XSS-Protection = "1; mode=block"
|
X-XSS-Protection = "1; mode=block"
|
||||||
|
|
||||||
[[redirects]]
|
[[redirects]]
|
||||||
|
|||||||
@@ -15,8 +15,7 @@
|
|||||||
"typecheck": "pnpm -r do-typecheck",
|
"typecheck": "pnpm -r do-typecheck",
|
||||||
"lintfix": "pnpm -r do-lintfix",
|
"lintfix": "pnpm -r do-lintfix",
|
||||||
"pre-commit": "pnpm -r do-lint && pnpm -r do-typecheck",
|
"pre-commit": "pnpm -r do-lint && pnpm -r do-typecheck",
|
||||||
"test": "pnpm -r do-test",
|
"test": "pnpm -r do-test"
|
||||||
"generate-ui": "pnpm -r do-build-ui"
|
|
||||||
},
|
},
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"./packages/*"
|
"./packages/*"
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ a {
|
|||||||
@apply shadow-none;
|
@apply shadow-none;
|
||||||
@apply fixed;
|
@apply fixed;
|
||||||
@apply inline-flex;
|
@apply inline-flex;
|
||||||
@apply -mt-8;
|
@apply -mt-6;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -501,10 +501,6 @@ pre.ace_editor {
|
|||||||
@apply rounded;
|
@apply rounded;
|
||||||
@apply ml-2;
|
@apply ml-2;
|
||||||
@apply px-1;
|
@apply px-1;
|
||||||
@apply min-w-5;
|
|
||||||
@apply min-h-5;
|
|
||||||
@apply items-center;
|
|
||||||
@apply justify-center;
|
|
||||||
@apply border border-dividerDark;
|
@apply border border-dividerDark;
|
||||||
@apply shadow-sm;
|
@apply shadow-sm;
|
||||||
@apply <sm:hidden;
|
@apply <sm:hidden;
|
||||||
@@ -538,16 +534,8 @@ details[open] summary .indicator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.env-highlight {
|
.env-highlight {
|
||||||
@apply text-accentContrast;
|
* {
|
||||||
|
@apply text-accentContrast;
|
||||||
&.env-found {
|
|
||||||
@apply bg-accentDark;
|
|
||||||
@apply hover:bg-accent;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.env-not-found {
|
|
||||||
@apply bg-red-500;
|
|
||||||
@apply hover:bg-red-600;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -203,9 +203,6 @@
|
|||||||
"browser_support_sse": "Dit lyk nie asof hierdie blaaier ondersteuning vir bedieners gestuurde geleenthede het nie.",
|
"browser_support_sse": "Dit lyk nie asof hierdie blaaier ondersteuning vir bedieners gestuurde geleenthede het nie.",
|
||||||
"check_console_details": "Kyk na die konsole -log vir meer inligting.",
|
"check_console_details": "Kyk na die konsole -log vir meer inligting.",
|
||||||
"curl_invalid_format": "cURL is nie behoorlik geformateer nie",
|
"curl_invalid_format": "cURL is nie behoorlik geformateer nie",
|
||||||
"danger_zone": "Danger zone",
|
|
||||||
"delete_account": "Your account is currently an owner in these teams:",
|
|
||||||
"delete_account_description": "You must either remove yourself, transfer ownership, or delete these teams before you can delete your account.",
|
|
||||||
"empty_req_name": "Leë versoeknaam",
|
"empty_req_name": "Leë versoeknaam",
|
||||||
"f12_details": "(F12 vir meer inligting)",
|
"f12_details": "(F12 vir meer inligting)",
|
||||||
"gql_prettify_invalid_query": "Kon nie 'n ongeldige navraag mooi maak nie, los sintaksisfoute op en probeer weer",
|
"gql_prettify_invalid_query": "Kon nie 'n ongeldige navraag mooi maak nie, los sintaksisfoute op en probeer weer",
|
||||||
@@ -440,7 +437,6 @@
|
|||||||
"settings": {
|
"settings": {
|
||||||
"accent_color": "Aksent kleur",
|
"accent_color": "Aksent kleur",
|
||||||
"account": "Rekening",
|
"account": "Rekening",
|
||||||
"account_deleted": "Your account has been deleted",
|
|
||||||
"account_description": "Pas u rekeninginstellings aan.",
|
"account_description": "Pas u rekeninginstellings aan.",
|
||||||
"account_email_description": "Jou primêre e -posadres.",
|
"account_email_description": "Jou primêre e -posadres.",
|
||||||
"account_name_description": "Dit is u vertoonnaam.",
|
"account_name_description": "Dit is u vertoonnaam.",
|
||||||
@@ -449,8 +445,6 @@
|
|||||||
"change_font_size": "Verander lettergrootte",
|
"change_font_size": "Verander lettergrootte",
|
||||||
"choose_language": "Kies taal",
|
"choose_language": "Kies taal",
|
||||||
"dark_mode": "Donker",
|
"dark_mode": "Donker",
|
||||||
"delete_account": "Delete account",
|
|
||||||
"delete_account_description": "Once you delete your account, all your data will be permanently deleted. This action cannot be undone.",
|
|
||||||
"expand_navigation": "Expand navigation",
|
"expand_navigation": "Expand navigation",
|
||||||
"experiments": "Eksperimente",
|
"experiments": "Eksperimente",
|
||||||
"experiments_notice": "Dit is 'n versameling eksperimente waaraan ons werk, wat nuttig, pret of beide kan wees. Hulle is nie finaal nie en is moontlik nie stabiel nie, dus moenie paniekerig raak as iets te vreemd gebeur nie. Skakel net die ding uit. Grappies eenkant,",
|
"experiments_notice": "Dit is 'n versameling eksperimente waaraan ons werk, wat nuttig, pret of beide kan wees. Hulle is nie finaal nie en is moontlik nie stabiel nie, dus moenie paniekerig raak as iets te vreemd gebeur nie. Skakel net die ding uit. Grappies eenkant,",
|
||||||
|
|||||||
@@ -203,9 +203,6 @@
|
|||||||
"browser_support_sse": "يبدو أن هذا المستعرض لا يدعم أحداث إرسال الخادم.",
|
"browser_support_sse": "يبدو أن هذا المستعرض لا يدعم أحداث إرسال الخادم.",
|
||||||
"check_console_details": "تحقق من سجل وحدة التحكم للحصول على التفاصيل.",
|
"check_console_details": "تحقق من سجل وحدة التحكم للحصول على التفاصيل.",
|
||||||
"curl_invalid_format": "لم يتم تنسيق cURL بشكل صحيح",
|
"curl_invalid_format": "لم يتم تنسيق cURL بشكل صحيح",
|
||||||
"danger_zone": "Danger zone",
|
|
||||||
"delete_account": "Your account is currently an owner in these teams:",
|
|
||||||
"delete_account_description": "You must either remove yourself, transfer ownership, or delete these teams before you can delete your account.",
|
|
||||||
"empty_req_name": "اسم الطلب فارغ",
|
"empty_req_name": "اسم الطلب فارغ",
|
||||||
"f12_details": "(للحصول على تفاصيل F12)",
|
"f12_details": "(للحصول على تفاصيل F12)",
|
||||||
"gql_prettify_invalid_query": "تعذر تحسين استعلام غير صالح وحل أخطاء بنية الاستعلام وحاول مرة أخرى",
|
"gql_prettify_invalid_query": "تعذر تحسين استعلام غير صالح وحل أخطاء بنية الاستعلام وحاول مرة أخرى",
|
||||||
@@ -440,7 +437,6 @@
|
|||||||
"settings": {
|
"settings": {
|
||||||
"accent_color": "لون التمييز",
|
"accent_color": "لون التمييز",
|
||||||
"account": "حساب",
|
"account": "حساب",
|
||||||
"account_deleted": "Your account has been deleted",
|
|
||||||
"account_description": "تخصيص إعدادات حسابك.",
|
"account_description": "تخصيص إعدادات حسابك.",
|
||||||
"account_email_description": "عنوان بريدك الإلكتروني الأساسي.",
|
"account_email_description": "عنوان بريدك الإلكتروني الأساسي.",
|
||||||
"account_name_description": "هذا هو اسم العرض الخاص بك.",
|
"account_name_description": "هذا هو اسم العرض الخاص بك.",
|
||||||
@@ -449,8 +445,6 @@
|
|||||||
"change_font_size": "تغيير حجم الخط",
|
"change_font_size": "تغيير حجم الخط",
|
||||||
"choose_language": "اختر اللغة",
|
"choose_language": "اختر اللغة",
|
||||||
"dark_mode": "داكن",
|
"dark_mode": "داكن",
|
||||||
"delete_account": "Delete account",
|
|
||||||
"delete_account_description": "Once you delete your account, all your data will be permanently deleted. This action cannot be undone.",
|
|
||||||
"expand_navigation": "توسيع التنقل",
|
"expand_navigation": "توسيع التنقل",
|
||||||
"experiments": "التجارب",
|
"experiments": "التجارب",
|
||||||
"experiments_notice": "هذه مجموعة من التجارب التي نعمل عليها والتي قد تكون مفيدة ، أو ممتعة ، أو كليهما ، أو لا شيء. إنها ليست نهائية وقد لا تكون مستقرة ، لذلك إذا حدث شيء غريب للغاية ، فلا داعي للذعر. فقط قم بإيقاف تشغيل الشيء. النكات جانبا،",
|
"experiments_notice": "هذه مجموعة من التجارب التي نعمل عليها والتي قد تكون مفيدة ، أو ممتعة ، أو كليهما ، أو لا شيء. إنها ليست نهائية وقد لا تكون مستقرة ، لذلك إذا حدث شيء غريب للغاية ، فلا داعي للذعر. فقط قم بإيقاف تشغيل الشيء. النكات جانبا،",
|
||||||
|
|||||||
@@ -203,9 +203,6 @@
|
|||||||
"browser_support_sse": "Sembla que aquest navegador no és compatible amb els Esdeveniments Enviats pel Servidor (Server Sent Events).",
|
"browser_support_sse": "Sembla que aquest navegador no és compatible amb els Esdeveniments Enviats pel Servidor (Server Sent Events).",
|
||||||
"check_console_details": "Consulta el registre de la consola per obtenir més informació.",
|
"check_console_details": "Consulta el registre de la consola per obtenir més informació.",
|
||||||
"curl_invalid_format": "cURL no està formatat correctament",
|
"curl_invalid_format": "cURL no està formatat correctament",
|
||||||
"danger_zone": "Danger zone",
|
|
||||||
"delete_account": "Your account is currently an owner in these teams:",
|
|
||||||
"delete_account_description": "You must either remove yourself, transfer ownership, or delete these teams before you can delete your account.",
|
|
||||||
"empty_req_name": "Nom de la sol·licitud buida",
|
"empty_req_name": "Nom de la sol·licitud buida",
|
||||||
"f12_details": "(F12 per obtenir més informació)",
|
"f12_details": "(F12 per obtenir més informació)",
|
||||||
"gql_prettify_invalid_query": "No s'ha pogut definir una consulta no vàlida, resoldre els errors de sintaxi de la consulta i tornar-ho a provar",
|
"gql_prettify_invalid_query": "No s'ha pogut definir una consulta no vàlida, resoldre els errors de sintaxi de la consulta i tornar-ho a provar",
|
||||||
@@ -440,7 +437,6 @@
|
|||||||
"settings": {
|
"settings": {
|
||||||
"accent_color": "Color d'accent",
|
"accent_color": "Color d'accent",
|
||||||
"account": "Compte",
|
"account": "Compte",
|
||||||
"account_deleted": "Your account has been deleted",
|
|
||||||
"account_description": "Personalitzeu la configuració del compte.",
|
"account_description": "Personalitzeu la configuració del compte.",
|
||||||
"account_email_description": "La vostra adreça de correu electrònic principal.",
|
"account_email_description": "La vostra adreça de correu electrònic principal.",
|
||||||
"account_name_description": "Aquest és el vostre nom d'exposició",
|
"account_name_description": "Aquest és el vostre nom d'exposició",
|
||||||
@@ -449,8 +445,6 @@
|
|||||||
"change_font_size": "Canvia la mida de la lletra",
|
"change_font_size": "Canvia la mida de la lletra",
|
||||||
"choose_language": "Tria l'idioma",
|
"choose_language": "Tria l'idioma",
|
||||||
"dark_mode": "Fosc",
|
"dark_mode": "Fosc",
|
||||||
"delete_account": "Delete account",
|
|
||||||
"delete_account_description": "Once you delete your account, all your data will be permanently deleted. This action cannot be undone.",
|
|
||||||
"expand_navigation": "Ampliar navegació",
|
"expand_navigation": "Ampliar navegació",
|
||||||
"experiments": "Experiments",
|
"experiments": "Experiments",
|
||||||
"experiments_notice": "Es tracta d'una col·lecció d'experiments en què estem treballant que poden resultar útils, divertits, o ambdós. No són finals i potser no són estables, de manera que si passa alguna cosa massa estrany, no us espanteu. Només cal que el desactiveu. Bromes a part,",
|
"experiments_notice": "Es tracta d'una col·lecció d'experiments en què estem treballant que poden resultar útils, divertits, o ambdós. No són finals i potser no són estables, de manera que si passa alguna cosa massa estrany, no us espanteu. Només cal que el desactiveu. Bromes a part,",
|
||||||
|
|||||||
@@ -1,43 +1,43 @@
|
|||||||
{
|
{
|
||||||
"action": {
|
"action": {
|
||||||
"autoscroll": "自动滚动",
|
"autoscroll": "Autoscroll",
|
||||||
"cancel": "取消",
|
"cancel": "取消",
|
||||||
"choose_file": "选择文件",
|
"choose_file": "选择文件",
|
||||||
"clear": "清除",
|
"clear": "清除",
|
||||||
"clear_all": "全部清除",
|
"clear_all": "全部清除",
|
||||||
"close": "关闭",
|
"close": "Close",
|
||||||
"connect": "连接",
|
"connect": "连接",
|
||||||
"connecting": "连接中",
|
"connecting": "Connecting",
|
||||||
"copy": "复制",
|
"copy": "复制",
|
||||||
"delete": "删除",
|
"delete": "删除",
|
||||||
"disconnect": "断开连接",
|
"disconnect": "断开连接",
|
||||||
"dismiss": "忽略",
|
"dismiss": "忽略",
|
||||||
"dont_save": "不保存",
|
"dont_save": "不保存",
|
||||||
"download_file": "下载文件",
|
"download_file": "下载文件",
|
||||||
"drag_to_reorder": "拖曳以重新排序",
|
"drag_to_reorder": "Drag to reorder",
|
||||||
"duplicate": "复制",
|
"duplicate": "复制",
|
||||||
"edit": "编辑",
|
"edit": "编辑",
|
||||||
"filter": "过滤",
|
"filter": "Filter",
|
||||||
"go_back": "返回",
|
"go_back": "返回",
|
||||||
"group_by": "分组方式",
|
"group_by": "Group by",
|
||||||
"label": "标签",
|
"label": "标签",
|
||||||
"learn_more": "了解更多",
|
"learn_more": "了解更多",
|
||||||
"less": "更少",
|
"less": "更少",
|
||||||
"more": "更多",
|
"more": "更多",
|
||||||
"new": "新增",
|
"new": "新增",
|
||||||
"no": "否",
|
"no": "否",
|
||||||
"open_workspace": "打开工作区",
|
"open_workspace": "Open workspace",
|
||||||
"paste": "粘贴",
|
"paste": "粘贴",
|
||||||
"prettify": "美化",
|
"prettify": "美化",
|
||||||
"remove": "移除",
|
"remove": "移除",
|
||||||
"restore": "恢复",
|
"restore": "恢复",
|
||||||
"save": "保存",
|
"save": "保存",
|
||||||
"scroll_to_bottom": "滚动至底部",
|
"scroll_to_bottom": "Scroll to bottom",
|
||||||
"scroll_to_top": "滚动至顶部",
|
"scroll_to_top": "Scroll to top",
|
||||||
"search": "搜索",
|
"search": "搜索",
|
||||||
"send": "发送",
|
"send": "发送",
|
||||||
"start": "开始",
|
"start": "开始",
|
||||||
"starting": "正在开始",
|
"starting": "Starting",
|
||||||
"stop": "停止",
|
"stop": "停止",
|
||||||
"to_close": "以关闭",
|
"to_close": "以关闭",
|
||||||
"to_navigate": "以定位",
|
"to_navigate": "以定位",
|
||||||
@@ -174,8 +174,8 @@
|
|||||||
"profile": "登录以查看你的个人档案",
|
"profile": "登录以查看你的个人档案",
|
||||||
"protocols": "协议为空",
|
"protocols": "协议为空",
|
||||||
"schema": "连接至 GraphQL 端点",
|
"schema": "连接至 GraphQL 端点",
|
||||||
"shortcodes": "Shortcodes 为空",
|
"shortcodes": "Shortcodes are empty",
|
||||||
"subscription": "订阅为空",
|
"subscription": "Subscriptions are empty",
|
||||||
"team_name": "团队名称为空",
|
"team_name": "团队名称为空",
|
||||||
"teams": "团队为空",
|
"teams": "团队为空",
|
||||||
"tests": "没有针对该请求的测试"
|
"tests": "没有针对该请求的测试"
|
||||||
@@ -188,13 +188,13 @@
|
|||||||
"deleted": "环境已删除",
|
"deleted": "环境已删除",
|
||||||
"edit": "编辑环境",
|
"edit": "编辑环境",
|
||||||
"invalid_name": "请提供有效的环境名称",
|
"invalid_name": "请提供有效的环境名称",
|
||||||
"my_environments": "我的环境",
|
"my_environments": "My Environments",
|
||||||
"nested_overflow": "环境嵌套深度超过限制(10层)",
|
"nested_overflow": "环境嵌套深度超过限制(10层)",
|
||||||
"new": "新建环境",
|
"new": "新建环境",
|
||||||
"no_environment": "无环境",
|
"no_environment": "无环境",
|
||||||
"no_environment_description": "没有选择环境。选择如何处理以下变量。",
|
"no_environment_description": "没有选择环境。选择如何处理以下变量。",
|
||||||
"select": "选择环境",
|
"select": "选择环境",
|
||||||
"team_environments": "团队环境",
|
"team_environments": "Team Environments",
|
||||||
"title": "环境",
|
"title": "环境",
|
||||||
"updated": "环境已更新",
|
"updated": "环境已更新",
|
||||||
"variable_list": "变量列表"
|
"variable_list": "变量列表"
|
||||||
@@ -203,9 +203,6 @@
|
|||||||
"browser_support_sse": "该浏览器似乎不支持 SSE。",
|
"browser_support_sse": "该浏览器似乎不支持 SSE。",
|
||||||
"check_console_details": "检查控制台日志以获悉详情",
|
"check_console_details": "检查控制台日志以获悉详情",
|
||||||
"curl_invalid_format": "cURL 格式不正确",
|
"curl_invalid_format": "cURL 格式不正确",
|
||||||
"danger_zone": "Danger zone",
|
|
||||||
"delete_account": "您的帐号目前为这些团队的拥有者:",
|
|
||||||
"delete_account_description": "您在删除帐号前必须先将您自己从团队中移除、转移拥有权,或是删除团队。",
|
|
||||||
"empty_req_name": "空请求名称",
|
"empty_req_name": "空请求名称",
|
||||||
"f12_details": "(F12 详情)",
|
"f12_details": "(F12 详情)",
|
||||||
"gql_prettify_invalid_query": "无法美化无效的查询,处理查询语法错误并重试",
|
"gql_prettify_invalid_query": "无法美化无效的查询,处理查询语法错误并重试",
|
||||||
@@ -218,8 +215,8 @@
|
|||||||
"network_error": "好像发生了网络错误,请重试。",
|
"network_error": "好像发生了网络错误,请重试。",
|
||||||
"network_fail": "无法发送请求",
|
"network_fail": "无法发送请求",
|
||||||
"no_duration": "无持续时间",
|
"no_duration": "无持续时间",
|
||||||
"no_results_found": "找不到结果",
|
"no_results_found": "No matches found",
|
||||||
"page_not_found": "找不到此頁面",
|
"page_not_found": "This page could not be found",
|
||||||
"script_fail": "无法执行预请求脚本",
|
"script_fail": "无法执行预请求脚本",
|
||||||
"something_went_wrong": "发生了一些错误",
|
"something_went_wrong": "发生了一些错误",
|
||||||
"test_script_fail": "无法执行请求脚本"
|
"test_script_fail": "无法执行请求脚本"
|
||||||
@@ -229,12 +226,12 @@
|
|||||||
"create_secret_gist": "创建私密 Gist",
|
"create_secret_gist": "创建私密 Gist",
|
||||||
"gist_created": "已创建 Gist",
|
"gist_created": "已创建 Gist",
|
||||||
"require_github": "使用 GitHub 登录以创建私密 Gist",
|
"require_github": "使用 GitHub 登录以创建私密 Gist",
|
||||||
"title": "导出"
|
"title": "Export"
|
||||||
},
|
},
|
||||||
"filter": {
|
"filter": {
|
||||||
"all": "全部",
|
"all": "All",
|
||||||
"none": "无",
|
"none": "None",
|
||||||
"starred": "已加星号"
|
"starred": "Starred"
|
||||||
},
|
},
|
||||||
"folder": {
|
"folder": {
|
||||||
"created": "已创建文件夹",
|
"created": "已创建文件夹",
|
||||||
@@ -250,8 +247,8 @@
|
|||||||
"subscriptions": "订阅"
|
"subscriptions": "订阅"
|
||||||
},
|
},
|
||||||
"group": {
|
"group": {
|
||||||
"time": "时间",
|
"time": "Time",
|
||||||
"url": "网址"
|
"url": "URL"
|
||||||
},
|
},
|
||||||
"header": {
|
"header": {
|
||||||
"install_pwa": "安装应用",
|
"install_pwa": "安装应用",
|
||||||
@@ -294,10 +291,10 @@
|
|||||||
"from_postman_description": "从 Postman 集合中导入",
|
"from_postman_description": "从 Postman 集合中导入",
|
||||||
"from_url": "从 URL 导入",
|
"from_url": "从 URL 导入",
|
||||||
"gist_url": "输入 Gist URL",
|
"gist_url": "输入 Gist URL",
|
||||||
"import_from_url_invalid_fetch": "无法从网址取得资料",
|
"import_from_url_invalid_fetch": "Couldn't get data from the url",
|
||||||
"import_from_url_invalid_file_format": "导入组合时发生错误",
|
"import_from_url_invalid_file_format": "Error while importing collections",
|
||||||
"import_from_url_invalid_type": "不支持此类型。可接受的值为 'hoppscotch'、'openapi'、'postman'、'insomnia'",
|
"import_from_url_invalid_type": "Unsupported type. accepted values are 'hoppscotch', 'openapi', 'postman', 'insomnia'",
|
||||||
"import_from_url_success": "已导入组合",
|
"import_from_url_success": "Collections Imported",
|
||||||
"json_description": "从 Hoppscotch 的集合文件导入(JSON)",
|
"json_description": "从 Hoppscotch 的集合文件导入(JSON)",
|
||||||
"title": "导入"
|
"title": "导入"
|
||||||
},
|
},
|
||||||
@@ -316,16 +313,16 @@
|
|||||||
"import_export": "导入/导出"
|
"import_export": "导入/导出"
|
||||||
},
|
},
|
||||||
"mqtt": {
|
"mqtt": {
|
||||||
"already_subscribed": "您已经订阅了此主題。",
|
"already_subscribed": "You are already subscribed to this topic.",
|
||||||
"clean_session": "清除会话",
|
"clean_session": "Clean Session",
|
||||||
"clear_input": "清除输入",
|
"clear_input": "Clear input",
|
||||||
"clear_input_on_send": "发送后清除输入",
|
"clear_input_on_send": "Clear input on send",
|
||||||
"client_id": "客户端 ID",
|
"client_id": "Client ID",
|
||||||
"color": "选择颜色",
|
"color": "Pick a color",
|
||||||
"communication": "通讯",
|
"communication": "通讯",
|
||||||
"connection_config": "连接配置",
|
"connection_config": "Connection Config",
|
||||||
"connection_not_authorized": "此MQTT连接未使用任何验证。",
|
"connection_not_authorized": "This MQTT connection does not use any authentication.",
|
||||||
"invalid_topic": "请提供该订阅的主题",
|
"invalid_topic": "Please provide a topic for the subscription",
|
||||||
"keep_alive": "Keep Alive",
|
"keep_alive": "Keep Alive",
|
||||||
"log": "日志",
|
"log": "日志",
|
||||||
"lw_message": "Last-Will Message",
|
"lw_message": "Last-Will Message",
|
||||||
@@ -333,8 +330,8 @@
|
|||||||
"lw_retain": "Last-Will Retain",
|
"lw_retain": "Last-Will Retain",
|
||||||
"lw_topic": "Last-Will Topic",
|
"lw_topic": "Last-Will Topic",
|
||||||
"message": "消息",
|
"message": "消息",
|
||||||
"new": "新订阅",
|
"new": "New Subscription",
|
||||||
"not_connected": "请先启动MQTT连接。",
|
"not_connected": "Please start a MQTT connection first.",
|
||||||
"publish": "发布",
|
"publish": "发布",
|
||||||
"qos": "QoS",
|
"qos": "QoS",
|
||||||
"ssl": "SSL",
|
"ssl": "SSL",
|
||||||
@@ -361,7 +358,7 @@
|
|||||||
},
|
},
|
||||||
"profile": {
|
"profile": {
|
||||||
"app_settings": "应用设置",
|
"app_settings": "应用设置",
|
||||||
"default_hopp_displayname": "未命名使用者",
|
"default_hopp_displayname": "Unnamed User",
|
||||||
"editor": "编辑者",
|
"editor": "编辑者",
|
||||||
"editor_description": "编辑者可以添加、编辑和删除请求。",
|
"editor_description": "编辑者可以添加、编辑和删除请求。",
|
||||||
"email_verification_mail": "确认邮件已发送至你的邮箱,请点击链接以验证你的电子邮箱。",
|
"email_verification_mail": "确认邮件已发送至你的邮箱,请点击链接以验证你的电子邮箱。",
|
||||||
@@ -384,9 +381,9 @@
|
|||||||
"choose_language": "选择语言",
|
"choose_language": "选择语言",
|
||||||
"content_type": "内容类型",
|
"content_type": "内容类型",
|
||||||
"content_type_titles": {
|
"content_type_titles": {
|
||||||
"others": "其他",
|
"others": "Others",
|
||||||
"structured": "结构",
|
"structured": "Structured",
|
||||||
"text": "文字"
|
"text": "Text"
|
||||||
},
|
},
|
||||||
"copy_link": "复制链接",
|
"copy_link": "复制链接",
|
||||||
"duration": "持续时间",
|
"duration": "持续时间",
|
||||||
@@ -418,11 +415,11 @@
|
|||||||
"type": "请求类型",
|
"type": "请求类型",
|
||||||
"url": "URL",
|
"url": "URL",
|
||||||
"variables": "变量",
|
"variables": "变量",
|
||||||
"view_my_links": "查看我的链接"
|
"view_my_links": "View my links"
|
||||||
},
|
},
|
||||||
"response": {
|
"response": {
|
||||||
"body": "响应体",
|
"body": "响应体",
|
||||||
"filter_response_body": "筛选JSON响应本体(使用JSONPath语法)",
|
"filter_response_body": "Filter JSON response body (uses JSONPath syntax)",
|
||||||
"headers": "响应头",
|
"headers": "响应头",
|
||||||
"html": "HTML",
|
"html": "HTML",
|
||||||
"image": "图像",
|
"image": "图像",
|
||||||
@@ -440,7 +437,6 @@
|
|||||||
"settings": {
|
"settings": {
|
||||||
"accent_color": "强调色",
|
"accent_color": "强调色",
|
||||||
"account": "帐户",
|
"account": "帐户",
|
||||||
"account_deleted": "已刪除您的账号",
|
|
||||||
"account_description": "自定义您的帐户设置。",
|
"account_description": "自定义您的帐户设置。",
|
||||||
"account_email_description": "您的主要电子邮箱地址。",
|
"account_email_description": "您的主要电子邮箱地址。",
|
||||||
"account_name_description": "这是您的显示名称。",
|
"account_name_description": "这是您的显示名称。",
|
||||||
@@ -449,8 +445,6 @@
|
|||||||
"change_font_size": "更改字体大小",
|
"change_font_size": "更改字体大小",
|
||||||
"choose_language": "选择语言",
|
"choose_language": "选择语言",
|
||||||
"dark_mode": "暗色",
|
"dark_mode": "暗色",
|
||||||
"delete_account": "刪除账号",
|
|
||||||
"delete_account_description": "一旦您删除了您的帐号,您的所有数据将被永久删除。此操作无法复原。",
|
|
||||||
"expand_navigation": "展开导航栏",
|
"expand_navigation": "展开导航栏",
|
||||||
"experiments": "实验功能",
|
"experiments": "实验功能",
|
||||||
"experiments_notice": "下面是我们正在开发中的一些实验功能,这些功能可能会很有用,可能很有趣,又或者二者都是或都不是。这些功能并非最终版本且可能不稳定,所以如果发生了一些过于奇怪的事情,不要惊慌,关掉它们就好了。玩笑归玩笑,",
|
"experiments_notice": "下面是我们正在开发中的一些实验功能,这些功能可能会很有用,可能很有趣,又或者二者都是或都不是。这些功能并非最终版本且可能不稳定,所以如果发生了一些过于奇怪的事情,不要惊慌,关掉它们就好了。玩笑归玩笑,",
|
||||||
@@ -477,8 +471,8 @@
|
|||||||
"proxy_use_toggle": "使用代理中间件发送请求",
|
"proxy_use_toggle": "使用代理中间件发送请求",
|
||||||
"read_the": "阅读",
|
"read_the": "阅读",
|
||||||
"reset_default": "重置为默认",
|
"reset_default": "重置为默认",
|
||||||
"short_codes": "快捷键",
|
"short_codes": "Short codes",
|
||||||
"short_codes_description": "我们为您打造的快捷键。",
|
"short_codes_description": "Short codes which were created by you.",
|
||||||
"sidebar_on_left": "侧边栏移至左侧",
|
"sidebar_on_left": "侧边栏移至左侧",
|
||||||
"sync": "同步",
|
"sync": "同步",
|
||||||
"sync_collections": "集合",
|
"sync_collections": "集合",
|
||||||
@@ -492,16 +486,16 @@
|
|||||||
"theme_description": "自定义您的应用程序主题。",
|
"theme_description": "自定义您的应用程序主题。",
|
||||||
"use_experimental_url_bar": "使用实验性的带有环境高亮的 URL 栏",
|
"use_experimental_url_bar": "使用实验性的带有环境高亮的 URL 栏",
|
||||||
"user": "用户",
|
"user": "用户",
|
||||||
"verified_email": "已验证电子邮件地址",
|
"verified_email": "Verified email",
|
||||||
"verify_email": "验证电子邮箱"
|
"verify_email": "验证电子邮箱"
|
||||||
},
|
},
|
||||||
"shortcodes": {
|
"shortcodes": {
|
||||||
"actions": "操作",
|
"actions": "Actions",
|
||||||
"created_on": "创建于",
|
"created_on": "Created on",
|
||||||
"deleted": "已刪除快捷键",
|
"deleted": "Shortcode deleted",
|
||||||
"method": "方法",
|
"method": "Method",
|
||||||
"not_found": "找不到快捷键",
|
"not_found": "Shortcode not found",
|
||||||
"short_code": "快捷键",
|
"short_code": "Short code",
|
||||||
"url": "URL"
|
"url": "URL"
|
||||||
},
|
},
|
||||||
"shortcut": {
|
"shortcut": {
|
||||||
@@ -543,9 +537,9 @@
|
|||||||
"title": "请求"
|
"title": "请求"
|
||||||
},
|
},
|
||||||
"response": {
|
"response": {
|
||||||
"copy": "复制响应至剪贴板",
|
"copy": "Copy response to clipboard",
|
||||||
"download": "下载响应",
|
"download": "Download response as file",
|
||||||
"title": "响应"
|
"title": "Response"
|
||||||
},
|
},
|
||||||
"theme": {
|
"theme": {
|
||||||
"black": "切换为黑色主题",
|
"black": "切换为黑色主题",
|
||||||
@@ -563,7 +557,7 @@
|
|||||||
},
|
},
|
||||||
"socketio": {
|
"socketio": {
|
||||||
"communication": "通讯",
|
"communication": "通讯",
|
||||||
"connection_not_authorized": "此SocketIO连接未使用任何验证。",
|
"connection_not_authorized": "This SocketIO connection does not use any authentication.",
|
||||||
"event_name": "事件名称",
|
"event_name": "事件名称",
|
||||||
"events": "事件",
|
"events": "事件",
|
||||||
"log": "日志",
|
"log": "日志",
|
||||||
@@ -581,9 +575,9 @@
|
|||||||
"connected": "已连接",
|
"connected": "已连接",
|
||||||
"connected_to": "已连接到 {name}",
|
"connected_to": "已连接到 {name}",
|
||||||
"connecting_to": "正在连接到 {name}……",
|
"connecting_to": "正在连接到 {name}……",
|
||||||
"connection_error": "连接错误",
|
"connection_error": "Failed to connect",
|
||||||
"connection_failed": "连接失败",
|
"connection_failed": "Connection failed",
|
||||||
"connection_lost": "连接丢失",
|
"connection_lost": "Connection lost",
|
||||||
"copied_to_clipboard": "已复制到剪贴板",
|
"copied_to_clipboard": "已复制到剪贴板",
|
||||||
"deleted": "已删除",
|
"deleted": "已删除",
|
||||||
"deprecated": "已弃用",
|
"deprecated": "已弃用",
|
||||||
@@ -598,17 +592,17 @@
|
|||||||
"history_deleted": "历史记录已删除",
|
"history_deleted": "历史记录已删除",
|
||||||
"linewrap": "换行",
|
"linewrap": "换行",
|
||||||
"loading": "正在加载……",
|
"loading": "正在加载……",
|
||||||
"message_received": "信息:{message}已到达主题:{topic}",
|
"message_received": "Message: {message} arrived on topic: {topic}",
|
||||||
"mqtt_subscription_failed": "订阅此主题时发生错误:{topic}",
|
"mqtt_subscription_failed": "Something went wrong while subscribing to topic: {topic}",
|
||||||
"none": "无",
|
"none": "无",
|
||||||
"nothing_found": "没有找到",
|
"nothing_found": "没有找到",
|
||||||
"published_error": "将信息:{topic}发布至主题:{message}时发生错误",
|
"published_error": "Something went wrong while publishing msg: {topic} to topic: {message}",
|
||||||
"published_message": "已将此信息:{message}发布至主题:{topic}",
|
"published_message": "Published message: {message} to topic: {topic}",
|
||||||
"reconnection_error": "重连失败",
|
"reconnection_error": "Failed to reconnect",
|
||||||
"subscribed_failed": "无法订阅此主題:{topic}",
|
"subscribed_failed": "Failed to subscribe to topic: {topic}",
|
||||||
"subscribed_success": "成功订阅此主題:{topic}",
|
"subscribed_success": "Successfully subscribed to topic: {topic}",
|
||||||
"unsubscribed_failed": "无法取消订阅此主題:{topic}",
|
"unsubscribed_failed": "Failed to unsubscribe from topic: {topic}",
|
||||||
"unsubscribed_success": "成功取消订阅此主題:{topic}",
|
"unsubscribed_success": "Successfully unsubscribed from topic: {topic}",
|
||||||
"waiting_send_request": "等待发送请求"
|
"waiting_send_request": "等待发送请求"
|
||||||
},
|
},
|
||||||
"support": {
|
"support": {
|
||||||
@@ -693,9 +687,9 @@
|
|||||||
"we_sent_invite_link_description": "请所有受邀者检查他们的收件箱,点击链接以加入团队。"
|
"we_sent_invite_link_description": "请所有受邀者检查他们的收件箱,点击链接以加入团队。"
|
||||||
},
|
},
|
||||||
"team_environment": {
|
"team_environment": {
|
||||||
"deleted": "已刪除环境",
|
"deleted": "Environment Deleted",
|
||||||
"duplicate": "已复制环境",
|
"duplicate": "Environment Duplicated",
|
||||||
"not_found": "找不到环境。"
|
"not_found": "Environment not found."
|
||||||
},
|
},
|
||||||
"test": {
|
"test": {
|
||||||
"failed": "测试失败",
|
"failed": "测试失败",
|
||||||
|
|||||||
@@ -203,9 +203,6 @@
|
|||||||
"browser_support_sse": "Zdá se, že tento prohlížeč nemá podporu událostí odeslaných serverem.",
|
"browser_support_sse": "Zdá se, že tento prohlížeč nemá podporu událostí odeslaných serverem.",
|
||||||
"check_console_details": "Podrobnosti najdete v protokolu konzoly.",
|
"check_console_details": "Podrobnosti najdete v protokolu konzoly.",
|
||||||
"curl_invalid_format": "cURL nemá správný formát",
|
"curl_invalid_format": "cURL nemá správný formát",
|
||||||
"danger_zone": "Danger zone",
|
|
||||||
"delete_account": "Your account is currently an owner in these teams:",
|
|
||||||
"delete_account_description": "You must either remove yourself, transfer ownership, or delete these teams before you can delete your account.",
|
|
||||||
"empty_req_name": "Název prázdného požadavku",
|
"empty_req_name": "Název prázdného požadavku",
|
||||||
"f12_details": "(F12 pro podrobnosti)",
|
"f12_details": "(F12 pro podrobnosti)",
|
||||||
"gql_prettify_invalid_query": "Neplatný dotaz nelze předběžně upravit, vyřešit chyby syntaxe dotazu a zkusit to znovu",
|
"gql_prettify_invalid_query": "Neplatný dotaz nelze předběžně upravit, vyřešit chyby syntaxe dotazu a zkusit to znovu",
|
||||||
@@ -440,7 +437,6 @@
|
|||||||
"settings": {
|
"settings": {
|
||||||
"accent_color": "Akcentní barva",
|
"accent_color": "Akcentní barva",
|
||||||
"account": "Účet",
|
"account": "Účet",
|
||||||
"account_deleted": "Your account has been deleted",
|
|
||||||
"account_description": "Přizpůsobte si nastavení účtu.",
|
"account_description": "Přizpůsobte si nastavení účtu.",
|
||||||
"account_email_description": "Vaše primární e -mailová adresa.",
|
"account_email_description": "Vaše primární e -mailová adresa.",
|
||||||
"account_name_description": "Toto je vaše zobrazované jméno.",
|
"account_name_description": "Toto je vaše zobrazované jméno.",
|
||||||
@@ -449,8 +445,6 @@
|
|||||||
"change_font_size": "Změnit velikost písma",
|
"change_font_size": "Změnit velikost písma",
|
||||||
"choose_language": "Vyber jazyk",
|
"choose_language": "Vyber jazyk",
|
||||||
"dark_mode": "Temný",
|
"dark_mode": "Temný",
|
||||||
"delete_account": "Delete account",
|
|
||||||
"delete_account_description": "Once you delete your account, all your data will be permanently deleted. This action cannot be undone.",
|
|
||||||
"expand_navigation": "Expand navigation",
|
"expand_navigation": "Expand navigation",
|
||||||
"experiments": "Experimenty",
|
"experiments": "Experimenty",
|
||||||
"experiments_notice": "Toto je sbírka experimentů, na kterých pracujeme a které se mohou ukázat jako užitečné, zábavné, obojí, nebo ani jedno. Nejsou konečné a nemusí být stabilní, takže pokud se stane něco příliš divného, nepanikařte. Prostě vypni tu nebezpečnou věc. Vtipy stranou,",
|
"experiments_notice": "Toto je sbírka experimentů, na kterých pracujeme a které se mohou ukázat jako užitečné, zábavné, obojí, nebo ani jedno. Nejsou konečné a nemusí být stabilní, takže pokud se stane něco příliš divného, nepanikařte. Prostě vypni tu nebezpečnou věc. Vtipy stranou,",
|
||||||
|
|||||||
@@ -203,9 +203,6 @@
|
|||||||
"browser_support_sse": "Det ser ikke ud til, at denne browser understøtter Server Sent Events.",
|
"browser_support_sse": "Det ser ikke ud til, at denne browser understøtter Server Sent Events.",
|
||||||
"check_console_details": "Tjek konsollog for at få flere oplysninger.",
|
"check_console_details": "Tjek konsollog for at få flere oplysninger.",
|
||||||
"curl_invalid_format": "cURL er ikke formateret korrekt",
|
"curl_invalid_format": "cURL er ikke formateret korrekt",
|
||||||
"danger_zone": "Danger zone",
|
|
||||||
"delete_account": "Your account is currently an owner in these teams:",
|
|
||||||
"delete_account_description": "You must either remove yourself, transfer ownership, or delete these teams before you can delete your account.",
|
|
||||||
"empty_req_name": "Tom anmodningsnavn",
|
"empty_req_name": "Tom anmodningsnavn",
|
||||||
"f12_details": "(F12 for detaljer)",
|
"f12_details": "(F12 for detaljer)",
|
||||||
"gql_prettify_invalid_query": "Kunne ikke prætificere en ugyldig forespørgsel, løse forespørgselssyntaksfejl og prøve igen",
|
"gql_prettify_invalid_query": "Kunne ikke prætificere en ugyldig forespørgsel, løse forespørgselssyntaksfejl og prøve igen",
|
||||||
@@ -440,7 +437,6 @@
|
|||||||
"settings": {
|
"settings": {
|
||||||
"accent_color": "Accent farve",
|
"accent_color": "Accent farve",
|
||||||
"account": "Konto",
|
"account": "Konto",
|
||||||
"account_deleted": "Your account has been deleted",
|
|
||||||
"account_description": "Tilpas dine kontoindstillinger.",
|
"account_description": "Tilpas dine kontoindstillinger.",
|
||||||
"account_email_description": "Din primære e -mail -adresse.",
|
"account_email_description": "Din primære e -mail -adresse.",
|
||||||
"account_name_description": "Dette er dit visningsnavn.",
|
"account_name_description": "Dette er dit visningsnavn.",
|
||||||
@@ -449,8 +445,6 @@
|
|||||||
"change_font_size": "Skift skriftstørrelse",
|
"change_font_size": "Skift skriftstørrelse",
|
||||||
"choose_language": "Vælg sprog",
|
"choose_language": "Vælg sprog",
|
||||||
"dark_mode": "Mørk",
|
"dark_mode": "Mørk",
|
||||||
"delete_account": "Delete account",
|
|
||||||
"delete_account_description": "Once you delete your account, all your data will be permanently deleted. This action cannot be undone.",
|
|
||||||
"expand_navigation": "Expand navigation",
|
"expand_navigation": "Expand navigation",
|
||||||
"experiments": "Eksperimenter",
|
"experiments": "Eksperimenter",
|
||||||
"experiments_notice": "Dette er en samling af eksperimenter, vi arbejder på, der kan vise sig at være nyttige, sjove, begge dele eller ingen af dem. De er ikke endelige og er muligvis ikke stabile, så hvis der sker noget alt for mærkeligt, skal du ikke gå i panik. Bare sluk for dang -tingen. Vittigheder til side,",
|
"experiments_notice": "Dette er en samling af eksperimenter, vi arbejder på, der kan vise sig at være nyttige, sjove, begge dele eller ingen af dem. De er ikke endelige og er muligvis ikke stabile, så hvis der sker noget alt for mærkeligt, skal du ikke gå i panik. Bare sluk for dang -tingen. Vittigheder til side,",
|
||||||
|
|||||||
@@ -203,9 +203,6 @@
|
|||||||
"browser_support_sse": "Dieser Browser scheint keine Unterstützung für die vom Server gesendete Ereignisse zu haben.",
|
"browser_support_sse": "Dieser Browser scheint keine Unterstützung für die vom Server gesendete Ereignisse zu haben.",
|
||||||
"check_console_details": "Einzelheiten findest Du in der Browser-Konsole.",
|
"check_console_details": "Einzelheiten findest Du in der Browser-Konsole.",
|
||||||
"curl_invalid_format": "cURL ist nicht richtig formatiert",
|
"curl_invalid_format": "cURL ist nicht richtig formatiert",
|
||||||
"danger_zone": "Danger zone",
|
|
||||||
"delete_account": "Your account is currently an owner in these teams:",
|
|
||||||
"delete_account_description": "You must either remove yourself, transfer ownership, or delete these teams before you can delete your account.",
|
|
||||||
"empty_req_name": "Leerer Anfragename",
|
"empty_req_name": "Leerer Anfragename",
|
||||||
"f12_details": "(F12 für Details)",
|
"f12_details": "(F12 für Details)",
|
||||||
"gql_prettify_invalid_query": "Eine ungültige Abfrage konnte nicht verschönert werden. Fehler in der Abfragesyntax beheben und erneut versuchen",
|
"gql_prettify_invalid_query": "Eine ungültige Abfrage konnte nicht verschönert werden. Fehler in der Abfragesyntax beheben und erneut versuchen",
|
||||||
@@ -440,7 +437,6 @@
|
|||||||
"settings": {
|
"settings": {
|
||||||
"accent_color": "Akzentfarbe",
|
"accent_color": "Akzentfarbe",
|
||||||
"account": "Konto",
|
"account": "Konto",
|
||||||
"account_deleted": "Your account has been deleted",
|
|
||||||
"account_description": "Passe Deine Kontoeinstellungen an.",
|
"account_description": "Passe Deine Kontoeinstellungen an.",
|
||||||
"account_email_description": "Deine primäre E-Mail-Adresse.",
|
"account_email_description": "Deine primäre E-Mail-Adresse.",
|
||||||
"account_name_description": "Dies ist Dein Anzeigename.",
|
"account_name_description": "Dies ist Dein Anzeigename.",
|
||||||
@@ -449,8 +445,6 @@
|
|||||||
"change_font_size": "Schriftgröße ändern",
|
"change_font_size": "Schriftgröße ändern",
|
||||||
"choose_language": "Sprache wählen",
|
"choose_language": "Sprache wählen",
|
||||||
"dark_mode": "Dunkel",
|
"dark_mode": "Dunkel",
|
||||||
"delete_account": "Delete account",
|
|
||||||
"delete_account_description": "Once you delete your account, all your data will be permanently deleted. This action cannot be undone.",
|
|
||||||
"expand_navigation": "Menüpunkte vergrößern",
|
"expand_navigation": "Menüpunkte vergrößern",
|
||||||
"experiments": "Experimente",
|
"experiments": "Experimente",
|
||||||
"experiments_notice": "Dies ist eine Sammlung von Experimenten, an denen wir aktuell arbeiten und die sich als nützlich erweisen könnten, Spaß machen, beides oder keines von beiden. Sie sind nicht endgültig und möglicherweise nicht stabil. Wenn also etwas übermäßig Seltsames passiert, gerate nicht in Panik. Schalte das verdammte Ding einfach aus. Scherz beiseite,",
|
"experiments_notice": "Dies ist eine Sammlung von Experimenten, an denen wir aktuell arbeiten und die sich als nützlich erweisen könnten, Spaß machen, beides oder keines von beiden. Sie sind nicht endgültig und möglicherweise nicht stabil. Wenn also etwas übermäßig Seltsames passiert, gerate nicht in Panik. Schalte das verdammte Ding einfach aus. Scherz beiseite,",
|
||||||
|
|||||||
@@ -203,9 +203,6 @@
|
|||||||
"browser_support_sse": "Αυτό το πρόγραμμα περιήγησης δεν φαίνεται να υποστηρίζει διακομιστές που έχουν σταλεί συμβάντα.",
|
"browser_support_sse": "Αυτό το πρόγραμμα περιήγησης δεν φαίνεται να υποστηρίζει διακομιστές που έχουν σταλεί συμβάντα.",
|
||||||
"check_console_details": "Ελέγξτε το αρχείο καταγραφής της κονσόλας για λεπτομέρειες.",
|
"check_console_details": "Ελέγξτε το αρχείο καταγραφής της κονσόλας για λεπτομέρειες.",
|
||||||
"curl_invalid_format": "Το cURL δεν έχει μορφοποιηθεί σωστά",
|
"curl_invalid_format": "Το cURL δεν έχει μορφοποιηθεί σωστά",
|
||||||
"danger_zone": "Danger zone",
|
|
||||||
"delete_account": "Your account is currently an owner in these teams:",
|
|
||||||
"delete_account_description": "You must either remove yourself, transfer ownership, or delete these teams before you can delete your account.",
|
|
||||||
"empty_req_name": "Όνομα κενού αιτήματος",
|
"empty_req_name": "Όνομα κενού αιτήματος",
|
||||||
"f12_details": "(F12 για λεπτομέρειες)",
|
"f12_details": "(F12 για λεπτομέρειες)",
|
||||||
"gql_prettify_invalid_query": "Δεν ήταν δυνατή η προεπιλογή ενός μη έγκυρου ερωτήματος, η επίλυση σφαλμάτων σύνταξης ερωτήματος και η δοκιμή ξανά",
|
"gql_prettify_invalid_query": "Δεν ήταν δυνατή η προεπιλογή ενός μη έγκυρου ερωτήματος, η επίλυση σφαλμάτων σύνταξης ερωτήματος και η δοκιμή ξανά",
|
||||||
@@ -440,7 +437,6 @@
|
|||||||
"settings": {
|
"settings": {
|
||||||
"accent_color": "Χρώμα προφοράς",
|
"accent_color": "Χρώμα προφοράς",
|
||||||
"account": "λογαριασμός",
|
"account": "λογαριασμός",
|
||||||
"account_deleted": "Your account has been deleted",
|
|
||||||
"account_description": "Προσαρμόστε τις ρυθμίσεις του λογαριασμού σας.",
|
"account_description": "Προσαρμόστε τις ρυθμίσεις του λογαριασμού σας.",
|
||||||
"account_email_description": "Η κύρια διεύθυνση email σας.",
|
"account_email_description": "Η κύρια διεύθυνση email σας.",
|
||||||
"account_name_description": "Αυτό είναι το εμφανιζόμενο όνομά σας.",
|
"account_name_description": "Αυτό είναι το εμφανιζόμενο όνομά σας.",
|
||||||
@@ -449,8 +445,6 @@
|
|||||||
"change_font_size": "Αλλαγή μεγέθους γραμματοσειράς",
|
"change_font_size": "Αλλαγή μεγέθους γραμματοσειράς",
|
||||||
"choose_language": "Διάλεξε γλώσσα",
|
"choose_language": "Διάλεξε γλώσσα",
|
||||||
"dark_mode": "Σκοτάδι",
|
"dark_mode": "Σκοτάδι",
|
||||||
"delete_account": "Delete account",
|
|
||||||
"delete_account_description": "Once you delete your account, all your data will be permanently deleted. This action cannot be undone.",
|
|
||||||
"expand_navigation": "Επέκταση navigation",
|
"expand_navigation": "Επέκταση navigation",
|
||||||
"experiments": "Πειράματα",
|
"experiments": "Πειράματα",
|
||||||
"experiments_notice": "Αυτή είναι μια συλλογή πειραμάτων που δουλεύουμε και που μπορεί να αποδειχθούν χρήσιμα, διασκεδαστικά, και τα δύο ή κανένα από τα δύο. Δεν είναι οριστικά και μπορεί να μην είναι σταθερά, οπότε αν συμβεί κάτι υπερβολικά περίεργο, μην πανικοβληθείτε. Απλώς απενεργοποιήστε το πράγμα. Τα αστεία στην άκρη,",
|
"experiments_notice": "Αυτή είναι μια συλλογή πειραμάτων που δουλεύουμε και που μπορεί να αποδειχθούν χρήσιμα, διασκεδαστικά, και τα δύο ή κανένα από τα δύο. Δεν είναι οριστικά και μπορεί να μην είναι σταθερά, οπότε αν συμβεί κάτι υπερβολικά περίεργο, μην πανικοβληθείτε. Απλώς απενεργοποιήστε το πράγμα. Τα αστεία στην άκρη,",
|
||||||
|
|||||||
@@ -203,9 +203,6 @@
|
|||||||
"browser_support_sse": "This browser doesn't seems to have Server Sent Events support.",
|
"browser_support_sse": "This browser doesn't seems to have Server Sent Events support.",
|
||||||
"check_console_details": "Check console log for details.",
|
"check_console_details": "Check console log for details.",
|
||||||
"curl_invalid_format": "cURL is not formatted properly",
|
"curl_invalid_format": "cURL is not formatted properly",
|
||||||
"danger_zone": "Danger zone",
|
|
||||||
"delete_account": "Your account is currently an owner in these teams:",
|
|
||||||
"delete_account_description": "You must either remove yourself, transfer ownership, or delete these teams before you can delete your account.",
|
|
||||||
"empty_req_name": "Empty Request Name",
|
"empty_req_name": "Empty Request Name",
|
||||||
"f12_details": "(F12 for details)",
|
"f12_details": "(F12 for details)",
|
||||||
"gql_prettify_invalid_query": "Couldn't prettify an invalid query, solve query syntax errors and try again",
|
"gql_prettify_invalid_query": "Couldn't prettify an invalid query, solve query syntax errors and try again",
|
||||||
@@ -391,7 +388,6 @@
|
|||||||
"copy_link": "Copy link",
|
"copy_link": "Copy link",
|
||||||
"duration": "Duration",
|
"duration": "Duration",
|
||||||
"enter_curl": "Enter cURL command",
|
"enter_curl": "Enter cURL command",
|
||||||
"duplicated": "Request duplicated",
|
|
||||||
"generate_code": "Generate code",
|
"generate_code": "Generate code",
|
||||||
"generated_code": "Generated code",
|
"generated_code": "Generated code",
|
||||||
"header_list": "Header List",
|
"header_list": "Header List",
|
||||||
@@ -441,7 +437,6 @@
|
|||||||
"settings": {
|
"settings": {
|
||||||
"accent_color": "Accent color",
|
"accent_color": "Accent color",
|
||||||
"account": "Account",
|
"account": "Account",
|
||||||
"account_deleted": "Your account has been deleted",
|
|
||||||
"account_description": "Customize your account settings.",
|
"account_description": "Customize your account settings.",
|
||||||
"account_email_description": "Your primary email address.",
|
"account_email_description": "Your primary email address.",
|
||||||
"account_name_description": "This is your display name.",
|
"account_name_description": "This is your display name.",
|
||||||
@@ -450,8 +445,6 @@
|
|||||||
"change_font_size": "Change font size",
|
"change_font_size": "Change font size",
|
||||||
"choose_language": "Choose language",
|
"choose_language": "Choose language",
|
||||||
"dark_mode": "Dark",
|
"dark_mode": "Dark",
|
||||||
"delete_account": "Delete account",
|
|
||||||
"delete_account_description": "Once you delete your account, all your data will be permanently deleted. This action cannot be undone.",
|
|
||||||
"expand_navigation": "Expand navigation",
|
"expand_navigation": "Expand navigation",
|
||||||
"experiments": "Experiments",
|
"experiments": "Experiments",
|
||||||
"experiments_notice": "This is a collection of experiments we're working on that might turn out to be useful, fun, both, or neither. They're not final and may not be stable, so if something overly weird happens, don't panic. Just turn the dang thing off. Jokes aside, ",
|
"experiments_notice": "This is a collection of experiments we're working on that might turn out to be useful, fun, both, or neither. They're not final and may not be stable, so if something overly weird happens, don't panic. Just turn the dang thing off. Jokes aside, ",
|
||||||
|
|||||||
@@ -203,9 +203,6 @@
|
|||||||
"browser_support_sse": "Este navegador no parece ser compatible con los eventos enviados por el servidor.",
|
"browser_support_sse": "Este navegador no parece ser compatible con los eventos enviados por el servidor.",
|
||||||
"check_console_details": "Consulta el registro de la consola para obtener más detalles.",
|
"check_console_details": "Consulta el registro de la consola para obtener más detalles.",
|
||||||
"curl_invalid_format": "cURL no está formateado correctamente",
|
"curl_invalid_format": "cURL no está formateado correctamente",
|
||||||
"danger_zone": "Danger zone",
|
|
||||||
"delete_account": "Your account is currently an owner in these teams:",
|
|
||||||
"delete_account_description": "You must either remove yourself, transfer ownership, or delete these teams before you can delete your account.",
|
|
||||||
"empty_req_name": "Nombre de petición vacío",
|
"empty_req_name": "Nombre de petición vacío",
|
||||||
"f12_details": "(F12 para más detalles)",
|
"f12_details": "(F12 para más detalles)",
|
||||||
"gql_prettify_invalid_query": "No se puede aplicar embellecedor a una consulta no válida, resuelve los errores de sintaxis de la consulta y vuelve a intentarlo",
|
"gql_prettify_invalid_query": "No se puede aplicar embellecedor a una consulta no válida, resuelve los errores de sintaxis de la consulta y vuelve a intentarlo",
|
||||||
@@ -440,7 +437,6 @@
|
|||||||
"settings": {
|
"settings": {
|
||||||
"accent_color": "Color de acentuación",
|
"accent_color": "Color de acentuación",
|
||||||
"account": "Cuenta",
|
"account": "Cuenta",
|
||||||
"account_deleted": "Your account has been deleted",
|
|
||||||
"account_description": "Personaliza la configuración de tu cuenta.",
|
"account_description": "Personaliza la configuración de tu cuenta.",
|
||||||
"account_email_description": "Tu dirección de correo electrónico principal.",
|
"account_email_description": "Tu dirección de correo electrónico principal.",
|
||||||
"account_name_description": "Este es tu nombre para mostrar.",
|
"account_name_description": "Este es tu nombre para mostrar.",
|
||||||
@@ -449,8 +445,6 @@
|
|||||||
"change_font_size": "Cambiar tamaño de fuente",
|
"change_font_size": "Cambiar tamaño de fuente",
|
||||||
"choose_language": "Elegir idioma",
|
"choose_language": "Elegir idioma",
|
||||||
"dark_mode": "Oscuro",
|
"dark_mode": "Oscuro",
|
||||||
"delete_account": "Delete account",
|
|
||||||
"delete_account_description": "Once you delete your account, all your data will be permanently deleted. This action cannot be undone.",
|
|
||||||
"expand_navigation": "Expandir la navegación",
|
"expand_navigation": "Expandir la navegación",
|
||||||
"experiments": "Experimentos",
|
"experiments": "Experimentos",
|
||||||
"experiments_notice": "Esta es una colección de experimentos en los que estamos trabajando que podrían resultar útiles, divertidos, ambos o ninguno. No son definitivos y es posible que no sean estables, por lo que si sucede algo demasiado extraño, no se asuste. Solo apaga la maldita cosa. Fuera de bromas,",
|
"experiments_notice": "Esta es una colección de experimentos en los que estamos trabajando que podrían resultar útiles, divertidos, ambos o ninguno. No son definitivos y es posible que no sean estables, por lo que si sucede algo demasiado extraño, no se asuste. Solo apaga la maldita cosa. Fuera de bromas,",
|
||||||
|
|||||||
@@ -203,9 +203,6 @@
|
|||||||
"browser_support_sse": "Tämä selain ei näytä tukevan palvelimen lähettämiä tapahtumia.",
|
"browser_support_sse": "Tämä selain ei näytä tukevan palvelimen lähettämiä tapahtumia.",
|
||||||
"check_console_details": "Katso lisätietoja konsolilokista.",
|
"check_console_details": "Katso lisätietoja konsolilokista.",
|
||||||
"curl_invalid_format": "cURL ei ole alustettu oikein",
|
"curl_invalid_format": "cURL ei ole alustettu oikein",
|
||||||
"danger_zone": "Danger zone",
|
|
||||||
"delete_account": "Your account is currently an owner in these teams:",
|
|
||||||
"delete_account_description": "You must either remove yourself, transfer ownership, or delete these teams before you can delete your account.",
|
|
||||||
"empty_req_name": "Tyhjä pyynnön nimi",
|
"empty_req_name": "Tyhjä pyynnön nimi",
|
||||||
"f12_details": "(F12 lisätietoja)",
|
"f12_details": "(F12 lisätietoja)",
|
||||||
"gql_prettify_invalid_query": "Virheellistä kyselyä ei voitu määrittää, ratkaista kyselyn syntaksivirheet ja yrittää uudelleen",
|
"gql_prettify_invalid_query": "Virheellistä kyselyä ei voitu määrittää, ratkaista kyselyn syntaksivirheet ja yrittää uudelleen",
|
||||||
@@ -440,7 +437,6 @@
|
|||||||
"settings": {
|
"settings": {
|
||||||
"accent_color": "Korostusväri",
|
"accent_color": "Korostusväri",
|
||||||
"account": "Tili",
|
"account": "Tili",
|
||||||
"account_deleted": "Your account has been deleted",
|
|
||||||
"account_description": "Muokkaa tilisi asetuksia.",
|
"account_description": "Muokkaa tilisi asetuksia.",
|
||||||
"account_email_description": "Ensisijainen sähköpostiosoitteesi.",
|
"account_email_description": "Ensisijainen sähköpostiosoitteesi.",
|
||||||
"account_name_description": "Tämä on näyttönimesi.",
|
"account_name_description": "Tämä on näyttönimesi.",
|
||||||
@@ -449,8 +445,6 @@
|
|||||||
"change_font_size": "Vaihda fontin kokoa",
|
"change_font_size": "Vaihda fontin kokoa",
|
||||||
"choose_language": "Valitse kieli",
|
"choose_language": "Valitse kieli",
|
||||||
"dark_mode": "Tumma",
|
"dark_mode": "Tumma",
|
||||||
"delete_account": "Delete account",
|
|
||||||
"delete_account_description": "Once you delete your account, all your data will be permanently deleted. This action cannot be undone.",
|
|
||||||
"expand_navigation": "Expand navigation",
|
"expand_navigation": "Expand navigation",
|
||||||
"experiments": "Kokeet",
|
"experiments": "Kokeet",
|
||||||
"experiments_notice": "Tämä on kokoelma kokeiluja, joita parhaillaan käsittelemme ja jotka voivat osoittautua hyödyllisiksi, hauskoiksi, molemmiksi tai kumpikaan. Ne eivät ole lopullisia eivätkä ehkä vakaita, joten jos jotain liian outoa tapahtuu, älä panikoi. Kytke vain paska pois päältä. Vitsit sivuun,",
|
"experiments_notice": "Tämä on kokoelma kokeiluja, joita parhaillaan käsittelemme ja jotka voivat osoittautua hyödyllisiksi, hauskoiksi, molemmiksi tai kumpikaan. Ne eivät ole lopullisia eivätkä ehkä vakaita, joten jos jotain liian outoa tapahtuu, älä panikoi. Kytke vain paska pois päältä. Vitsit sivuun,",
|
||||||
|
|||||||
@@ -203,9 +203,6 @@
|
|||||||
"browser_support_sse": "Ce navigateur ne semble pas prendre en charge les événements envoyés par le serveur.",
|
"browser_support_sse": "Ce navigateur ne semble pas prendre en charge les événements envoyés par le serveur.",
|
||||||
"check_console_details": "Consultez le journal de la console pour plus de détails.",
|
"check_console_details": "Consultez le journal de la console pour plus de détails.",
|
||||||
"curl_invalid_format": "cURL n'est pas formaté correctement",
|
"curl_invalid_format": "cURL n'est pas formaté correctement",
|
||||||
"danger_zone": "Danger zone",
|
|
||||||
"delete_account": "Your account is currently an owner in these teams:",
|
|
||||||
"delete_account_description": "You must either remove yourself, transfer ownership, or delete these teams before you can delete your account.",
|
|
||||||
"empty_req_name": "Nom de la requête vide",
|
"empty_req_name": "Nom de la requête vide",
|
||||||
"f12_details": "(F12 pour les détails)",
|
"f12_details": "(F12 pour les détails)",
|
||||||
"gql_prettify_invalid_query": "Impossible de formater une requête non valide, résolvez les erreurs de syntaxe de la requête et réessayer",
|
"gql_prettify_invalid_query": "Impossible de formater une requête non valide, résolvez les erreurs de syntaxe de la requête et réessayer",
|
||||||
@@ -440,7 +437,6 @@
|
|||||||
"settings": {
|
"settings": {
|
||||||
"accent_color": "Couleur d'accent",
|
"accent_color": "Couleur d'accent",
|
||||||
"account": "Compte",
|
"account": "Compte",
|
||||||
"account_deleted": "Your account has been deleted",
|
|
||||||
"account_description": "Personnalisez les paramètres de votre compte.",
|
"account_description": "Personnalisez les paramètres de votre compte.",
|
||||||
"account_email_description": "Votre adresse e-mail principale.",
|
"account_email_description": "Votre adresse e-mail principale.",
|
||||||
"account_name_description": "Ceci est votre nom d'affichage.",
|
"account_name_description": "Ceci est votre nom d'affichage.",
|
||||||
@@ -449,8 +445,6 @@
|
|||||||
"change_font_size": "Changer la taille de la police",
|
"change_font_size": "Changer la taille de la police",
|
||||||
"choose_language": "Choisissez la langue",
|
"choose_language": "Choisissez la langue",
|
||||||
"dark_mode": "Sombre",
|
"dark_mode": "Sombre",
|
||||||
"delete_account": "Delete account",
|
|
||||||
"delete_account_description": "Once you delete your account, all your data will be permanently deleted. This action cannot be undone.",
|
|
||||||
"expand_navigation": "Expand navigation",
|
"expand_navigation": "Expand navigation",
|
||||||
"experiments": "Expériences",
|
"experiments": "Expériences",
|
||||||
"experiments_notice": "Il s'agit d'une collection d'expériences sur lesquelles nous travaillons et qui pourraient s'avérer utiles, amusantes, les deux ou aucune. Ils ne sont pas définitifs et peuvent ne pas être stables, donc si quelque chose de trop étrange se produit, ne paniquez pas. Il suffit d'éteindre le truc. Blague à part,",
|
"experiments_notice": "Il s'agit d'une collection d'expériences sur lesquelles nous travaillons et qui pourraient s'avérer utiles, amusantes, les deux ou aucune. Ils ne sont pas définitifs et peuvent ne pas être stables, donc si quelque chose de trop étrange se produit, ne paniquez pas. Il suffit d'éteindre le truc. Blague à part,",
|
||||||
|
|||||||
@@ -203,9 +203,6 @@
|
|||||||
"browser_support_sse": "נראה שלדפדפן זה אין תמיכה באירועי שרת נשלח.",
|
"browser_support_sse": "נראה שלדפדפן זה אין תמיכה באירועי שרת נשלח.",
|
||||||
"check_console_details": "בדוק את יומן המסוף לפרטים.",
|
"check_console_details": "בדוק את יומן המסוף לפרטים.",
|
||||||
"curl_invalid_format": "cURL אינו בפורמט תקין",
|
"curl_invalid_format": "cURL אינו בפורמט תקין",
|
||||||
"danger_zone": "Danger zone",
|
|
||||||
"delete_account": "Your account is currently an owner in these teams:",
|
|
||||||
"delete_account_description": "You must either remove yourself, transfer ownership, or delete these teams before you can delete your account.",
|
|
||||||
"empty_req_name": "שם הבקשה ריק",
|
"empty_req_name": "שם הבקשה ריק",
|
||||||
"f12_details": "(F12 לפרטים)",
|
"f12_details": "(F12 לפרטים)",
|
||||||
"gql_prettify_invalid_query": "לא ניתן היה לייפות שאילתה לא חוקית, לפתור שגיאות תחביר שאילתות ולנסות שוב",
|
"gql_prettify_invalid_query": "לא ניתן היה לייפות שאילתה לא חוקית, לפתור שגיאות תחביר שאילתות ולנסות שוב",
|
||||||
@@ -440,7 +437,6 @@
|
|||||||
"settings": {
|
"settings": {
|
||||||
"accent_color": "צבע הדגשה",
|
"accent_color": "צבע הדגשה",
|
||||||
"account": "חֶשְׁבּוֹן",
|
"account": "חֶשְׁבּוֹן",
|
||||||
"account_deleted": "Your account has been deleted",
|
|
||||||
"account_description": "התאם אישית את הגדרות החשבון שלך.",
|
"account_description": "התאם אישית את הגדרות החשבון שלך.",
|
||||||
"account_email_description": "כתובת הדוא\"ל הראשית שלך.",
|
"account_email_description": "כתובת הדוא\"ל הראשית שלך.",
|
||||||
"account_name_description": "זהו שם התצוגה שלך.",
|
"account_name_description": "זהו שם התצוגה שלך.",
|
||||||
@@ -449,8 +445,6 @@
|
|||||||
"change_font_size": "שנה גודל פונט",
|
"change_font_size": "שנה גודל פונט",
|
||||||
"choose_language": "בחר שפה",
|
"choose_language": "בחר שפה",
|
||||||
"dark_mode": "אפל",
|
"dark_mode": "אפל",
|
||||||
"delete_account": "Delete account",
|
|
||||||
"delete_account_description": "Once you delete your account, all your data will be permanently deleted. This action cannot be undone.",
|
|
||||||
"expand_navigation": "Expand navigation",
|
"expand_navigation": "Expand navigation",
|
||||||
"experiments": "ניסויים",
|
"experiments": "ניסויים",
|
||||||
"experiments_notice": "זהו אוסף של ניסויים שעליהם אנו עובדים שעשויים להיות שימושיים, מהנים, שניהם או לא. הם לא סופיים ואולי לא יציבים, אז אם קורה משהו מוזר מדי, אל תיבהל. פשוט כבה את העניין המטומטם. בדיחות בצד,",
|
"experiments_notice": "זהו אוסף של ניסויים שעליהם אנו עובדים שעשויים להיות שימושיים, מהנים, שניהם או לא. הם לא סופיים ואולי לא יציבים, אז אם קורה משהו מוזר מדי, אל תיבהל. פשוט כבה את העניין המטומטם. בדיחות בצד,",
|
||||||
|
|||||||
@@ -203,9 +203,6 @@
|
|||||||
"browser_support_sse": " ऐसा लगता है कि इस ब्राउज़र में सर्वर से भेजे गए इवेंट का समर्थन नहीं है।",
|
"browser_support_sse": " ऐसा लगता है कि इस ब्राउज़र में सर्वर से भेजे गए इवेंट का समर्थन नहीं है।",
|
||||||
"check_console_details": " विवरण के लिए कंसोल लॉग की जाँच करें।",
|
"check_console_details": " विवरण के लिए कंसोल लॉग की जाँच करें।",
|
||||||
"curl_invalid_format": " कर्ल ठीक से स्वरूपित नहीं है",
|
"curl_invalid_format": " कर्ल ठीक से स्वरूपित नहीं है",
|
||||||
"danger_zone": "Danger zone",
|
|
||||||
"delete_account": "Your account is currently an owner in these teams:",
|
|
||||||
"delete_account_description": "You must either remove yourself, transfer ownership, or delete these teams before you can delete your account.",
|
|
||||||
"empty_req_name": " खाली अनुरोध का नाम",
|
"empty_req_name": " खाली अनुरोध का नाम",
|
||||||
"f12_details": " (विवरण के लिए F12)",
|
"f12_details": " (विवरण के लिए F12)",
|
||||||
"gql_prettify_invalid_query": " अमान्य क्वेरी को सुंदर नहीं बना सका, क्वेरी सिंटैक्स त्रुटियों को हल नहीं कर सका और पुनः प्रयास करें",
|
"gql_prettify_invalid_query": " अमान्य क्वेरी को सुंदर नहीं बना सका, क्वेरी सिंटैक्स त्रुटियों को हल नहीं कर सका और पुनः प्रयास करें",
|
||||||
@@ -441,7 +438,6 @@
|
|||||||
"settings": {
|
"settings": {
|
||||||
"accent_color": "स्वरोंका रंग",
|
"accent_color": "स्वरोंका रंग",
|
||||||
"account": "खाता",
|
"account": "खाता",
|
||||||
"account_deleted": "Your account has been deleted",
|
|
||||||
"account_description": "अपनी खाता सेटिंग कस्टमाइज़ करें।",
|
"account_description": "अपनी खाता सेटिंग कस्टमाइज़ करें।",
|
||||||
"account_email_description": "आपका प्राथमिक ईमेल पता।",
|
"account_email_description": "आपका प्राथमिक ईमेल पता।",
|
||||||
"account_name_description": "यह आपका प्रदर्शन नाम है।",
|
"account_name_description": "यह आपका प्रदर्शन नाम है।",
|
||||||
@@ -450,8 +446,6 @@
|
|||||||
"change_font_size": "फॉण्ट आकार बदलें",
|
"change_font_size": "फॉण्ट आकार बदलें",
|
||||||
"choose_language": "भाषा चुनें",
|
"choose_language": "भाषा चुनें",
|
||||||
"dark_mode": "अँधेरा",
|
"dark_mode": "अँधेरा",
|
||||||
"delete_account": "Delete account",
|
|
||||||
"delete_account_description": "Once you delete your account, all your data will be permanently deleted. This action cannot be undone.",
|
|
||||||
"expand_navigation": "नेविगेशन का विस्तार करें",
|
"expand_navigation": "नेविगेशन का विस्तार करें",
|
||||||
"experiments": "प्रयोगों",
|
"experiments": "प्रयोगों",
|
||||||
"experiments_notice": "यह उन प्रयोगों का एक संग्रह है, जिन पर हम काम कर रहे हैं, जो उपयोगी, मज़ेदार, दोनों या न ही हो सकते हैं। वे अंतिम नहीं हैं और स्थिर नहीं हो सकते हैं, इसलिए यदि कुछ अजीब होता है, तो घबराएं नहीं। बस खतरे को बंद कर दें। एक तरफ मजाक,",
|
"experiments_notice": "यह उन प्रयोगों का एक संग्रह है, जिन पर हम काम कर रहे हैं, जो उपयोगी, मज़ेदार, दोनों या न ही हो सकते हैं। वे अंतिम नहीं हैं और स्थिर नहीं हो सकते हैं, इसलिए यदि कुछ अजीब होता है, तो घबराएं नहीं। बस खतरे को बंद कर दें। एक तरफ मजाक,",
|
||||||
|
|||||||
@@ -203,9 +203,6 @@
|
|||||||
"browser_support_sse": "Úgy tűnik, hogy ez a böngésző nem támogatja a kiszolgáló által küldött eseményeket.",
|
"browser_support_sse": "Úgy tűnik, hogy ez a böngésző nem támogatja a kiszolgáló által küldött eseményeket.",
|
||||||
"check_console_details": "Nézze meg a konzolnaplót a részletekért.",
|
"check_console_details": "Nézze meg a konzolnaplót a részletekért.",
|
||||||
"curl_invalid_format": "A cURL nincs megfelelően formázva",
|
"curl_invalid_format": "A cURL nincs megfelelően formázva",
|
||||||
"danger_zone": "Danger zone",
|
|
||||||
"delete_account": "Your account is currently an owner in these teams:",
|
|
||||||
"delete_account_description": "You must either remove yourself, transfer ownership, or delete these teams before you can delete your account.",
|
|
||||||
"empty_req_name": "Üres kérésnév",
|
"empty_req_name": "Üres kérésnév",
|
||||||
"f12_details": "(F12 a részletekért)",
|
"f12_details": "(F12 a részletekért)",
|
||||||
"gql_prettify_invalid_query": "Nem sikerült csinosítani egy érvénytelen lekérdezést, oldja meg a lekérdezés szintaktikai hibáit, és próbálja újra",
|
"gql_prettify_invalid_query": "Nem sikerült csinosítani egy érvénytelen lekérdezést, oldja meg a lekérdezés szintaktikai hibáit, és próbálja újra",
|
||||||
@@ -440,7 +437,6 @@
|
|||||||
"settings": {
|
"settings": {
|
||||||
"accent_color": "Kiemelőszín",
|
"accent_color": "Kiemelőszín",
|
||||||
"account": "Fiók",
|
"account": "Fiók",
|
||||||
"account_deleted": "Your account has been deleted",
|
|
||||||
"account_description": "A fiókbeállítások személyre szabása.",
|
"account_description": "A fiókbeállítások személyre szabása.",
|
||||||
"account_email_description": "Az Ön elsődleges e-mail-címe.",
|
"account_email_description": "Az Ön elsődleges e-mail-címe.",
|
||||||
"account_name_description": "Ez a megjelenített neve.",
|
"account_name_description": "Ez a megjelenített neve.",
|
||||||
@@ -449,8 +445,6 @@
|
|||||||
"change_font_size": "Betűméret megváltoztatása",
|
"change_font_size": "Betűméret megváltoztatása",
|
||||||
"choose_language": "Nyelv kiválasztása",
|
"choose_language": "Nyelv kiválasztása",
|
||||||
"dark_mode": "Sötét",
|
"dark_mode": "Sötét",
|
||||||
"delete_account": "Delete account",
|
|
||||||
"delete_account_description": "Once you delete your account, all your data will be permanently deleted. This action cannot be undone.",
|
|
||||||
"expand_navigation": "Navigáció kinyitása",
|
"expand_navigation": "Navigáció kinyitása",
|
||||||
"experiments": "Kísérletek",
|
"experiments": "Kísérletek",
|
||||||
"experiments_notice": "Ez olyan kísérletek gyűjteménye, amelyeken dolgozunk, és amelyek hasznosak, szórakoztatóak lehetnek, mindkettő, vagy egyik sem. Ezek nem véglegesek és nem stabilak, ezért ha valami túl furcsa dolog történik, ne essen pánikba. Egyszerűen kapcsolja ki a hibás dolgot. Viccet félretéve, ",
|
"experiments_notice": "Ez olyan kísérletek gyűjteménye, amelyeken dolgozunk, és amelyek hasznosak, szórakoztatóak lehetnek, mindkettő, vagy egyik sem. Ezek nem véglegesek és nem stabilak, ezért ha valami túl furcsa dolog történik, ne essen pánikba. Egyszerűen kapcsolja ki a hibás dolgot. Viccet félretéve, ",
|
||||||
|
|||||||
@@ -203,9 +203,6 @@
|
|||||||
"browser_support_sse": "Browser ini sepertinya tidak memiliki dukungan Server Sent Events.",
|
"browser_support_sse": "Browser ini sepertinya tidak memiliki dukungan Server Sent Events.",
|
||||||
"check_console_details": "Periksa console log untuk detailnya.",
|
"check_console_details": "Periksa console log untuk detailnya.",
|
||||||
"curl_invalid_format": "cURL tidak diformat dengan benar",
|
"curl_invalid_format": "cURL tidak diformat dengan benar",
|
||||||
"danger_zone": "Danger zone",
|
|
||||||
"delete_account": "Your account is currently an owner in these teams:",
|
|
||||||
"delete_account_description": "You must either remove yourself, transfer ownership, or delete these teams before you can delete your account.",
|
|
||||||
"empty_req_name": "Nama Permintaan Kosong",
|
"empty_req_name": "Nama Permintaan Kosong",
|
||||||
"f12_details": "(F12 untuk detailnya)",
|
"f12_details": "(F12 untuk detailnya)",
|
||||||
"gql_prettify_invalid_query": "Tidak dapat prettify kueri yang tidak valid, menyelesaikan kesalahan sintaksis kueri, dan coba lagi",
|
"gql_prettify_invalid_query": "Tidak dapat prettify kueri yang tidak valid, menyelesaikan kesalahan sintaksis kueri, dan coba lagi",
|
||||||
@@ -440,7 +437,6 @@
|
|||||||
"settings": {
|
"settings": {
|
||||||
"accent_color": "Accent color",
|
"accent_color": "Accent color",
|
||||||
"account": "Akun",
|
"account": "Akun",
|
||||||
"account_deleted": "Your account has been deleted",
|
|
||||||
"account_description": "Sesuaikan pengaturan akun Anda.",
|
"account_description": "Sesuaikan pengaturan akun Anda.",
|
||||||
"account_email_description": "Alamat surel utama Anda.",
|
"account_email_description": "Alamat surel utama Anda.",
|
||||||
"account_name_description": "Ini adalah nama tampilan Anda.",
|
"account_name_description": "Ini adalah nama tampilan Anda.",
|
||||||
@@ -449,8 +445,6 @@
|
|||||||
"change_font_size": "Ubah ukuran font",
|
"change_font_size": "Ubah ukuran font",
|
||||||
"choose_language": "Pilih bahasa",
|
"choose_language": "Pilih bahasa",
|
||||||
"dark_mode": "Gelap",
|
"dark_mode": "Gelap",
|
||||||
"delete_account": "Delete account",
|
|
||||||
"delete_account_description": "Once you delete your account, all your data will be permanently deleted. This action cannot be undone.",
|
|
||||||
"expand_navigation": "Perluas navigasi",
|
"expand_navigation": "Perluas navigasi",
|
||||||
"experiments": "Eksperimen",
|
"experiments": "Eksperimen",
|
||||||
"experiments_notice": "Ini adalah kumpulan eksperimen yang sedang kami kerjakan yang mungkin berguna, menyenangkan, keduanya, atau tidak keduanya. Mereka tidak final dan mungkin tidak stabil, jadi jika sesuatu yang terlalu aneh terjadi, jangan panik. Matikan saja. Kesampingkan lelucon, ",
|
"experiments_notice": "Ini adalah kumpulan eksperimen yang sedang kami kerjakan yang mungkin berguna, menyenangkan, keduanya, atau tidak keduanya. Mereka tidak final dan mungkin tidak stabil, jadi jika sesuatu yang terlalu aneh terjadi, jangan panik. Matikan saja. Kesampingkan lelucon, ",
|
||||||
|
|||||||
@@ -203,9 +203,6 @@
|
|||||||
"browser_support_sse": "Questo browser non sembra supportare gli eventi inviati dal server (Server Sent Events).",
|
"browser_support_sse": "Questo browser non sembra supportare gli eventi inviati dal server (Server Sent Events).",
|
||||||
"check_console_details": "Controlla il log della console per i dettagli.",
|
"check_console_details": "Controlla il log della console per i dettagli.",
|
||||||
"curl_invalid_format": "cURL non è formattato correttamente",
|
"curl_invalid_format": "cURL non è formattato correttamente",
|
||||||
"danger_zone": "Danger zone",
|
|
||||||
"delete_account": "Your account is currently an owner in these teams:",
|
|
||||||
"delete_account_description": "You must either remove yourself, transfer ownership, or delete these teams before you can delete your account.",
|
|
||||||
"empty_req_name": "Nome richiesta vuoto",
|
"empty_req_name": "Nome richiesta vuoto",
|
||||||
"f12_details": "(F12 per i dettagli)",
|
"f12_details": "(F12 per i dettagli)",
|
||||||
"gql_prettify_invalid_query": "Impossibile abbellire una query non valida, risolvere gli errori di sintassi della query e riprovare",
|
"gql_prettify_invalid_query": "Impossibile abbellire una query non valida, risolvere gli errori di sintassi della query e riprovare",
|
||||||
@@ -440,7 +437,6 @@
|
|||||||
"settings": {
|
"settings": {
|
||||||
"accent_color": "Colore in risalto",
|
"accent_color": "Colore in risalto",
|
||||||
"account": "Account",
|
"account": "Account",
|
||||||
"account_deleted": "Your account has been deleted",
|
|
||||||
"account_description": "Personalizza le impostazioni del tuo account.",
|
"account_description": "Personalizza le impostazioni del tuo account.",
|
||||||
"account_email_description": "Il tuo indirizzo email principale.",
|
"account_email_description": "Il tuo indirizzo email principale.",
|
||||||
"account_name_description": "Questo è il tuo nome mostrato.",
|
"account_name_description": "Questo è il tuo nome mostrato.",
|
||||||
@@ -449,8 +445,6 @@
|
|||||||
"change_font_size": "Cambia la dimensione dei caratteri",
|
"change_font_size": "Cambia la dimensione dei caratteri",
|
||||||
"choose_language": "Scegli la lingua",
|
"choose_language": "Scegli la lingua",
|
||||||
"dark_mode": "Scuro",
|
"dark_mode": "Scuro",
|
||||||
"delete_account": "Delete account",
|
|
||||||
"delete_account_description": "Once you delete your account, all your data will be permanently deleted. This action cannot be undone.",
|
|
||||||
"expand_navigation": "Espandi navigazione",
|
"expand_navigation": "Espandi navigazione",
|
||||||
"experiments": "Sperimentale",
|
"experiments": "Sperimentale",
|
||||||
"experiments_notice": "Questa è una raccolta di esperimenti su cui stiamo lavorando che potrebbero rivelarsi utili, divertenti, entrambi o nessuno dei due. Non sono definitivi e potrebbero non essere stabili, quindi se succede qualcosa di eccessivamente strano, niente panico. Basta solo disabilitare quella dannata cosa. Scherzi a parte, ",
|
"experiments_notice": "Questa è una raccolta di esperimenti su cui stiamo lavorando che potrebbero rivelarsi utili, divertenti, entrambi o nessuno dei due. Non sono definitivi e potrebbero non essere stabili, quindi se succede qualcosa di eccessivamente strano, niente panico. Basta solo disabilitare quella dannata cosa. Scherzi a parte, ",
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -203,9 +203,6 @@
|
|||||||
"browser_support_sse": "이 브라우저는 서버 전송 이벤트를 지원하지 않는 것 같습니다.",
|
"browser_support_sse": "이 브라우저는 서버 전송 이벤트를 지원하지 않는 것 같습니다.",
|
||||||
"check_console_details": "자세한 내용은 콘솔 로그를 확인하세요.",
|
"check_console_details": "자세한 내용은 콘솔 로그를 확인하세요.",
|
||||||
"curl_invalid_format": "cURL 형식이 올바르지 않습니다.",
|
"curl_invalid_format": "cURL 형식이 올바르지 않습니다.",
|
||||||
"danger_zone": "Danger zone",
|
|
||||||
"delete_account": "Your account is currently an owner in these teams:",
|
|
||||||
"delete_account_description": "You must either remove yourself, transfer ownership, or delete these teams before you can delete your account.",
|
|
||||||
"empty_req_name": "빈 요청 이름",
|
"empty_req_name": "빈 요청 이름",
|
||||||
"f12_details": "(자세한 내용은 F12)",
|
"f12_details": "(자세한 내용은 F12)",
|
||||||
"gql_prettify_invalid_query": "잘못된 쿼리를 구문 강조할 수 없습니다. 쿼리 구문 오류를 해결하고 다시 시도하세요.",
|
"gql_prettify_invalid_query": "잘못된 쿼리를 구문 강조할 수 없습니다. 쿼리 구문 오류를 해결하고 다시 시도하세요.",
|
||||||
@@ -440,7 +437,6 @@
|
|||||||
"settings": {
|
"settings": {
|
||||||
"accent_color": "강조색",
|
"accent_color": "강조색",
|
||||||
"account": "계정",
|
"account": "계정",
|
||||||
"account_deleted": "Your account has been deleted",
|
|
||||||
"account_description": "계정 설정을 수정합니다.",
|
"account_description": "계정 설정을 수정합니다.",
|
||||||
"account_email_description": "기본 이메일 주소입니다.",
|
"account_email_description": "기본 이메일 주소입니다.",
|
||||||
"account_name_description": "디스플레이 이름입니다.",
|
"account_name_description": "디스플레이 이름입니다.",
|
||||||
@@ -449,8 +445,6 @@
|
|||||||
"change_font_size": "글자 크기 변경",
|
"change_font_size": "글자 크기 변경",
|
||||||
"choose_language": "언어 선택",
|
"choose_language": "언어 선택",
|
||||||
"dark_mode": "어두운 테마",
|
"dark_mode": "어두운 테마",
|
||||||
"delete_account": "Delete account",
|
|
||||||
"delete_account_description": "Once you delete your account, all your data will be permanently deleted. This action cannot be undone.",
|
|
||||||
"expand_navigation": "내비게이션 확장",
|
"expand_navigation": "내비게이션 확장",
|
||||||
"experiments": "실험실",
|
"experiments": "실험실",
|
||||||
"experiments_notice": "이것은 유용하거나 재미있거나 둘 다이거나 어느 쪽도 아닌, 아직 실험 중인 기능입니다. 완성되지 않았고 안정적이지 않을 수 있으므로 이상한 일이 발생하더라도 당황하지 마세요. 진지하게 말해서, 끄세요.",
|
"experiments_notice": "이것은 유용하거나 재미있거나 둘 다이거나 어느 쪽도 아닌, 아직 실험 중인 기능입니다. 완성되지 않았고 안정적이지 않을 수 있으므로 이상한 일이 발생하더라도 당황하지 마세요. 진지하게 말해서, 끄세요.",
|
||||||
|
|||||||
@@ -203,9 +203,6 @@
|
|||||||
"browser_support_sse": "Deze browser lijkt geen ondersteuning te hebben voor door de server verzonden gebeurtenissen.",
|
"browser_support_sse": "Deze browser lijkt geen ondersteuning te hebben voor door de server verzonden gebeurtenissen.",
|
||||||
"check_console_details": "Controleer het consolelogboek voor details.",
|
"check_console_details": "Controleer het consolelogboek voor details.",
|
||||||
"curl_invalid_format": "cURL is niet correct geformatteerd",
|
"curl_invalid_format": "cURL is niet correct geformatteerd",
|
||||||
"danger_zone": "Danger zone",
|
|
||||||
"delete_account": "Your account is currently an owner in these teams:",
|
|
||||||
"delete_account_description": "You must either remove yourself, transfer ownership, or delete these teams before you can delete your account.",
|
|
||||||
"empty_req_name": "Lege aanvraagnaam",
|
"empty_req_name": "Lege aanvraagnaam",
|
||||||
"f12_details": "(F12 voor details)",
|
"f12_details": "(F12 voor details)",
|
||||||
"gql_prettify_invalid_query": "Kon een ongeldige zoekopdracht niet mooier maken, syntaxisfouten in de query oplossen en opnieuw proberen",
|
"gql_prettify_invalid_query": "Kon een ongeldige zoekopdracht niet mooier maken, syntaxisfouten in de query oplossen en opnieuw proberen",
|
||||||
@@ -440,7 +437,6 @@
|
|||||||
"settings": {
|
"settings": {
|
||||||
"accent_color": "Accentkleur",
|
"accent_color": "Accentkleur",
|
||||||
"account": "Account",
|
"account": "Account",
|
||||||
"account_deleted": "Your account has been deleted",
|
|
||||||
"account_description": "Pas uw accountinstellingen aan.",
|
"account_description": "Pas uw accountinstellingen aan.",
|
||||||
"account_email_description": "Uw primaire e-mailadres.",
|
"account_email_description": "Uw primaire e-mailadres.",
|
||||||
"account_name_description": "Dit is uw weergavenaam.",
|
"account_name_description": "Dit is uw weergavenaam.",
|
||||||
@@ -449,8 +445,6 @@
|
|||||||
"change_font_size": "Verander lettergrootte",
|
"change_font_size": "Verander lettergrootte",
|
||||||
"choose_language": "Kies een taal",
|
"choose_language": "Kies een taal",
|
||||||
"dark_mode": "Donker",
|
"dark_mode": "Donker",
|
||||||
"delete_account": "Delete account",
|
|
||||||
"delete_account_description": "Once you delete your account, all your data will be permanently deleted. This action cannot be undone.",
|
|
||||||
"expand_navigation": "Expand navigation",
|
"expand_navigation": "Expand navigation",
|
||||||
"experiments": "Experimentele functies",
|
"experiments": "Experimentele functies",
|
||||||
"experiments_notice": "Dit is een verzameling experimenten waaraan we werken en die nuttig, leuk, beide of geen van beide kunnen te zijn. Ze zijn niet definitief en zijn mogelijk niet stabiel, dus als er iets heel raars gebeurt, raak dan niet in paniek. Zet de functie uit, en",
|
"experiments_notice": "Dit is een verzameling experimenten waaraan we werken en die nuttig, leuk, beide of geen van beide kunnen te zijn. Ze zijn niet definitief en zijn mogelijk niet stabiel, dus als er iets heel raars gebeurt, raak dan niet in paniek. Zet de functie uit, en",
|
||||||
|
|||||||
@@ -203,9 +203,6 @@
|
|||||||
"browser_support_sse": "Denne nettleseren ser ikke ut til å ha Server Sent Events -støtte.",
|
"browser_support_sse": "Denne nettleseren ser ikke ut til å ha Server Sent Events -støtte.",
|
||||||
"check_console_details": "Sjekk konsollloggen for detaljer.",
|
"check_console_details": "Sjekk konsollloggen for detaljer.",
|
||||||
"curl_invalid_format": "cURL er ikke riktig formatert",
|
"curl_invalid_format": "cURL er ikke riktig formatert",
|
||||||
"danger_zone": "Danger zone",
|
|
||||||
"delete_account": "Your account is currently an owner in these teams:",
|
|
||||||
"delete_account_description": "You must either remove yourself, transfer ownership, or delete these teams before you can delete your account.",
|
|
||||||
"empty_req_name": "Tom forespørselsnavn",
|
"empty_req_name": "Tom forespørselsnavn",
|
||||||
"f12_details": "(F12 for detaljer)",
|
"f12_details": "(F12 for detaljer)",
|
||||||
"gql_prettify_invalid_query": "Kunne ikke forskjønne en ugyldig spørring, løse spørringssyntaksfeil og prøve igjen",
|
"gql_prettify_invalid_query": "Kunne ikke forskjønne en ugyldig spørring, løse spørringssyntaksfeil og prøve igjen",
|
||||||
@@ -440,7 +437,6 @@
|
|||||||
"settings": {
|
"settings": {
|
||||||
"accent_color": "Aksentfarge",
|
"accent_color": "Aksentfarge",
|
||||||
"account": "Konto",
|
"account": "Konto",
|
||||||
"account_deleted": "Your account has been deleted",
|
|
||||||
"account_description": "Tilpass kontoinnstillingene dine.",
|
"account_description": "Tilpass kontoinnstillingene dine.",
|
||||||
"account_email_description": "Din primære e-postadresse.",
|
"account_email_description": "Din primære e-postadresse.",
|
||||||
"account_name_description": "Dette er visningsnavnet ditt.",
|
"account_name_description": "Dette er visningsnavnet ditt.",
|
||||||
@@ -449,8 +445,6 @@
|
|||||||
"change_font_size": "Endre skriftstørrelse",
|
"change_font_size": "Endre skriftstørrelse",
|
||||||
"choose_language": "Velg språk",
|
"choose_language": "Velg språk",
|
||||||
"dark_mode": "Mørk",
|
"dark_mode": "Mørk",
|
||||||
"delete_account": "Delete account",
|
|
||||||
"delete_account_description": "Once you delete your account, all your data will be permanently deleted. This action cannot be undone.",
|
|
||||||
"expand_navigation": "Expand navigation",
|
"expand_navigation": "Expand navigation",
|
||||||
"experiments": "Eksperimenter",
|
"experiments": "Eksperimenter",
|
||||||
"experiments_notice": "Dette er en samling eksperimenter vi jobber med som kan vise seg å være nyttige, morsomme, begge deler eller ingen av dem. De er ikke endelige og er kanskje ikke stabile, så hvis det skjer noe altfor rart, ikke få panikk. Bare slå av det. Vitser til side,",
|
"experiments_notice": "Dette er en samling eksperimenter vi jobber med som kan vise seg å være nyttige, morsomme, begge deler eller ingen av dem. De er ikke endelige og er kanskje ikke stabile, så hvis det skjer noe altfor rart, ikke få panikk. Bare slå av det. Vitser til side,",
|
||||||
|
|||||||
@@ -203,9 +203,6 @@
|
|||||||
"browser_support_sse": "Wygląda na to, że ta przeglądarka nie obsługuje zdarzeń wysłanych przez serwer.",
|
"browser_support_sse": "Wygląda na to, że ta przeglądarka nie obsługuje zdarzeń wysłanych przez serwer.",
|
||||||
"check_console_details": "Sprawdź dziennik konsoli, aby uzyskać szczegółowe informacje.",
|
"check_console_details": "Sprawdź dziennik konsoli, aby uzyskać szczegółowe informacje.",
|
||||||
"curl_invalid_format": "cURL nie jest poprawnie sformatowany",
|
"curl_invalid_format": "cURL nie jest poprawnie sformatowany",
|
||||||
"danger_zone": "Danger zone",
|
|
||||||
"delete_account": "Your account is currently an owner in these teams:",
|
|
||||||
"delete_account_description": "You must either remove yourself, transfer ownership, or delete these teams before you can delete your account.",
|
|
||||||
"empty_req_name": "Pusta nazwa żądania",
|
"empty_req_name": "Pusta nazwa żądania",
|
||||||
"f12_details": "(F12 po szczegóły)",
|
"f12_details": "(F12 po szczegóły)",
|
||||||
"gql_prettify_invalid_query": "Nie można poprawić czytelności nieprawidłowego zapytania, napraw błędy składni zapytania i spróbuj ponownie",
|
"gql_prettify_invalid_query": "Nie można poprawić czytelności nieprawidłowego zapytania, napraw błędy składni zapytania i spróbuj ponownie",
|
||||||
@@ -440,7 +437,6 @@
|
|||||||
"settings": {
|
"settings": {
|
||||||
"accent_color": "Kolor akcentu",
|
"accent_color": "Kolor akcentu",
|
||||||
"account": "Konto",
|
"account": "Konto",
|
||||||
"account_deleted": "Your account has been deleted",
|
|
||||||
"account_description": "Dostosuj ustawienia swojego konta.",
|
"account_description": "Dostosuj ustawienia swojego konta.",
|
||||||
"account_email_description": "Twój podstawowy adres e-mail.",
|
"account_email_description": "Twój podstawowy adres e-mail.",
|
||||||
"account_name_description": "To jest Twoja nazwa wyświetlana.",
|
"account_name_description": "To jest Twoja nazwa wyświetlana.",
|
||||||
@@ -449,8 +445,6 @@
|
|||||||
"change_font_size": "Zmień rozmiar czczionki",
|
"change_font_size": "Zmień rozmiar czczionki",
|
||||||
"choose_language": "Wybierz język",
|
"choose_language": "Wybierz język",
|
||||||
"dark_mode": "Ciemny",
|
"dark_mode": "Ciemny",
|
||||||
"delete_account": "Delete account",
|
|
||||||
"delete_account_description": "Once you delete your account, all your data will be permanently deleted. This action cannot be undone.",
|
|
||||||
"expand_navigation": "Expand navigation",
|
"expand_navigation": "Expand navigation",
|
||||||
"experiments": "Eksperymenty",
|
"experiments": "Eksperymenty",
|
||||||
"experiments_notice": "To jest zbiór eksperymentów, nad którymi pracujemy, które mogą okazać się przydatne, zabawne, obie te rzeczy albo żadne. Nie są ostateczne i mogą nie być stabilne, więc jeśli wydarzy się coś zbyt dziwnego, nie panikuj. Po prostu wyłącz to cholerstwo. Żarty na bok,",
|
"experiments_notice": "To jest zbiór eksperymentów, nad którymi pracujemy, które mogą okazać się przydatne, zabawne, obie te rzeczy albo żadne. Nie są ostateczne i mogą nie być stabilne, więc jeśli wydarzy się coś zbyt dziwnego, nie panikuj. Po prostu wyłącz to cholerstwo. Żarty na bok,",
|
||||||
|
|||||||
@@ -203,9 +203,6 @@
|
|||||||
"browser_support_sse": "Este navegador não parece ter suporte para eventos enviados pelo servidor.",
|
"browser_support_sse": "Este navegador não parece ter suporte para eventos enviados pelo servidor.",
|
||||||
"check_console_details": "Verifique o log do console para obter detalhes.",
|
"check_console_details": "Verifique o log do console para obter detalhes.",
|
||||||
"curl_invalid_format": "cURL não está formatado corretamente",
|
"curl_invalid_format": "cURL não está formatado corretamente",
|
||||||
"danger_zone": "Danger zone",
|
|
||||||
"delete_account": "Your account is currently an owner in these teams:",
|
|
||||||
"delete_account_description": "You must either remove yourself, transfer ownership, or delete these teams before you can delete your account.",
|
|
||||||
"empty_req_name": "Nome de requisição vazio",
|
"empty_req_name": "Nome de requisição vazio",
|
||||||
"f12_details": "(F12 para detalhes)",
|
"f12_details": "(F12 para detalhes)",
|
||||||
"gql_prettify_invalid_query": "Não foi possível justificar uma requisição inválida, resolva os erros de sintaxe da requisição e tente novamente",
|
"gql_prettify_invalid_query": "Não foi possível justificar uma requisição inválida, resolva os erros de sintaxe da requisição e tente novamente",
|
||||||
@@ -440,7 +437,6 @@
|
|||||||
"settings": {
|
"settings": {
|
||||||
"accent_color": "Cor de destaque",
|
"accent_color": "Cor de destaque",
|
||||||
"account": "Conta",
|
"account": "Conta",
|
||||||
"account_deleted": "Your account has been deleted",
|
|
||||||
"account_description": "Personalize as configurações da sua conta.",
|
"account_description": "Personalize as configurações da sua conta.",
|
||||||
"account_email_description": "Seu endereço de e-mail principal.",
|
"account_email_description": "Seu endereço de e-mail principal.",
|
||||||
"account_name_description": "Este é o seu nome de exibição.",
|
"account_name_description": "Este é o seu nome de exibição.",
|
||||||
@@ -449,8 +445,6 @@
|
|||||||
"change_font_size": "Mudar TAMANHO DA FONTE",
|
"change_font_size": "Mudar TAMANHO DA FONTE",
|
||||||
"choose_language": "Escolha o seu idioma",
|
"choose_language": "Escolha o seu idioma",
|
||||||
"dark_mode": "Escuro",
|
"dark_mode": "Escuro",
|
||||||
"delete_account": "Delete account",
|
|
||||||
"delete_account_description": "Once you delete your account, all your data will be permanently deleted. This action cannot be undone.",
|
|
||||||
"expand_navigation": "Expandir navegação",
|
"expand_navigation": "Expandir navegação",
|
||||||
"experiments": "Experimentos",
|
"experiments": "Experimentos",
|
||||||
"experiments_notice": "Esta é uma coleção de experimentos em que estamos trabalhando que podem ser úteis, divertidos, ambos ou nenhum dos dois. Eles não são finais e podem não ser estáveis, portanto, se algo muito estranho acontecer, não entre em pânico. Apenas desligue essa maldita coisa. Piadas à parte,",
|
"experiments_notice": "Esta é uma coleção de experimentos em que estamos trabalhando que podem ser úteis, divertidos, ambos ou nenhum dos dois. Eles não são finais e podem não ser estáveis, portanto, se algo muito estranho acontecer, não entre em pânico. Apenas desligue essa maldita coisa. Piadas à parte,",
|
||||||
|
|||||||
@@ -203,9 +203,6 @@
|
|||||||
"browser_support_sse": "Este navegador não parece ter suporte para eventos enviados pelo servidor.",
|
"browser_support_sse": "Este navegador não parece ter suporte para eventos enviados pelo servidor.",
|
||||||
"check_console_details": "Verifique o log do console para obter detalhes.",
|
"check_console_details": "Verifique o log do console para obter detalhes.",
|
||||||
"curl_invalid_format": "cURL não está formatado corretamente",
|
"curl_invalid_format": "cURL não está formatado corretamente",
|
||||||
"danger_zone": "Danger zone",
|
|
||||||
"delete_account": "Your account is currently an owner in these teams:",
|
|
||||||
"delete_account_description": "You must either remove yourself, transfer ownership, or delete these teams before you can delete your account.",
|
|
||||||
"empty_req_name": "Nome do pedido vazio",
|
"empty_req_name": "Nome do pedido vazio",
|
||||||
"f12_details": "(F12 para detalhes)",
|
"f12_details": "(F12 para detalhes)",
|
||||||
"gql_prettify_invalid_query": "Não foi possível justificar uma consulta inválida, resolva os erros de sintaxe da consulta e tente novamente",
|
"gql_prettify_invalid_query": "Não foi possível justificar uma consulta inválida, resolva os erros de sintaxe da consulta e tente novamente",
|
||||||
@@ -440,7 +437,6 @@
|
|||||||
"settings": {
|
"settings": {
|
||||||
"accent_color": "Cor de destaque",
|
"accent_color": "Cor de destaque",
|
||||||
"account": "Conta",
|
"account": "Conta",
|
||||||
"account_deleted": "Your account has been deleted",
|
|
||||||
"account_description": "Personalize as configurações da sua conta.",
|
"account_description": "Personalize as configurações da sua conta.",
|
||||||
"account_email_description": "Seu endereço de e-mail principal.",
|
"account_email_description": "Seu endereço de e-mail principal.",
|
||||||
"account_name_description": "Este é o seu nome de exibição.",
|
"account_name_description": "Este é o seu nome de exibição.",
|
||||||
@@ -449,8 +445,6 @@
|
|||||||
"change_font_size": "Mudar TAMANHO DA FONTE",
|
"change_font_size": "Mudar TAMANHO DA FONTE",
|
||||||
"choose_language": "Escolha o seu idioma",
|
"choose_language": "Escolha o seu idioma",
|
||||||
"dark_mode": "Escuro",
|
"dark_mode": "Escuro",
|
||||||
"delete_account": "Delete account",
|
|
||||||
"delete_account_description": "Once you delete your account, all your data will be permanently deleted. This action cannot be undone.",
|
|
||||||
"expand_navigation": "Expand navigation",
|
"expand_navigation": "Expand navigation",
|
||||||
"experiments": "Experimentos",
|
"experiments": "Experimentos",
|
||||||
"experiments_notice": "Esta é uma coleção de experimentos em que estamos trabalhando que podem ser úteis, divertidos, ambos ou nenhum dos dois. Eles não são finais e podem não ser estáveis, portanto, se algo muito estranho acontecer, não entre em pânico. Apenas desligue essa maldita coisa. Piadas à parte,",
|
"experiments_notice": "Esta é uma coleção de experimentos em que estamos trabalhando que podem ser úteis, divertidos, ambos ou nenhum dos dois. Eles não são finais e podem não ser estáveis, portanto, se algo muito estranho acontecer, não entre em pânico. Apenas desligue essa maldita coisa. Piadas à parte,",
|
||||||
|
|||||||
@@ -203,9 +203,6 @@
|
|||||||
"browser_support_sse": "Acest browser pare să nu aibă suport pentru Server Sent Events.",
|
"browser_support_sse": "Acest browser pare să nu aibă suport pentru Server Sent Events.",
|
||||||
"check_console_details": "Verificați jurnalul consolei pentru detalii.",
|
"check_console_details": "Verificați jurnalul consolei pentru detalii.",
|
||||||
"curl_invalid_format": "cURL nu este formatat corect",
|
"curl_invalid_format": "cURL nu este formatat corect",
|
||||||
"danger_zone": "Danger zone",
|
|
||||||
"delete_account": "Your account is currently an owner in these teams:",
|
|
||||||
"delete_account_description": "You must either remove yourself, transfer ownership, or delete these teams before you can delete your account.",
|
|
||||||
"empty_req_name": "Nume cerere goală",
|
"empty_req_name": "Nume cerere goală",
|
||||||
"f12_details": "(F12 pentru detalii)",
|
"f12_details": "(F12 pentru detalii)",
|
||||||
"gql_prettify_invalid_query": "Nu am putut formata o interogare nevalidă, rezolvați erorile de sintaxă ale interogării și încercați din nou",
|
"gql_prettify_invalid_query": "Nu am putut formata o interogare nevalidă, rezolvați erorile de sintaxă ale interogării și încercați din nou",
|
||||||
@@ -440,7 +437,6 @@
|
|||||||
"settings": {
|
"settings": {
|
||||||
"accent_color": "Culoare de accent",
|
"accent_color": "Culoare de accent",
|
||||||
"account": "Cont",
|
"account": "Cont",
|
||||||
"account_deleted": "Your account has been deleted",
|
|
||||||
"account_description": "Personalizați setările contului.",
|
"account_description": "Personalizați setările contului.",
|
||||||
"account_email_description": "Adresa dvs. de e-mail principală.",
|
"account_email_description": "Adresa dvs. de e-mail principală.",
|
||||||
"account_name_description": "Acesta este numele dvs. afișat.",
|
"account_name_description": "Acesta este numele dvs. afișat.",
|
||||||
@@ -449,8 +445,6 @@
|
|||||||
"change_font_size": "Schimbă marimea fontului",
|
"change_font_size": "Schimbă marimea fontului",
|
||||||
"choose_language": "Alege limba",
|
"choose_language": "Alege limba",
|
||||||
"dark_mode": "Întunecat",
|
"dark_mode": "Întunecat",
|
||||||
"delete_account": "Delete account",
|
|
||||||
"delete_account_description": "Once you delete your account, all your data will be permanently deleted. This action cannot be undone.",
|
|
||||||
"expand_navigation": "Expandează navigarea",
|
"expand_navigation": "Expandează navigarea",
|
||||||
"experiments": "Experimente",
|
"experiments": "Experimente",
|
||||||
"experiments_notice": "Aceasta este o colecție de experimente la care lucrăm, care s-ar putea dovedi a fi utile, distractive, ambele sau nici una. Nu sunt definitive și s-ar putea să nu fie stabile, așa că, dacă se întâmplă ceva prea ciudat, nu vă panicați. Doar oprește-l. Lăsând glumele deoparte,",
|
"experiments_notice": "Aceasta este o colecție de experimente la care lucrăm, care s-ar putea dovedi a fi utile, distractive, ambele sau nici una. Nu sunt definitive și s-ar putea să nu fie stabile, așa că, dacă se întâmplă ceva prea ciudat, nu vă panicați. Doar oprește-l. Lăsând glumele deoparte,",
|
||||||
|
|||||||
@@ -203,9 +203,6 @@
|
|||||||
"browser_support_sse": "Похоже, в этом браузере нет поддержки событий, отправленных сервером.",
|
"browser_support_sse": "Похоже, в этом браузере нет поддержки событий, отправленных сервером.",
|
||||||
"check_console_details": "Подробности смотрите в журнале консоли.",
|
"check_console_details": "Подробности смотрите в журнале консоли.",
|
||||||
"curl_invalid_format": "cURL неправильно отформатирован",
|
"curl_invalid_format": "cURL неправильно отформатирован",
|
||||||
"danger_zone": "Danger zone",
|
|
||||||
"delete_account": "Your account is currently an owner in these teams:",
|
|
||||||
"delete_account_description": "You must either remove yourself, transfer ownership, or delete these teams before you can delete your account.",
|
|
||||||
"empty_req_name": "Пустое имя запроса",
|
"empty_req_name": "Пустое имя запроса",
|
||||||
"f12_details": "(F12 для подробностей)",
|
"f12_details": "(F12 для подробностей)",
|
||||||
"gql_prettify_invalid_query": "Не удалось определить недопустимый запрос, устранить синтаксические ошибки запроса и повторить попытку.",
|
"gql_prettify_invalid_query": "Не удалось определить недопустимый запрос, устранить синтаксические ошибки запроса и повторить попытку.",
|
||||||
@@ -440,7 +437,6 @@
|
|||||||
"settings": {
|
"settings": {
|
||||||
"accent_color": "Основной цвет",
|
"accent_color": "Основной цвет",
|
||||||
"account": "Счет",
|
"account": "Счет",
|
||||||
"account_deleted": "Your account has been deleted",
|
|
||||||
"account_description": "Настройте параметры своей учетной записи.",
|
"account_description": "Настройте параметры своей учетной записи.",
|
||||||
"account_email_description": "Ваш основной адрес электронной почты.",
|
"account_email_description": "Ваш основной адрес электронной почты.",
|
||||||
"account_name_description": "Это ваше отображаемое имя.",
|
"account_name_description": "Это ваше отображаемое имя.",
|
||||||
@@ -449,8 +445,6 @@
|
|||||||
"change_font_size": "Изменить размер шрифта",
|
"change_font_size": "Изменить размер шрифта",
|
||||||
"choose_language": "Выберите язык",
|
"choose_language": "Выберите язык",
|
||||||
"dark_mode": "Темный",
|
"dark_mode": "Темный",
|
||||||
"delete_account": "Delete account",
|
|
||||||
"delete_account_description": "Once you delete your account, all your data will be permanently deleted. This action cannot be undone.",
|
|
||||||
"expand_navigation": "Раскрыть панель навигации",
|
"expand_navigation": "Раскрыть панель навигации",
|
||||||
"experiments": "Эксперименты",
|
"experiments": "Эксперименты",
|
||||||
"experiments_notice": "Это набор экспериментов, над которыми мы работаем, которые могут оказаться полезными, интересными, и тем, и другим, или ни тем, ни другим. Они не окончательные и могут быть нестабильными, поэтому, если произойдет что-то слишком странное, не паникуйте. Просто выключи эту чертову штуку. Шутки в сторону,",
|
"experiments_notice": "Это набор экспериментов, над которыми мы работаем, которые могут оказаться полезными, интересными, и тем, и другим, или ни тем, ни другим. Они не окончательные и могут быть нестабильными, поэтому, если произойдет что-то слишком странное, не паникуйте. Просто выключи эту чертову штуку. Шутки в сторону,",
|
||||||
|
|||||||
@@ -203,9 +203,6 @@
|
|||||||
"browser_support_sse": "Изгледа да овај прегледач нема подршку за Послане догађаје са сервера.",
|
"browser_support_sse": "Изгледа да овај прегледач нема подршку за Послане догађаје са сервера.",
|
||||||
"check_console_details": "Детаље потражите у дневнику конзоле.",
|
"check_console_details": "Детаље потражите у дневнику конзоле.",
|
||||||
"curl_invalid_format": "цУРЛ није правилно форматиран",
|
"curl_invalid_format": "цУРЛ није правилно форматиран",
|
||||||
"danger_zone": "Danger zone",
|
|
||||||
"delete_account": "Your account is currently an owner in these teams:",
|
|
||||||
"delete_account_description": "You must either remove yourself, transfer ownership, or delete these teams before you can delete your account.",
|
|
||||||
"empty_req_name": "Празан назив захтева",
|
"empty_req_name": "Празан назив захтева",
|
||||||
"f12_details": "(Ф12 за детаље)",
|
"f12_details": "(Ф12 за детаље)",
|
||||||
"gql_prettify_invalid_query": "Није могуће унапредити неважећи упит, решити грешке у синтакси упита и покушати поново",
|
"gql_prettify_invalid_query": "Није могуће унапредити неважећи упит, решити грешке у синтакси упита и покушати поново",
|
||||||
@@ -440,7 +437,6 @@
|
|||||||
"settings": {
|
"settings": {
|
||||||
"accent_color": "Боја акцента",
|
"accent_color": "Боја акцента",
|
||||||
"account": "Рачун",
|
"account": "Рачун",
|
||||||
"account_deleted": "Your account has been deleted",
|
|
||||||
"account_description": "Прилагодите поставке налога.",
|
"account_description": "Прилагодите поставке налога.",
|
||||||
"account_email_description": "Ваша примарна адреса е -поште.",
|
"account_email_description": "Ваша примарна адреса е -поште.",
|
||||||
"account_name_description": "Ово је ваше име за приказ.",
|
"account_name_description": "Ово је ваше име за приказ.",
|
||||||
@@ -449,8 +445,6 @@
|
|||||||
"change_font_size": "Промените величину фонта",
|
"change_font_size": "Промените величину фонта",
|
||||||
"choose_language": "Изабери језик",
|
"choose_language": "Изабери језик",
|
||||||
"dark_mode": "Дарк",
|
"dark_mode": "Дарк",
|
||||||
"delete_account": "Delete account",
|
|
||||||
"delete_account_description": "Once you delete your account, all your data will be permanently deleted. This action cannot be undone.",
|
|
||||||
"expand_navigation": "Expand navigation",
|
"expand_navigation": "Expand navigation",
|
||||||
"experiments": "Експерименти",
|
"experiments": "Експерименти",
|
||||||
"experiments_notice": "Ово је збирка експеримената на којима радимо и који би се могли показати корисним, забавним, обоје или ниједно. Нису коначни и можда нису стабилни, па ако се догоди нешто превише чудно, немојте паничарити. Само искључите опасну ствар. Шалу на страну,",
|
"experiments_notice": "Ово је збирка експеримената на којима радимо и који би се могли показати корисним, забавним, обоје или ниједно. Нису коначни и можда нису стабилни, па ако се догоди нешто превише чудно, немојте паничарити. Само искључите опасну ствар. Шалу на страну,",
|
||||||
|
|||||||
@@ -203,9 +203,6 @@
|
|||||||
"browser_support_sse": "Den här webbläsaren verkar inte ha stöd för Server Sent Events.",
|
"browser_support_sse": "Den här webbläsaren verkar inte ha stöd för Server Sent Events.",
|
||||||
"check_console_details": "Kontrollera konsolloggen för mer information.",
|
"check_console_details": "Kontrollera konsolloggen för mer information.",
|
||||||
"curl_invalid_format": "cURL är inte korrekt formaterad",
|
"curl_invalid_format": "cURL är inte korrekt formaterad",
|
||||||
"danger_zone": "Danger zone",
|
|
||||||
"delete_account": "Your account is currently an owner in these teams:",
|
|
||||||
"delete_account_description": "You must either remove yourself, transfer ownership, or delete these teams before you can delete your account.",
|
|
||||||
"empty_req_name": "Tom förfrågningsnamn",
|
"empty_req_name": "Tom förfrågningsnamn",
|
||||||
"f12_details": "(F12 för detaljer)",
|
"f12_details": "(F12 för detaljer)",
|
||||||
"gql_prettify_invalid_query": "Det gick inte att pryda en ogiltig fråga, lösa frågesyntaxfel och försök igen",
|
"gql_prettify_invalid_query": "Det gick inte att pryda en ogiltig fråga, lösa frågesyntaxfel och försök igen",
|
||||||
@@ -440,7 +437,6 @@
|
|||||||
"settings": {
|
"settings": {
|
||||||
"accent_color": "Accentfärg",
|
"accent_color": "Accentfärg",
|
||||||
"account": "konto",
|
"account": "konto",
|
||||||
"account_deleted": "Your account has been deleted",
|
|
||||||
"account_description": "Anpassa dina kontoinställningar.",
|
"account_description": "Anpassa dina kontoinställningar.",
|
||||||
"account_email_description": "Din primära e -postadress.",
|
"account_email_description": "Din primära e -postadress.",
|
||||||
"account_name_description": "Detta är ditt visningsnamn.",
|
"account_name_description": "Detta är ditt visningsnamn.",
|
||||||
@@ -449,8 +445,6 @@
|
|||||||
"change_font_size": "Ändra typsnittsstorlek",
|
"change_font_size": "Ändra typsnittsstorlek",
|
||||||
"choose_language": "Välj språk",
|
"choose_language": "Välj språk",
|
||||||
"dark_mode": "Mörk",
|
"dark_mode": "Mörk",
|
||||||
"delete_account": "Delete account",
|
|
||||||
"delete_account_description": "Once you delete your account, all your data will be permanently deleted. This action cannot be undone.",
|
|
||||||
"expand_navigation": "Expand navigation",
|
"expand_navigation": "Expand navigation",
|
||||||
"experiments": "Experiment",
|
"experiments": "Experiment",
|
||||||
"experiments_notice": "Det här är en samling experiment vi arbetar med som kan visa sig vara användbara, roliga, båda eller ingen av dem. De är inte slutgiltiga och kanske inte är stabila, så om inget alltför konstigt händer, var inte rädd. Stäng bara av det jävla. Skämt åt sidan,",
|
"experiments_notice": "Det här är en samling experiment vi arbetar med som kan visa sig vara användbara, roliga, båda eller ingen av dem. De är inte slutgiltiga och kanske inte är stabila, så om inget alltför konstigt händer, var inte rädd. Stäng bara av det jävla. Skämt åt sidan,",
|
||||||
|
|||||||
@@ -203,9 +203,6 @@
|
|||||||
"browser_support_sse": "Bu tarayıcıda SSE desteği yok gibi görünüyor.",
|
"browser_support_sse": "Bu tarayıcıda SSE desteği yok gibi görünüyor.",
|
||||||
"check_console_details": "Ayrıntılar için konsol günlüğünü kontrol edin.",
|
"check_console_details": "Ayrıntılar için konsol günlüğünü kontrol edin.",
|
||||||
"curl_invalid_format": "cURL düzgün biçimlendirilmemiş",
|
"curl_invalid_format": "cURL düzgün biçimlendirilmemiş",
|
||||||
"danger_zone": "Danger zone",
|
|
||||||
"delete_account": "Your account is currently an owner in these teams:",
|
|
||||||
"delete_account_description": "You must either remove yourself, transfer ownership, or delete these teams before you can delete your account.",
|
|
||||||
"empty_req_name": "Boş İstek Adı",
|
"empty_req_name": "Boş İstek Adı",
|
||||||
"f12_details": "(Ayrıntılar için F12)",
|
"f12_details": "(Ayrıntılar için F12)",
|
||||||
"gql_prettify_invalid_query": "Geçersiz bir sorgu güzelleştirilemedi, sorgu sözdizimi hatalarını çözüp tekrar deneyin",
|
"gql_prettify_invalid_query": "Geçersiz bir sorgu güzelleştirilemedi, sorgu sözdizimi hatalarını çözüp tekrar deneyin",
|
||||||
@@ -440,7 +437,6 @@
|
|||||||
"settings": {
|
"settings": {
|
||||||
"accent_color": "Vurgu rengi",
|
"accent_color": "Vurgu rengi",
|
||||||
"account": "Hesap",
|
"account": "Hesap",
|
||||||
"account_deleted": "Your account has been deleted",
|
|
||||||
"account_description": "Hesap ayarlarınızı özelleştirin.",
|
"account_description": "Hesap ayarlarınızı özelleştirin.",
|
||||||
"account_email_description": "Birincil e-posta adresiniz.",
|
"account_email_description": "Birincil e-posta adresiniz.",
|
||||||
"account_name_description": "Bu sizin görünen adınız.",
|
"account_name_description": "Bu sizin görünen adınız.",
|
||||||
@@ -449,8 +445,6 @@
|
|||||||
"change_font_size": "Yazı tipi boyutunu değiştir",
|
"change_font_size": "Yazı tipi boyutunu değiştir",
|
||||||
"choose_language": "Dil seçiniz",
|
"choose_language": "Dil seçiniz",
|
||||||
"dark_mode": "Karanlık",
|
"dark_mode": "Karanlık",
|
||||||
"delete_account": "Delete account",
|
|
||||||
"delete_account_description": "Once you delete your account, all your data will be permanently deleted. This action cannot be undone.",
|
|
||||||
"expand_navigation": "Gezinmeyi genişlet",
|
"expand_navigation": "Gezinmeyi genişlet",
|
||||||
"experiments": "Deneyler",
|
"experiments": "Deneyler",
|
||||||
"experiments_notice": "Bu, üzerinde çalıştığımız, yararlı, eğlenceli, her ikisi de ya da hiçbiri olabilecek bir deneyler koleksiyonudur. Nihai değiller ve istikrarlı olmayabilirler, bu yüzden aşırı garip bir şey olursa panik yapmayın.",
|
"experiments_notice": "Bu, üzerinde çalıştığımız, yararlı, eğlenceli, her ikisi de ya da hiçbiri olabilecek bir deneyler koleksiyonudur. Nihai değiller ve istikrarlı olmayabilirler, bu yüzden aşırı garip bir şey olursa panik yapmayın.",
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
"edit": "編輯",
|
"edit": "編輯",
|
||||||
"filter": "篩選回應",
|
"filter": "篩選回應",
|
||||||
"go_back": "返回",
|
"go_back": "返回",
|
||||||
"group_by": "分組方式",
|
"group_by": "Group by",
|
||||||
"label": "標籤",
|
"label": "標籤",
|
||||||
"learn_more": "瞭解更多",
|
"learn_more": "瞭解更多",
|
||||||
"less": "更少",
|
"less": "更少",
|
||||||
@@ -175,7 +175,7 @@
|
|||||||
"protocols": "協定為空",
|
"protocols": "協定為空",
|
||||||
"schema": "連線至 GraphQL 端點",
|
"schema": "連線至 GraphQL 端點",
|
||||||
"shortcodes": "Shortcodes 為空",
|
"shortcodes": "Shortcodes 為空",
|
||||||
"subscription": "訂閱為空",
|
"subscription": "Subscriptions are empty",
|
||||||
"team_name": "團隊名稱為空",
|
"team_name": "團隊名稱為空",
|
||||||
"teams": "團隊為空",
|
"teams": "團隊為空",
|
||||||
"tests": "沒有針對該請求的測試"
|
"tests": "沒有針對該請求的測試"
|
||||||
@@ -203,9 +203,6 @@
|
|||||||
"browser_support_sse": "此瀏覽器似乎不支援 SSE。",
|
"browser_support_sse": "此瀏覽器似乎不支援 SSE。",
|
||||||
"check_console_details": "檢查控制台日誌以獲悉詳情",
|
"check_console_details": "檢查控制台日誌以獲悉詳情",
|
||||||
"curl_invalid_format": "cURL 格式不正確",
|
"curl_invalid_format": "cURL 格式不正確",
|
||||||
"danger_zone": "Danger zone",
|
|
||||||
"delete_account": "您的帳號目前為這些團隊的擁有者:",
|
|
||||||
"delete_account_description": "您在刪除帳號前必須先將您自己從團隊中移除、轉移擁有權,或是刪除團隊。",
|
|
||||||
"empty_req_name": "空請求名稱",
|
"empty_req_name": "空請求名稱",
|
||||||
"f12_details": "(按下 F12 以獲悉詳情)",
|
"f12_details": "(按下 F12 以獲悉詳情)",
|
||||||
"gql_prettify_invalid_query": "無法美化無效的查詢,處理查詢語法錯誤並重試",
|
"gql_prettify_invalid_query": "無法美化無效的查詢,處理查詢語法錯誤並重試",
|
||||||
@@ -232,9 +229,9 @@
|
|||||||
"title": "匯出"
|
"title": "匯出"
|
||||||
},
|
},
|
||||||
"filter": {
|
"filter": {
|
||||||
"all": "全部",
|
"all": "All",
|
||||||
"none": "無",
|
"none": "None",
|
||||||
"starred": "已加星號"
|
"starred": "Starred"
|
||||||
},
|
},
|
||||||
"folder": {
|
"folder": {
|
||||||
"created": "已建立資料夾",
|
"created": "已建立資料夾",
|
||||||
@@ -250,8 +247,8 @@
|
|||||||
"subscriptions": "訂閱"
|
"subscriptions": "訂閱"
|
||||||
},
|
},
|
||||||
"group": {
|
"group": {
|
||||||
"time": "時間",
|
"time": "Time",
|
||||||
"url": "網址"
|
"url": "URL"
|
||||||
},
|
},
|
||||||
"header": {
|
"header": {
|
||||||
"install_pwa": "安裝應用程式",
|
"install_pwa": "安裝應用程式",
|
||||||
@@ -316,16 +313,16 @@
|
|||||||
"import_export": "匯入/匯出"
|
"import_export": "匯入/匯出"
|
||||||
},
|
},
|
||||||
"mqtt": {
|
"mqtt": {
|
||||||
"already_subscribed": "您已經訂閱了此主題。",
|
"already_subscribed": "You are already subscribed to this topic.",
|
||||||
"clean_session": "清理工作階段",
|
"clean_session": "Clean Session",
|
||||||
"clear_input": "清除輸入",
|
"clear_input": "Clear input",
|
||||||
"clear_input_on_send": "傳送後清除輸入",
|
"clear_input_on_send": "Clear input on send",
|
||||||
"client_id": "客戶端 ID",
|
"client_id": "Client ID",
|
||||||
"color": "選擇顏色",
|
"color": "Pick a color",
|
||||||
"communication": "通訊",
|
"communication": "通訊",
|
||||||
"connection_config": "連線設定",
|
"connection_config": "Connection Config",
|
||||||
"connection_not_authorized": "此 MQTT 連線未使用任何驗證。",
|
"connection_not_authorized": "This MQTT connection does not use any authentication.",
|
||||||
"invalid_topic": "請提供該訂閱的主題",
|
"invalid_topic": "Please provide a topic for the subscription",
|
||||||
"keep_alive": "Keep Alive",
|
"keep_alive": "Keep Alive",
|
||||||
"log": "日誌",
|
"log": "日誌",
|
||||||
"lw_message": "Last-Will Message",
|
"lw_message": "Last-Will Message",
|
||||||
@@ -333,8 +330,8 @@
|
|||||||
"lw_retain": "Last-Will Retain",
|
"lw_retain": "Last-Will Retain",
|
||||||
"lw_topic": "Last-Will Topic",
|
"lw_topic": "Last-Will Topic",
|
||||||
"message": "訊息",
|
"message": "訊息",
|
||||||
"new": "新訂閱",
|
"new": "New Subscription",
|
||||||
"not_connected": "請先啟動 MQTT 連線。",
|
"not_connected": "Please start a MQTT connection first.",
|
||||||
"publish": "發佈",
|
"publish": "發佈",
|
||||||
"qos": "QoS",
|
"qos": "QoS",
|
||||||
"ssl": "SSL",
|
"ssl": "SSL",
|
||||||
@@ -440,7 +437,6 @@
|
|||||||
"settings": {
|
"settings": {
|
||||||
"accent_color": "強調色",
|
"accent_color": "強調色",
|
||||||
"account": "帳號",
|
"account": "帳號",
|
||||||
"account_deleted": "已刪除您的帳號",
|
|
||||||
"account_description": "自定義您的帳號設定。",
|
"account_description": "自定義您的帳號設定。",
|
||||||
"account_email_description": "您的主要電子郵件地址。",
|
"account_email_description": "您的主要電子郵件地址。",
|
||||||
"account_name_description": "這是您的顯示名稱。",
|
"account_name_description": "這是您的顯示名稱。",
|
||||||
@@ -449,8 +445,6 @@
|
|||||||
"change_font_size": "更改字型大小",
|
"change_font_size": "更改字型大小",
|
||||||
"choose_language": "選擇語言",
|
"choose_language": "選擇語言",
|
||||||
"dark_mode": "暗色",
|
"dark_mode": "暗色",
|
||||||
"delete_account": "刪除帳號",
|
|
||||||
"delete_account_description": "一旦您刪除了您的帳號,您的所有資料將被永久刪除。此操作無法復原。",
|
|
||||||
"expand_navigation": "展開導航欄",
|
"expand_navigation": "展開導航欄",
|
||||||
"experiments": "實驗功能",
|
"experiments": "實驗功能",
|
||||||
"experiments_notice": "下面是我們正在開發中的一些實驗功能,這些功能可能會很有用,可能很有趣,又或者二者都是或都不是。這些功能並非最終版本且可能不穩定,所以如果發生了一些過於奇怪的事情,不要驚慌,關掉它們就好了。玩笑歸玩笑,",
|
"experiments_notice": "下面是我們正在開發中的一些實驗功能,這些功能可能會很有用,可能很有趣,又或者二者都是或都不是。這些功能並非最終版本且可能不穩定,所以如果發生了一些過於奇怪的事情,不要驚慌,關掉它們就好了。玩笑歸玩笑,",
|
||||||
|
|||||||
@@ -3,42 +3,42 @@
|
|||||||
"autoscroll": "Автопрокручування",
|
"autoscroll": "Автопрокручування",
|
||||||
"cancel": "Скасувати",
|
"cancel": "Скасувати",
|
||||||
"choose_file": "Виберіть файл",
|
"choose_file": "Виберіть файл",
|
||||||
"clear": "Очистити",
|
"clear": "Ясно",
|
||||||
"clear_all": "Очистити все",
|
"clear_all": "Очистити все",
|
||||||
"close": "Закрити",
|
"close": "Закрити",
|
||||||
"connect": "Підключитись",
|
"connect": "Підключіться",
|
||||||
"connecting": "Підключення",
|
"connecting": "Connecting",
|
||||||
"copy": "Копіювати",
|
"copy": "Копіювати",
|
||||||
"delete": "Видалити",
|
"delete": "Видалити",
|
||||||
"disconnect": "Відключитись",
|
"disconnect": "Відключити",
|
||||||
"dismiss": "Відхилити",
|
"dismiss": "Відхилити",
|
||||||
"dont_save": "Не зберігати",
|
"dont_save": "Не зберігати",
|
||||||
"download_file": "Завантажити файл",
|
"download_file": "Завантажити файл",
|
||||||
"drag_to_reorder": "Перетягніть для зміни порядку",
|
"drag_to_reorder": "Перетягніть для зміни порядку",
|
||||||
"duplicate": "Дублювати",
|
"duplicate": "Дублювати",
|
||||||
"edit": "Редагувати",
|
"edit": "Редагувати",
|
||||||
"filter": "Фільтрувати відповіді",
|
"filter": "Фільтр відповіді",
|
||||||
"go_back": "Повернутись",
|
"go_back": "Повертайся",
|
||||||
"group_by": "Групувати за",
|
"group_by": "Group by",
|
||||||
"label": "Мітка",
|
"label": "Мітка",
|
||||||
"learn_more": "Дізнатись більше",
|
"learn_more": "Вчи більше",
|
||||||
"less": "Менше",
|
"less": "Менше",
|
||||||
"more": "Більше",
|
"more": "Більше",
|
||||||
"new": "Новий",
|
"new": "Новий",
|
||||||
"no": "Ні",
|
"no": "Немає",
|
||||||
"open_workspace": "Відкрити робочу область",
|
"open_workspace": "Відкрити робочу область",
|
||||||
"paste": "Вставити",
|
"paste": "Вставити",
|
||||||
"prettify": "Форматувати",
|
"prettify": "Прикрасьте",
|
||||||
"remove": "Видалити",
|
"remove": "Видалити",
|
||||||
"restore": "Відновити",
|
"restore": "Відновлювати",
|
||||||
"save": "Зберегти",
|
"save": "Зберегти",
|
||||||
"scroll_to_bottom": "Прокрутити вниз",
|
"scroll_to_bottom": "Прокрутити вниз",
|
||||||
"scroll_to_top": "Прокрутити вгору",
|
"scroll_to_top": "Прокрутити вгору",
|
||||||
"search": "Пошук",
|
"search": "Пошук",
|
||||||
"send": "Надіслати",
|
"send": "Надіслати",
|
||||||
"start": "Почати",
|
"start": "Почати",
|
||||||
"starting": "Розпочинається",
|
"starting": "Starting",
|
||||||
"stop": "Зупити",
|
"stop": "Стій",
|
||||||
"to_close": "щоб закрити",
|
"to_close": "щоб закрити",
|
||||||
"to_navigate": "для навігації",
|
"to_navigate": "для навігації",
|
||||||
"to_select": "щоб вибрати",
|
"to_select": "щоб вибрати",
|
||||||
@@ -52,7 +52,7 @@
|
|||||||
"star": "Додати зірочку"
|
"star": "Додати зірочку"
|
||||||
},
|
},
|
||||||
"app": {
|
"app": {
|
||||||
"chat_with_us": "Написати нам",
|
"chat_with_us": "Спілкуйтеся з нами",
|
||||||
"contact_us": "Зв'яжіться з нами",
|
"contact_us": "Зв'яжіться з нами",
|
||||||
"copy": "Копіювати",
|
"copy": "Копіювати",
|
||||||
"copy_user_id": "Скопіювати токен автентифікації користувача",
|
"copy_user_id": "Скопіювати токен автентифікації користувача",
|
||||||
@@ -66,10 +66,10 @@
|
|||||||
"invite": "Запросити",
|
"invite": "Запросити",
|
||||||
"invite_description": "У Hoppscotch ми розробили простий та інтуїтивно зрозумілий інтерфейс для створення та управління вашими API. Hoppscotch - це інструмент, який допомагає вам створювати, тестувати, документувати та ділитися своїми API.",
|
"invite_description": "У Hoppscotch ми розробили простий та інтуїтивно зрозумілий інтерфейс для створення та управління вашими API. Hoppscotch - це інструмент, який допомагає вам створювати, тестувати, документувати та ділитися своїми API.",
|
||||||
"invite_your_friends": "Запросіть своїх друзів",
|
"invite_your_friends": "Запросіть своїх друзів",
|
||||||
"join_discord_community": "Приєднуйтесь до нашої спільноти у Discord",
|
"join_discord_community": "Приєднуйтесь до нашої спільноти Discord",
|
||||||
"keyboard_shortcuts": "Гарячі клавіши",
|
"keyboard_shortcuts": "Гарячі клавіши",
|
||||||
"name": "Hoppscotch",
|
"name": "Гопскотч",
|
||||||
"new_version_found": "Знайдено нову версію. Перезавантажте сторінку, щоб оновити.",
|
"new_version_found": "Знайдено нову версію. Оновіть, щоб оновити.",
|
||||||
"options": "Опції",
|
"options": "Опції",
|
||||||
"proxy_privacy_policy": "Політика конфіденційності проксі",
|
"proxy_privacy_policy": "Політика конфіденційності проксі",
|
||||||
"reload": "Перезавантажити",
|
"reload": "Перезавантажити",
|
||||||
@@ -84,17 +84,17 @@
|
|||||||
"type_a_command_search": "Введіть команду або виконайте пошук…",
|
"type_a_command_search": "Введіть команду або виконайте пошук…",
|
||||||
"we_use_cookies": "Ми використовуємо файли cookie",
|
"we_use_cookies": "Ми використовуємо файли cookie",
|
||||||
"whats_new": "Що нового?",
|
"whats_new": "Що нового?",
|
||||||
"wiki": "Wiki"
|
"wiki": "Вікі"
|
||||||
},
|
},
|
||||||
"auth": {
|
"auth": {
|
||||||
"account_exists": "Обліковий запис існує з різними обліковими даними - увійдіть, щоб зв'язати обидва облікові записи",
|
"account_exists": "Обліковий запис існує з різними обліковими даними - увійдіть, щоб зв'язати обидва облікові записи",
|
||||||
"all_sign_in_options": "Усі параметри входу",
|
"all_sign_in_options": "Усі параметри входу",
|
||||||
"continue_with_email": "Продовжити з електронною поштою",
|
"continue_with_email": "Продовжити з електронною поштою",
|
||||||
"continue_with_github": "Продовжити з GitHub",
|
"continue_with_github": "Продовжити з GitHub",
|
||||||
"continue_with_google": "Продовжити з Google",
|
"continue_with_google": "Продовжуйте працювати з Google",
|
||||||
"continue_with_microsoft": "Продовжити з Microsoft",
|
"continue_with_microsoft": "Продовжити з Microsoft",
|
||||||
"email": "Електронна пошта",
|
"email": "Електронна пошта",
|
||||||
"logged_out": "Ви вийшли з облікового запису",
|
"logged_out": "Вийшли з системи",
|
||||||
"login": "Увійти",
|
"login": "Увійти",
|
||||||
"login_success": "Вхід успішно здійснено",
|
"login_success": "Вхід успішно здійснено",
|
||||||
"login_to_hoppscotch": "Увійдіть до Hoppscotch",
|
"login_to_hoppscotch": "Увійдіть до Hoppscotch",
|
||||||
@@ -102,7 +102,7 @@
|
|||||||
"re_enter_email": "Введіть електронну адресу ще раз",
|
"re_enter_email": "Введіть електронну адресу ще раз",
|
||||||
"send_magic_link": "Надішліть чарівне посилання",
|
"send_magic_link": "Надішліть чарівне посилання",
|
||||||
"sync": "Синхронізувати",
|
"sync": "Синхронізувати",
|
||||||
"we_sent_magic_link": "Ми надіслали Вам чарівне посилання!",
|
"we_sent_magic_link": "Ми надіслали вам чарівне посилання!",
|
||||||
"we_sent_magic_link_description": "Перевірте свою поштову скриньку - ми надіслали електронний лист на адресу {email}. Він містить чарівне посилання, яке дозволить вам увійти."
|
"we_sent_magic_link_description": "Перевірте свою поштову скриньку - ми надіслали електронний лист на адресу {email}. Він містить чарівне посилання, яке дозволить вам увійти."
|
||||||
},
|
},
|
||||||
"authorization": {
|
"authorization": {
|
||||||
@@ -154,7 +154,7 @@
|
|||||||
},
|
},
|
||||||
"documentation": {
|
"documentation": {
|
||||||
"generate": "Створення документації",
|
"generate": "Створення документації",
|
||||||
"generate_message": "Імпортуйте будь-яку колекцію Hoppscotch для автоматичного створення API-документації на ходу."
|
"generate_message": "Імпортуйте будь-яку колекцію Hoppscotch для створення документації API на ходу."
|
||||||
},
|
},
|
||||||
"empty": {
|
"empty": {
|
||||||
"authorization": "У цьому запиті не використовується авторизація",
|
"authorization": "У цьому запиті не використовується авторизація",
|
||||||
@@ -175,15 +175,15 @@
|
|||||||
"protocols": "Протоколи порожні",
|
"protocols": "Протоколи порожні",
|
||||||
"schema": "Підключіться до кінцевої точки GraphQL",
|
"schema": "Підключіться до кінцевої точки GraphQL",
|
||||||
"shortcodes": "Короткі коди порожні",
|
"shortcodes": "Короткі коди порожні",
|
||||||
"subscription": "Підписки порожні",
|
"subscription": "Subscriptions are empty",
|
||||||
"team_name": "Назва команди порожня",
|
"team_name": "Назва команди порожня",
|
||||||
"teams": "Команди порожні",
|
"teams": "Команди порожні",
|
||||||
"tests": "Для цього запиту немає тестів"
|
"tests": "Для цього запиту немає тестів"
|
||||||
},
|
},
|
||||||
"environment": {
|
"environment": {
|
||||||
"add_to_global": "Додати до Глобального середовища",
|
"add_to_global": "Додати до Глобального",
|
||||||
"added": "Додавання середовища",
|
"added": "Додавання середовища",
|
||||||
"create_new": "Створити нове середовище",
|
"create_new": "Створіть нове середовище",
|
||||||
"created": "Середовище створено",
|
"created": "Середовище створено",
|
||||||
"deleted": "Видалення середовища",
|
"deleted": "Видалення середовища",
|
||||||
"edit": "Редагувати середовище",
|
"edit": "Редагувати середовище",
|
||||||
@@ -196,16 +196,13 @@
|
|||||||
"select": "Виберіть середовище",
|
"select": "Виберіть середовище",
|
||||||
"team_environments": "Командні середовища",
|
"team_environments": "Командні середовища",
|
||||||
"title": "Середовища",
|
"title": "Середовища",
|
||||||
"updated": "Оновлення середовища",
|
"updated": "Environment updation",
|
||||||
"variable_list": "Список змінних"
|
"variable_list": "Список змінних"
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"browser_support_sse": "Схоже, цей браузер не підтримує події, надіслані сервером.",
|
"browser_support_sse": "Схоже, цей браузер не підтримує події, надіслані сервером.",
|
||||||
"check_console_details": "Детальніше перевірте журнал консолі.",
|
"check_console_details": "Детальніше перевірте журнал консолі.",
|
||||||
"curl_invalid_format": "cURL неправильно форматовано",
|
"curl_invalid_format": "cURL неправильно форматовано",
|
||||||
"danger_zone": "Небезпечна зона",
|
|
||||||
"delete_account": "Ваш обліковий запис на разі володіє цими командами:",
|
|
||||||
"delete_account_description": "Ви повинні або видалити себе, або передати право власності, або видалити ці команди, перш ніж ви зможете видалити свій обліковий запис.",
|
|
||||||
"empty_req_name": "Пуста назва запиту",
|
"empty_req_name": "Пуста назва запиту",
|
||||||
"f12_details": "(F12 для деталей)",
|
"f12_details": "(F12 для деталей)",
|
||||||
"gql_prettify_invalid_query": "Не вдалося попередньо визначити недійсний запит, вирішити синтаксичні помилки запиту та повторити спробу",
|
"gql_prettify_invalid_query": "Не вдалося попередньо визначити недійсний запит, вирішити синтаксичні помилки запиту та повторити спробу",
|
||||||
@@ -226,15 +223,15 @@
|
|||||||
},
|
},
|
||||||
"export": {
|
"export": {
|
||||||
"as_json": "Експортувати як JSON",
|
"as_json": "Експортувати як JSON",
|
||||||
"create_secret_gist": "Створити секретний GitHub Gist",
|
"create_secret_gist": "Створіть секретну суть",
|
||||||
"gist_created": "Gist створений",
|
"gist_created": "Суть створена",
|
||||||
"require_github": "Увійдіть за допомогою GitHub, щоб створити секретний Gist",
|
"require_github": "Увійдіть за допомогою GitHub, щоб створити секретну історію",
|
||||||
"title": "Експортувати"
|
"title": "Експорт"
|
||||||
},
|
},
|
||||||
"filter": {
|
"filter": {
|
||||||
"all": "Всі",
|
"all": "All",
|
||||||
"none": "Жодного",
|
"none": "None",
|
||||||
"starred": "З Зірочкою"
|
"starred": "Starred"
|
||||||
},
|
},
|
||||||
"folder": {
|
"folder": {
|
||||||
"created": "Папка створена",
|
"created": "Папка створена",
|
||||||
@@ -250,7 +247,7 @@
|
|||||||
"subscriptions": "Підписки"
|
"subscriptions": "Підписки"
|
||||||
},
|
},
|
||||||
"group": {
|
"group": {
|
||||||
"time": "Час",
|
"time": "Time",
|
||||||
"url": "URL"
|
"url": "URL"
|
||||||
},
|
},
|
||||||
"header": {
|
"header": {
|
||||||
@@ -260,19 +257,19 @@
|
|||||||
},
|
},
|
||||||
"helpers": {
|
"helpers": {
|
||||||
"authorization": "Заголовок авторизації буде автоматично сформований під час надсилання запиту.",
|
"authorization": "Заголовок авторизації буде автоматично сформований під час надсилання запиту.",
|
||||||
"generate_documentation_first": "Спочатку згенеруйте документацію",
|
"generate_documentation_first": "Спочатку сформуйте документацію",
|
||||||
"network_fail": "Не вдається зв'язатися з кінцевою точкою API. Перевірте підключення до мережі та повторіть спробу.",
|
"network_fail": "Не вдається зв'язатися з кінцевою точкою API. Перевірте підключення до мережі та повторіть спробу.",
|
||||||
"offline": "Ви, здається, не в мережі. Дані в цій робочій області можуть бути не актуальними.",
|
"offline": "Ви, здається, не в мережі. Дані в цій робочій області можуть бути не актуальними.",
|
||||||
"offline_short": "Ви, здається, не в мережі.",
|
"offline_short": "Ви, здається, не в мережі.",
|
||||||
"post_request_tests": "Тестові скрипти записуються на JavaScript і запускаються після отримання відповіді.",
|
"post_request_tests": "Тестові сценарії записуються на JavaScript і запускаються після отримання відповіді.",
|
||||||
"pre_request_script": "Скрипти написані на JavaScript і запускаються перед надсиланням запиту.",
|
"pre_request_script": "Сценарії попереднього запиту написані на JavaScript і запускаються перед надсиланням запиту.",
|
||||||
"script_fail": "Схоже, є збій у скрипті. Перевірте помилку нижче та виправте відповідним чином сценарій.",
|
"script_fail": "Схоже, є збій у сценарії попереднього запиту. Перевірте помилку нижче та виправте відповідним чином сценарій.",
|
||||||
"test_script_fail": "Здається виникла помилка з тестовим скриптом. Будь ласка, виправте помилки і спробуйте знову",
|
"test_script_fail": "Здається виникла помилка з тестовим сценарієм. Будь ласка, виправте помилки і спробуйте знову",
|
||||||
"tests": "Напишіть тестовий скрипт для автоматизації налагодження."
|
"tests": "Напишіть тестовий сценарій для автоматизації налагодження."
|
||||||
},
|
},
|
||||||
"hide": {
|
"hide": {
|
||||||
"collection": "Згорнути панель колекції",
|
"collection": "Згорнути панель колекції",
|
||||||
"more": "Приховати більше",
|
"more": "Приховай більше",
|
||||||
"preview": "Приховати попередній перегляд",
|
"preview": "Приховати попередній перегляд",
|
||||||
"sidebar": "Приховати бічну панель"
|
"sidebar": "Приховати бічну панель"
|
||||||
},
|
},
|
||||||
@@ -286,20 +283,20 @@
|
|||||||
"from_insomnia_description": "Імпорт із колекції Insomnia",
|
"from_insomnia_description": "Імпорт із колекції Insomnia",
|
||||||
"from_json": "Імпорт з Hoppscotch",
|
"from_json": "Імпорт з Hoppscotch",
|
||||||
"from_json_description": "Імпорт з файлу колекції Hoppscotch",
|
"from_json_description": "Імпорт з файлу колекції Hoppscotch",
|
||||||
"from_my_collections": "Імпортувати з моїх колекцій",
|
"from_my_collections": "Імпортувати з Моїх колекцій",
|
||||||
"from_my_collections_description": "Імпортувати з мого файлу колекцій",
|
"from_my_collections_description": "Імпортувати з мого файлу колекцій",
|
||||||
"from_openapi": "Імпорт з OpenAPI",
|
"from_openapi": "Імпорт з OpenAPI",
|
||||||
"from_openapi_description": "Імпорт з файлу специфікації OpenAPI (YML/JSON)",
|
"from_openapi_description": "Імпорт з файлу специфікації OpenAPI (YML/JSON)",
|
||||||
"from_postman": "Імпортувати з Postman",
|
"from_postman": "Імпортувати з Postman",
|
||||||
"from_postman_description": "Імпорт із колекції Postman",
|
"from_postman_description": "Імпорт із колекції Postman",
|
||||||
"from_url": "Імпорт з URL",
|
"from_url": "Імпорт з URL",
|
||||||
"gist_url": "Введіть URL-адресу Gist",
|
"gist_url": "Введіть URL -адресу Gist",
|
||||||
"import_from_url_invalid_fetch": "Не вдалося отримати дані з url",
|
"import_from_url_invalid_fetch": "Не вдалося отримати дані з url",
|
||||||
"import_from_url_invalid_file_format": "Помилка при імпорті колекцій",
|
"import_from_url_invalid_file_format": "Помилка при імпорті колекцій",
|
||||||
"import_from_url_invalid_type": "Непідтримуваний тип. Допустимими значеннями є 'hoppscotch', 'openapi', 'postman', 'insomnia'",
|
"import_from_url_invalid_type": "Непідтримуваний тип. Допустимими значеннями є 'hoppscotch', 'openapi', 'postman', 'insomnia'",
|
||||||
"import_from_url_success": "Колекції імпортовано",
|
"import_from_url_success": "Колекції імпортовано",
|
||||||
"json_description": "Імпортувати колекції з колекцій Hoppscotch JSON файлу",
|
"json_description": "Імпортувати колекції з колекцій Hoppscotch JSON файлу",
|
||||||
"title": "Імпортувати"
|
"title": "Імпорт"
|
||||||
},
|
},
|
||||||
"layout": {
|
"layout": {
|
||||||
"collapse_collection": "Згорнути або розширити колекції",
|
"collapse_collection": "Згорнути або розширити колекції",
|
||||||
@@ -316,16 +313,16 @@
|
|||||||
"import_export": "Імпорт-експорт"
|
"import_export": "Імпорт-експорт"
|
||||||
},
|
},
|
||||||
"mqtt": {
|
"mqtt": {
|
||||||
"already_subscribed": "Ви вже підписані на цю тему.",
|
"already_subscribed": "You are already subscribed to this topic.",
|
||||||
"clean_session": "Очистити сесію.",
|
"clean_session": "Clean Session",
|
||||||
"clear_input": "Очистити вхідні дані",
|
"clear_input": "Clear input",
|
||||||
"clear_input_on_send": "Очистити вхідні дані після надсилання",
|
"clear_input_on_send": "Clear input on send",
|
||||||
"client_id": "ID Клієнта",
|
"client_id": "Client ID",
|
||||||
"color": "Оберіть колір",
|
"color": "Pick a color",
|
||||||
"communication": "Комунікації",
|
"communication": "Спілкування",
|
||||||
"connection_config": "Налаштування підключення",
|
"connection_config": "Connection Config",
|
||||||
"connection_not_authorized": "Це MQTT-з'єднання не використовує автентифікацію.",
|
"connection_not_authorized": "This MQTT connection does not use any authentication.",
|
||||||
"invalid_topic": "Будь ласка, вкажіть тему для підписки",
|
"invalid_topic": "Please provide a topic for the subscription",
|
||||||
"keep_alive": "Keep Alive",
|
"keep_alive": "Keep Alive",
|
||||||
"log": "Журнал",
|
"log": "Журнал",
|
||||||
"lw_message": "Last-Will Message",
|
"lw_message": "Last-Will Message",
|
||||||
@@ -333,12 +330,12 @@
|
|||||||
"lw_retain": "Last-Will Retain",
|
"lw_retain": "Last-Will Retain",
|
||||||
"lw_topic": "Last-Will Topic",
|
"lw_topic": "Last-Will Topic",
|
||||||
"message": "повідомлення",
|
"message": "повідомлення",
|
||||||
"new": "Нова Підписка",
|
"new": "New Subscription",
|
||||||
"not_connected": "Будь ласка, спочатку створіть з'єднання MQTT.",
|
"not_connected": "Please start a MQTT connection first.",
|
||||||
"publish": "Опублікувати",
|
"publish": "Публікуйте",
|
||||||
"qos": "QoS",
|
"qos": "QoS",
|
||||||
"ssl": "SSL",
|
"ssl": "SSL",
|
||||||
"subscribe": "Підписатись",
|
"subscribe": "Підпишіться",
|
||||||
"topic": "Тема",
|
"topic": "Тема",
|
||||||
"topic_name": "Назва теми",
|
"topic_name": "Назва теми",
|
||||||
"topic_title": "Опублікувати / підписатися на тему",
|
"topic_title": "Опублікувати / підписатися на тему",
|
||||||
@@ -355,9 +352,9 @@
|
|||||||
},
|
},
|
||||||
"preRequest": {
|
"preRequest": {
|
||||||
"javascript_code": "Код JavaScript",
|
"javascript_code": "Код JavaScript",
|
||||||
"learn": "Прочитати документацію",
|
"learn": "Прочитайте документацію",
|
||||||
"script": "Сценарій попереднього запиту",
|
"script": "Сценарій попереднього запиту",
|
||||||
"snippets": "Сніпети"
|
"snippets": "Фрагменти"
|
||||||
},
|
},
|
||||||
"profile": {
|
"profile": {
|
||||||
"app_settings": "Параметри програми",
|
"app_settings": "Параметри програми",
|
||||||
@@ -439,18 +436,15 @@
|
|||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"accent_color": "Колір акценту",
|
"accent_color": "Колір акценту",
|
||||||
"account": "Обліковий запис",
|
"account": "Рахунок",
|
||||||
"account_deleted": "Ваш обліковий запис успішно видалено.",
|
|
||||||
"account_description": "Налаштуйте налаштування свого облікового запису.",
|
"account_description": "Налаштуйте налаштування свого облікового запису.",
|
||||||
"account_email_description": "Ваша основна електронна адреса.",
|
"account_email_description": "Ваша основна електронна адреса.",
|
||||||
"account_name_description": "Це ваше відображуване ім'я.",
|
"account_name_description": "Це ваше відображуване ім'я.",
|
||||||
"background": "Колір фону",
|
"background": "Довідка",
|
||||||
"black_mode": "Чорний",
|
"black_mode": "Чорний",
|
||||||
"change_font_size": "Змінити розмір шрифту",
|
"change_font_size": "Змінити розмір шрифту",
|
||||||
"choose_language": "Виберіть мову",
|
"choose_language": "Виберіть мову",
|
||||||
"dark_mode": "Темний",
|
"dark_mode": "Темний",
|
||||||
"delete_account": "Видалити обліковий запис",
|
|
||||||
"delete_account_description": "Якщо Ви видалите обліковий запис, усі дані будуть втрачені без можливості їх відновлення.",
|
|
||||||
"expand_navigation": "Розгорнути навігацію",
|
"expand_navigation": "Розгорнути навігацію",
|
||||||
"experiments": "Експерименти",
|
"experiments": "Експерименти",
|
||||||
"experiments_notice": "Це збірка експериментів, над якими ми працюємо, які можуть виявитися корисними, веселими, обома чи ні. Вони не остаточні і можуть бути нестійкими, тому, якщо трапиться щось надто дивне, не панікуйте. Просто вимкніть небезпеку. Жарти в сторону,",
|
"experiments_notice": "Це збірка експериментів, над якими ми працюємо, які можуть виявитися корисними, веселими, обома чи ні. Вони не остаточні і можуть бути нестійкими, тому, якщо трапиться щось надто дивне, не панікуйте. Просто вимкніть небезпеку. Жарти в сторону,",
|
||||||
@@ -636,7 +630,7 @@
|
|||||||
"queries": "Запити",
|
"queries": "Запити",
|
||||||
"query": "Запит",
|
"query": "Запит",
|
||||||
"schema": "Схема",
|
"schema": "Схема",
|
||||||
"socketio": "Socket.IO",
|
"socketio": "Сокет.IO",
|
||||||
"sse": "SSE",
|
"sse": "SSE",
|
||||||
"tests": "Тести",
|
"tests": "Тести",
|
||||||
"types": "Типи",
|
"types": "Типи",
|
||||||
@@ -698,14 +692,14 @@
|
|||||||
"not_found": "Середовище не знайдено."
|
"not_found": "Середовище не знайдено."
|
||||||
},
|
},
|
||||||
"test": {
|
"test": {
|
||||||
"failed": "Помилка тесту",
|
"failed": "помилка тесту",
|
||||||
"javascript_code": "Код JavaScript",
|
"javascript_code": "Код JavaScript",
|
||||||
"learn": "Прочитайте документацію",
|
"learn": "Прочитайте документацію",
|
||||||
"passed": "Тест пройдено",
|
"passed": "тест пройдено",
|
||||||
"report": "Протокол випробування",
|
"report": "Протокол випробування",
|
||||||
"results": "Результати тесту",
|
"results": "Результати тесту",
|
||||||
"script": "Скрипти",
|
"script": "Сценарій",
|
||||||
"snippets": "Сніпети"
|
"snippets": "Фрагменти"
|
||||||
},
|
},
|
||||||
"websocket": {
|
"websocket": {
|
||||||
"communication": "Спілкування",
|
"communication": "Спілкування",
|
||||||
|
|||||||
@@ -203,9 +203,6 @@
|
|||||||
"browser_support_sse": "Trình duyệt này dường như không có hỗ trợ Sự kiện do Máy chủ gửi.",
|
"browser_support_sse": "Trình duyệt này dường như không có hỗ trợ Sự kiện do Máy chủ gửi.",
|
||||||
"check_console_details": "Kiểm tra nhật ký bảng điều khiển để biết chi tiết.",
|
"check_console_details": "Kiểm tra nhật ký bảng điều khiển để biết chi tiết.",
|
||||||
"curl_invalid_format": "cURL không được định dạng đúng",
|
"curl_invalid_format": "cURL không được định dạng đúng",
|
||||||
"danger_zone": "Danger zone",
|
|
||||||
"delete_account": "Your account is currently an owner in these teams:",
|
|
||||||
"delete_account_description": "You must either remove yourself, transfer ownership, or delete these teams before you can delete your account.",
|
|
||||||
"empty_req_name": "Tên yêu cầu trống",
|
"empty_req_name": "Tên yêu cầu trống",
|
||||||
"f12_details": "(F12 để biết chi tiết)",
|
"f12_details": "(F12 để biết chi tiết)",
|
||||||
"gql_prettify_invalid_query": "Không thể xác minh trước một truy vấn không hợp lệ, hãy giải quyết các lỗi cú pháp truy vấn và thử lại",
|
"gql_prettify_invalid_query": "Không thể xác minh trước một truy vấn không hợp lệ, hãy giải quyết các lỗi cú pháp truy vấn và thử lại",
|
||||||
@@ -440,7 +437,6 @@
|
|||||||
"settings": {
|
"settings": {
|
||||||
"accent_color": "Màu nhấn",
|
"accent_color": "Màu nhấn",
|
||||||
"account": "Tài khoản",
|
"account": "Tài khoản",
|
||||||
"account_deleted": "Your account has been deleted",
|
|
||||||
"account_description": "Tùy chỉnh cài đặt tài khoản của bạn.",
|
"account_description": "Tùy chỉnh cài đặt tài khoản của bạn.",
|
||||||
"account_email_description": "Địa chỉ email chính của bạn.",
|
"account_email_description": "Địa chỉ email chính của bạn.",
|
||||||
"account_name_description": "Đây là tên hiển thị của bạn.",
|
"account_name_description": "Đây là tên hiển thị của bạn.",
|
||||||
@@ -449,8 +445,6 @@
|
|||||||
"change_font_size": "Thay đổi kích thước phông chữ",
|
"change_font_size": "Thay đổi kích thước phông chữ",
|
||||||
"choose_language": "Chọn ngôn ngữ",
|
"choose_language": "Chọn ngôn ngữ",
|
||||||
"dark_mode": "Tối tăm",
|
"dark_mode": "Tối tăm",
|
||||||
"delete_account": "Delete account",
|
|
||||||
"delete_account_description": "Once you delete your account, all your data will be permanently deleted. This action cannot be undone.",
|
|
||||||
"expand_navigation": "Expand navigation",
|
"expand_navigation": "Expand navigation",
|
||||||
"experiments": "Thí nghiệm",
|
"experiments": "Thí nghiệm",
|
||||||
"experiments_notice": "Đây là một bộ sưu tập các thử nghiệm mà chúng tôi đang thực hiện có thể hữu ích, thú vị, cả hai hoặc không. Chúng không phải là cuối cùng và có thể không ổn định, vì vậy nếu có điều gì đó quá kỳ lạ xảy ra, đừng hoảng sợ. Chỉ cần tắt điều này đi. Chuyện cười sang một bên,",
|
"experiments_notice": "Đây là một bộ sưu tập các thử nghiệm mà chúng tôi đang thực hiện có thể hữu ích, thú vị, cả hai hoặc không. Chúng không phải là cuối cùng và có thể không ổn định, vì vậy nếu có điều gì đó quá kỳ lạ xảy ra, đừng hoảng sợ. Chỉ cần tắt điều này đi. Chuyện cười sang một bên,",
|
||||||
|
|||||||
@@ -31,7 +31,6 @@
|
|||||||
"@codemirror/state": "^6.1.0",
|
"@codemirror/state": "^6.1.0",
|
||||||
"@codemirror/view": "^6.0.2",
|
"@codemirror/view": "^6.0.2",
|
||||||
"@hoppscotch/codemirror-lang-graphql": "workspace:^0.2.0",
|
"@hoppscotch/codemirror-lang-graphql": "workspace:^0.2.0",
|
||||||
"@hoppscotch/ui": "workspace:^0.0.1",
|
|
||||||
"@hoppscotch/data": "workspace:^0.4.4",
|
"@hoppscotch/data": "workspace:^0.4.4",
|
||||||
"@hoppscotch/js-sandbox": "workspace:^2.1.0",
|
"@hoppscotch/js-sandbox": "workspace:^2.1.0",
|
||||||
"@hoppscotch/vue-toasted": "^0.1.0",
|
"@hoppscotch/vue-toasted": "^0.1.0",
|
||||||
|
|||||||
64
packages/hoppscotch-common/src/components.d.ts
vendored
64
packages/hoppscotch-common/src/components.d.ts
vendored
@@ -7,7 +7,6 @@ export {}
|
|||||||
|
|
||||||
declare module '@vue/runtime-core' {
|
declare module '@vue/runtime-core' {
|
||||||
export interface GlobalComponents {
|
export interface GlobalComponents {
|
||||||
AppActionHandler: typeof import('./components/app/ActionHandler.vue')['default']
|
|
||||||
AppAnnouncement: typeof import('./components/app/Announcement.vue')['default']
|
AppAnnouncement: typeof import('./components/app/Announcement.vue')['default']
|
||||||
AppDeveloperOptions: typeof import('./components/app/DeveloperOptions.vue')['default']
|
AppDeveloperOptions: typeof import('./components/app/DeveloperOptions.vue')['default']
|
||||||
AppFooter: typeof import('./components/app/Footer.vue')['default']
|
AppFooter: typeof import('./components/app/Footer.vue')['default']
|
||||||
@@ -26,13 +25,13 @@ declare module '@vue/runtime-core' {
|
|||||||
AppShortcutsPrompt: typeof import('./components/app/ShortcutsPrompt.vue')['default']
|
AppShortcutsPrompt: typeof import('./components/app/ShortcutsPrompt.vue')['default']
|
||||||
AppSidenav: typeof import('./components/app/Sidenav.vue')['default']
|
AppSidenav: typeof import('./components/app/Sidenav.vue')['default']
|
||||||
AppSupport: typeof import('./components/app/Support.vue')['default']
|
AppSupport: typeof import('./components/app/Support.vue')['default']
|
||||||
ButtonPrimary: typeof import('./../../hoppscotch-ui/src/components/button/Primary.vue')['default']
|
ButtonPrimary: typeof import('./components/button/Primary.vue')['default']
|
||||||
ButtonSecondary: typeof import('./../../hoppscotch-ui/src/components/button/Secondary.vue')['default']
|
ButtonSecondary: typeof import('./components/button/Secondary.vue')['default']
|
||||||
Collections: typeof import('./components/collections/index.vue')['default']
|
Collections: typeof import('./components/collections/index.vue')['default']
|
||||||
CollectionsAdd: typeof import('./components/collections/Add.vue')['default']
|
CollectionsAdd: typeof import('./components/collections/Add.vue')['default']
|
||||||
CollectionsAddFolder: typeof import('./components/collections/AddFolder.vue')['default']
|
CollectionsAddFolder: typeof import('./components/collections/AddFolder.vue')['default']
|
||||||
CollectionsAddRequest: typeof import('./components/collections/AddRequest.vue')['default']
|
CollectionsAddRequest: typeof import('./components/collections/AddRequest.vue')['default']
|
||||||
CollectionsCollection: typeof import('./components/collections/Collection.vue')['default']
|
CollectionsChooseType: typeof import('./components/collections/ChooseType.vue')['default']
|
||||||
CollectionsEdit: typeof import('./components/collections/Edit.vue')['default']
|
CollectionsEdit: typeof import('./components/collections/Edit.vue')['default']
|
||||||
CollectionsEditFolder: typeof import('./components/collections/EditFolder.vue')['default']
|
CollectionsEditFolder: typeof import('./components/collections/EditFolder.vue')['default']
|
||||||
CollectionsEditRequest: typeof import('./components/collections/EditRequest.vue')['default']
|
CollectionsEditRequest: typeof import('./components/collections/EditRequest.vue')['default']
|
||||||
@@ -48,11 +47,13 @@ declare module '@vue/runtime-core' {
|
|||||||
CollectionsGraphqlImportExport: typeof import('./components/collections/graphql/ImportExport.vue')['default']
|
CollectionsGraphqlImportExport: typeof import('./components/collections/graphql/ImportExport.vue')['default']
|
||||||
CollectionsGraphqlRequest: typeof import('./components/collections/graphql/Request.vue')['default']
|
CollectionsGraphqlRequest: typeof import('./components/collections/graphql/Request.vue')['default']
|
||||||
CollectionsImportExport: typeof import('./components/collections/ImportExport.vue')['default']
|
CollectionsImportExport: typeof import('./components/collections/ImportExport.vue')['default']
|
||||||
CollectionsMyCollections: typeof import('./components/collections/MyCollections.vue')['default']
|
CollectionsMyCollection: typeof import('./components/collections/my/Collection.vue')['default']
|
||||||
CollectionsRequest: typeof import('./components/collections/Request.vue')['default']
|
CollectionsMyFolder: typeof import('./components/collections/my/Folder.vue')['default']
|
||||||
|
CollectionsMyRequest: typeof import('./components/collections/my/Request.vue')['default']
|
||||||
CollectionsSaveRequest: typeof import('./components/collections/SaveRequest.vue')['default']
|
CollectionsSaveRequest: typeof import('./components/collections/SaveRequest.vue')['default']
|
||||||
CollectionsTeamCollections: typeof import('./components/collections/TeamCollections.vue')['default']
|
CollectionsTeamsCollection: typeof import('./components/collections/teams/Collection.vue')['default']
|
||||||
CollectionsTeamSelect: typeof import('./components/collections/TeamSelect.vue')['default']
|
CollectionsTeamsFolder: typeof import('./components/collections/teams/Folder.vue')['default']
|
||||||
|
CollectionsTeamsRequest: typeof import('./components/collections/teams/Request.vue')['default']
|
||||||
Environments: typeof import('./components/environments/index.vue')['default']
|
Environments: typeof import('./components/environments/index.vue')['default']
|
||||||
EnvironmentsChooseType: typeof import('./components/environments/ChooseType.vue')['default']
|
EnvironmentsChooseType: typeof import('./components/environments/ChooseType.vue')['default']
|
||||||
EnvironmentsImportExport: typeof import('./components/environments/ImportExport.vue')['default']
|
EnvironmentsImportExport: typeof import('./components/environments/ImportExport.vue')['default']
|
||||||
@@ -98,6 +99,7 @@ declare module '@vue/runtime-core' {
|
|||||||
HttpTests: typeof import('./components/http/Tests.vue')['default']
|
HttpTests: typeof import('./components/http/Tests.vue')['default']
|
||||||
HttpURLEncodedParams: typeof import('./components/http/URLEncodedParams.vue')['default']
|
HttpURLEncodedParams: typeof import('./components/http/URLEncodedParams.vue')['default']
|
||||||
IconLucideArrowLeft: typeof import('~icons/lucide/arrow-left')['default']
|
IconLucideArrowLeft: typeof import('~icons/lucide/arrow-left')['default']
|
||||||
|
IconLucideBrush: typeof import('~icons/lucide/brush')['default']
|
||||||
IconLucideCheckCircle: typeof import('~icons/lucide/check-circle')['default']
|
IconLucideCheckCircle: typeof import('~icons/lucide/check-circle')['default']
|
||||||
IconLucideChevronRight: typeof import('~icons/lucide/chevron-right')['default']
|
IconLucideChevronRight: typeof import('~icons/lucide/chevron-right')['default']
|
||||||
IconLucideGlobe: typeof import('~icons/lucide/globe')['default']
|
IconLucideGlobe: typeof import('~icons/lucide/globe')['default']
|
||||||
@@ -106,9 +108,11 @@ declare module '@vue/runtime-core' {
|
|||||||
IconLucideLayers: typeof import('~icons/lucide/layers')['default']
|
IconLucideLayers: typeof import('~icons/lucide/layers')['default']
|
||||||
IconLucideLoader: typeof import('~icons/lucide/loader')['default']
|
IconLucideLoader: typeof import('~icons/lucide/loader')['default']
|
||||||
IconLucideMinus: typeof import('~icons/lucide/minus')['default']
|
IconLucideMinus: typeof import('~icons/lucide/minus')['default']
|
||||||
|
IconLucideRss: typeof import('~icons/lucide/rss')['default']
|
||||||
IconLucideSearch: typeof import('~icons/lucide/search')['default']
|
IconLucideSearch: typeof import('~icons/lucide/search')['default']
|
||||||
IconLucideUser: typeof import('~icons/lucide/user')['default']
|
IconLucideUser: typeof import('~icons/lucide/user')['default']
|
||||||
IconLucideUsers: typeof import('~icons/lucide/users')['default']
|
IconLucideUsers: typeof import('~icons/lucide/users')['default']
|
||||||
|
IconLucideVerified: typeof import('~icons/lucide/verified')['default']
|
||||||
LensesHeadersRenderer: typeof import('./components/lenses/HeadersRenderer.vue')['default']
|
LensesHeadersRenderer: typeof import('./components/lenses/HeadersRenderer.vue')['default']
|
||||||
LensesHeadersRendererEntry: typeof import('./components/lenses/HeadersRendererEntry.vue')['default']
|
LensesHeadersRendererEntry: typeof import('./components/lenses/HeadersRendererEntry.vue')['default']
|
||||||
LensesRenderersHTMLLensRenderer: typeof import('./components/lenses/renderers/HTMLLensRenderer.vue')['default']
|
LensesRenderersHTMLLensRenderer: typeof import('./components/lenses/renderers/HTMLLensRenderer.vue')['default']
|
||||||
@@ -120,40 +124,36 @@ declare module '@vue/runtime-core' {
|
|||||||
LensesResponseBodyRenderer: typeof import('./components/lenses/ResponseBodyRenderer.vue')['default']
|
LensesResponseBodyRenderer: typeof import('./components/lenses/ResponseBodyRenderer.vue')['default']
|
||||||
ProfilePicture: typeof import('./components/profile/Picture.vue')['default']
|
ProfilePicture: typeof import('./components/profile/Picture.vue')['default']
|
||||||
ProfileShortcode: typeof import('./components/profile/Shortcode.vue')['default']
|
ProfileShortcode: typeof import('./components/profile/Shortcode.vue')['default']
|
||||||
ProfileShortcodes: typeof import('./components/profile/Shortcodes.vue')['default']
|
|
||||||
ProfileUserDelete: typeof import('./components/profile/UserDelete.vue')['default']
|
|
||||||
RealtimeCommunication: typeof import('./components/realtime/Communication.vue')['default']
|
RealtimeCommunication: typeof import('./components/realtime/Communication.vue')['default']
|
||||||
RealtimeConnectionConfig: typeof import('./components/realtime/ConnectionConfig.vue')['default']
|
RealtimeConnectionConfig: typeof import('./components/realtime/ConnectionConfig.vue')['default']
|
||||||
RealtimeLog: typeof import('./components/realtime/Log.vue')['default']
|
RealtimeLog: typeof import('./components/realtime/Log.vue')['default']
|
||||||
RealtimeLogEntry: typeof import('./components/realtime/LogEntry.vue')['default']
|
RealtimeLogEntry: typeof import('./components/realtime/LogEntry.vue')['default']
|
||||||
RealtimeSubscription: typeof import('./components/realtime/Subscription.vue')['default']
|
RealtimeSubscription: typeof import('./components/realtime/Subscription.vue')['default']
|
||||||
SmartAccentModePicker: typeof import('./components/smart/AccentModePicker.vue')['default']
|
SmartAccentModePicker: typeof import('./components/smart/AccentModePicker.vue')['default']
|
||||||
SmartAnchor: typeof import('./../../hoppscotch-ui/src/components/smart/Anchor.vue')['default']
|
SmartAnchor: typeof import('./components/smart/Anchor.vue')['default']
|
||||||
SmartAutoComplete: typeof import('./../../hoppscotch-ui/src/components/smart/AutoComplete.vue')['default']
|
SmartAutoComplete: typeof import('./components/smart/AutoComplete.vue')['default']
|
||||||
SmartChangeLanguage: typeof import('./components/smart/ChangeLanguage.vue')['default']
|
SmartChangeLanguage: typeof import('./components/smart/ChangeLanguage.vue')['default']
|
||||||
SmartCheckbox: typeof import('./../../hoppscotch-ui/src/components/smart/Checkbox.vue')['default']
|
SmartCheckbox: typeof import('./components/smart/Checkbox.vue')['default']
|
||||||
SmartColorModePicker: typeof import('./components/smart/ColorModePicker.vue')['default']
|
SmartColorModePicker: typeof import('./components/smart/ColorModePicker.vue')['default']
|
||||||
SmartConfirmModal: typeof import('./../../hoppscotch-ui/src/components/smart/ConfirmModal.vue')['default']
|
SmartConfirmModal: typeof import('./components/smart/ConfirmModal.vue')['default']
|
||||||
SmartEnvInput: typeof import('./components/smart/EnvInput.vue')['default']
|
SmartEnvInput: typeof import('./components/smart/EnvInput.vue')['default']
|
||||||
SmartExpand: typeof import('./../../hoppscotch-ui/src/components/smart/Expand.vue')['default']
|
SmartExpand: typeof import('./components/smart/Expand.vue')['default']
|
||||||
SmartFileChip: typeof import('./../../hoppscotch-ui/src/components/smart/FileChip.vue')['default']
|
SmartFileChip: typeof import('./components/smart/FileChip.vue')['default']
|
||||||
SmartFontSizePicker: typeof import('./components/smart/FontSizePicker.vue')['default']
|
SmartFontSizePicker: typeof import('./components/smart/FontSizePicker.vue')['default']
|
||||||
SmartIntersection: typeof import('./../../hoppscotch-ui/src/components/smart/Intersection.vue')['default']
|
SmartIntersection: typeof import('./components/smart/Intersection.vue')['default']
|
||||||
SmartItem: typeof import('./../../hoppscotch-ui/src/components/smart/Item.vue')['default']
|
SmartItem: typeof import('./components/smart/Item.vue')['default']
|
||||||
SmartLink: typeof import('./../../hoppscotch-ui/src/components/smart/Link.vue')['default']
|
SmartLink: typeof import('./components/smart/Link.vue')['default']
|
||||||
SmartModal: typeof import('./../../hoppscotch-ui/src/components/smart/Modal.vue')['default']
|
SmartModal: typeof import('./components/smart/Modal.vue')['default']
|
||||||
SmartProgressRing: typeof import('./../../hoppscotch-ui/src/components/smart/ProgressRing.vue')['default']
|
SmartProgressRing: typeof import('./components/smart/ProgressRing.vue')['default']
|
||||||
SmartRadio: typeof import('./../../hoppscotch-ui/src/components/smart/Radio.vue')['default']
|
SmartRadio: typeof import('./components/smart/Radio.vue')['default']
|
||||||
SmartRadioGroup: typeof import('./../../hoppscotch-ui/src/components/smart/RadioGroup.vue')['default']
|
SmartRadioGroup: typeof import('./components/smart/RadioGroup.vue')['default']
|
||||||
SmartSlideOver: typeof import('./../../hoppscotch-ui/src/components/smart/SlideOver.vue')['default']
|
SmartSlideOver: typeof import('./components/smart/SlideOver.vue')['default']
|
||||||
SmartSpinner: typeof import('./../../hoppscotch-ui/src/components/smart/Spinner.vue')['default']
|
SmartSpinner: typeof import('./components/smart/Spinner.vue')['default']
|
||||||
SmartTab: typeof import('./../../hoppscotch-ui/src/components/smart/Tab.vue')['default']
|
SmartTab: typeof import('./components/smart/Tab.vue')['default']
|
||||||
SmartTabs: typeof import('./../../hoppscotch-ui/src/components/smart/Tabs.vue')['default']
|
SmartTabs: typeof import('./components/smart/Tabs.vue')['default']
|
||||||
SmartToggle: typeof import('./../../hoppscotch-ui/src/components/smart/Toggle.vue')['default']
|
SmartToggle: typeof import('./components/smart/Toggle.vue')['default']
|
||||||
SmartTree: typeof import('./components/smart/Tree.vue')['default']
|
SmartWindow: typeof import('./components/smart/Window.vue')['default']
|
||||||
SmartTreeBranch: typeof import('./components/smart/TreeBranch.vue')['default']
|
SmartWindows: typeof import('./components/smart/Windows.vue')['default']
|
||||||
SmartWindow: typeof import('./../../hoppscotch-ui/src/components/smart/Window.vue')['default']
|
|
||||||
SmartWindows: typeof import('./../../hoppscotch-ui/src/components/smart/Windows.vue')['default']
|
|
||||||
TabPrimary: typeof import('./components/tab/Primary.vue')['default']
|
TabPrimary: typeof import('./components/tab/Primary.vue')['default']
|
||||||
TabSecondary: typeof import('./components/tab/Secondary.vue')['default']
|
TabSecondary: typeof import('./components/tab/Secondary.vue')['default']
|
||||||
Teams: typeof import('./components/teams/index.vue')['default']
|
Teams: typeof import('./components/teams/index.vue')['default']
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
<template>
|
|
||||||
<AppShortcuts :show="showShortcuts" @close="showShortcuts = false" />
|
|
||||||
<AppShare :show="showShare" @hide-modal="showShare = false" />
|
|
||||||
<FirebaseLogin :show="showLogin" @hide-modal="showLogin = false" />
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { ref } from "vue"
|
|
||||||
import { defineActionHandler } from "~/helpers/actions"
|
|
||||||
|
|
||||||
const showShortcuts = ref(false)
|
|
||||||
const showShare = ref(false)
|
|
||||||
const showLogin = ref(false)
|
|
||||||
|
|
||||||
defineActionHandler("flyouts.keybinds.toggle", () => {
|
|
||||||
showShortcuts.value = !showShortcuts.value
|
|
||||||
})
|
|
||||||
|
|
||||||
defineActionHandler("modals.share.toggle", () => {
|
|
||||||
showShare.value = !showShare.value
|
|
||||||
})
|
|
||||||
|
|
||||||
defineActionHandler("modals.login.toggle", () => {
|
|
||||||
showLogin.value = !showLogin.value
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
@@ -29,7 +29,10 @@ import IconCheck from "~icons/lucide/check"
|
|||||||
import { copyToClipboard } from "~/helpers/utils/clipboard"
|
import { copyToClipboard } from "~/helpers/utils/clipboard"
|
||||||
import { useI18n } from "@composables/i18n"
|
import { useI18n } from "@composables/i18n"
|
||||||
import { useToast } from "@composables/toast"
|
import { useToast } from "@composables/toast"
|
||||||
import { platform } from "~/platform"
|
import { useReadonlyStream } from "@composables/stream"
|
||||||
|
import { authIdToken$ } from "~/helpers/fb/auth"
|
||||||
|
|
||||||
|
const userAuthToken = useReadonlyStream(authIdToken$, null)
|
||||||
|
|
||||||
const t = useI18n()
|
const t = useI18n()
|
||||||
|
|
||||||
@@ -50,9 +53,8 @@ const copyIcon = refAutoReset<typeof IconCopy | typeof IconCheck>(
|
|||||||
|
|
||||||
// Copy user auth token to clipboard
|
// Copy user auth token to clipboard
|
||||||
const copyUserAuthToken = () => {
|
const copyUserAuthToken = () => {
|
||||||
const token = platform.auth.getDevOptsBackendIDToken()
|
if (userAuthToken.value) {
|
||||||
if (token) {
|
copyToClipboard(userAuthToken.value)
|
||||||
copyToClipboard(token)
|
|
||||||
copyIcon.value = IconCheck
|
copyIcon.value = IconCheck
|
||||||
toast.success(`${t("state.copied_to_clipboard")}`)
|
toast.success(`${t("state.copied_to_clipboard")}`)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -50,9 +50,9 @@
|
|||||||
ref="tippyActions"
|
ref="tippyActions"
|
||||||
class="flex flex-col focus:outline-none"
|
class="flex flex-col focus:outline-none"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
@keyup.d="documentation!.$el.click()"
|
@keyup.d="documentation.$el.click()"
|
||||||
@keyup.s="shortcuts!.$el.click()"
|
@keyup.s="shortcuts.$el.click()"
|
||||||
@keyup.c="chat!.$el.click()"
|
@keyup.c="chat.$el.click()"
|
||||||
@keyup.escape="hide()"
|
@keyup.escape="hide()"
|
||||||
>
|
>
|
||||||
<SmartItem
|
<SmartItem
|
||||||
@@ -71,7 +71,7 @@
|
|||||||
:shortcut="['S']"
|
:shortcut="['S']"
|
||||||
@click="
|
@click="
|
||||||
() => {
|
() => {
|
||||||
invokeAction('flyouts.keybinds.toggle')
|
showShortcuts = true
|
||||||
hide()
|
hide()
|
||||||
}
|
}
|
||||||
"
|
"
|
||||||
@@ -122,7 +122,7 @@
|
|||||||
:label="`${t('app.invite')}`"
|
:label="`${t('app.invite')}`"
|
||||||
@click="
|
@click="
|
||||||
() => {
|
() => {
|
||||||
invokeAction('modals.share.toggle')
|
showShare = true
|
||||||
hide()
|
hide()
|
||||||
}
|
}
|
||||||
"
|
"
|
||||||
@@ -154,7 +154,7 @@
|
|||||||
'app.shortcuts'
|
'app.shortcuts'
|
||||||
)} <kbd>${getSpecialKey()}</kbd><kbd>K</kbd>`"
|
)} <kbd>${getSpecialKey()}</kbd><kbd>K</kbd>`"
|
||||||
:icon="IconZap"
|
:icon="IconZap"
|
||||||
@click="invokeAction('flyouts.keybinds.toggle')"
|
@click="showShortcuts = true"
|
||||||
/>
|
/>
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
v-if="navigatorShare"
|
v-if="navigatorShare"
|
||||||
@@ -188,6 +188,8 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<AppShortcuts :show="showShortcuts" @close="showShortcuts = false" />
|
||||||
|
<AppShare :show="showShare" @hide-modal="showShare = false" />
|
||||||
<AppDeveloperOptions
|
<AppDeveloperOptions
|
||||||
:show="showDeveloperOptions"
|
:show="showDeveloperOptions"
|
||||||
@hide-modal="showDeveloperOptions = false"
|
@hide-modal="showDeveloperOptions = false"
|
||||||
@@ -215,19 +217,29 @@ import IconGithub from "~icons/lucide/github"
|
|||||||
import IconTwitter from "~icons/lucide/twitter"
|
import IconTwitter from "~icons/lucide/twitter"
|
||||||
import IconUserPlus from "~icons/lucide/user-plus"
|
import IconUserPlus from "~icons/lucide/user-plus"
|
||||||
import IconLock from "~icons/lucide/lock"
|
import IconLock from "~icons/lucide/lock"
|
||||||
|
import { defineActionHandler } from "~/helpers/actions"
|
||||||
import { showChat } from "@modules/crisp"
|
import { showChat } from "@modules/crisp"
|
||||||
import { useSetting } from "@composables/settings"
|
import { useSetting } from "@composables/settings"
|
||||||
import { useI18n } from "@composables/i18n"
|
import { useI18n } from "@composables/i18n"
|
||||||
import { useReadonlyStream } from "@composables/stream"
|
import { useReadonlyStream } from "@composables/stream"
|
||||||
import { platform } from "~/platform"
|
import { currentUser$ } from "~/helpers/fb/auth"
|
||||||
import { TippyComponent } from "vue-tippy"
|
import { TippyComponent } from "vue-tippy"
|
||||||
|
import SmartItem from "@components/smart/Item.vue"
|
||||||
import { getPlatformSpecialKey as getSpecialKey } from "~/helpers/platformutils"
|
import { getPlatformSpecialKey as getSpecialKey } from "~/helpers/platformutils"
|
||||||
import { invokeAction } from "@helpers/actions"
|
|
||||||
import SmartItem from "@hoppscotch/ui/src/components/smart/Item.vue"
|
|
||||||
|
|
||||||
const t = useI18n()
|
const t = useI18n()
|
||||||
|
const showShortcuts = ref(false)
|
||||||
|
const showShare = ref(false)
|
||||||
const showDeveloperOptions = ref(false)
|
const showDeveloperOptions = ref(false)
|
||||||
|
|
||||||
|
defineActionHandler("flyouts.keybinds.toggle", () => {
|
||||||
|
showShortcuts.value = !showShortcuts.value
|
||||||
|
})
|
||||||
|
|
||||||
|
defineActionHandler("modals.share.toggle", () => {
|
||||||
|
showShare.value = !showShare.value
|
||||||
|
})
|
||||||
|
|
||||||
const EXPAND_NAVIGATION = useSetting("EXPAND_NAVIGATION")
|
const EXPAND_NAVIGATION = useSetting("EXPAND_NAVIGATION")
|
||||||
const SIDEBAR = useSetting("SIDEBAR")
|
const SIDEBAR = useSetting("SIDEBAR")
|
||||||
const ZEN_MODE = useSetting("ZEN_MODE")
|
const ZEN_MODE = useSetting("ZEN_MODE")
|
||||||
@@ -236,10 +248,7 @@ const SIDEBAR_ON_LEFT = useSetting("SIDEBAR_ON_LEFT")
|
|||||||
|
|
||||||
const navigatorShare = !!navigator.share
|
const navigatorShare = !!navigator.share
|
||||||
|
|
||||||
const currentUser = useReadonlyStream(
|
const currentUser = useReadonlyStream(currentUser$, null)
|
||||||
platform.auth.getCurrentUserStream(),
|
|
||||||
platform.auth.getCurrentUser()
|
|
||||||
)
|
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => ZEN_MODE.value,
|
() => ZEN_MODE.value,
|
||||||
@@ -274,7 +283,7 @@ const showDeveloperOptionModal = () => {
|
|||||||
|
|
||||||
// Template refs
|
// Template refs
|
||||||
const tippyActions = ref<TippyComponent | null>(null)
|
const tippyActions = ref<TippyComponent | null>(null)
|
||||||
const documentation = ref<typeof SmartItem>()
|
const documentation = ref<typeof SmartItem | null>(null)
|
||||||
const shortcuts = ref<typeof SmartItem>()
|
const shortcuts = ref<typeof SmartItem | null>(null)
|
||||||
const chat = ref<typeof SmartItem>()
|
const chat = ref<typeof SmartItem | null>(null)
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -3,13 +3,7 @@
|
|||||||
<header
|
<header
|
||||||
class="flex items-center justify-between flex-1 flex-shrink-0 px-2 py-2 space-x-2 overflow-x-auto overflow-y-hidden"
|
class="flex items-center justify-between flex-1 flex-shrink-0 px-2 py-2 space-x-2 overflow-x-auto overflow-y-hidden"
|
||||||
>
|
>
|
||||||
<div
|
<div class="inline-flex items-center space-x-2">
|
||||||
class="inline-flex items-center space-x-2"
|
|
||||||
:style="{
|
|
||||||
paddingTop: platform.ui?.appHeader?.paddingTop?.value,
|
|
||||||
paddingLeft: platform.ui?.appHeader?.paddingLeft?.value,
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
class="tracking-wide !font-bold !text-secondaryDark hover:bg-primaryDark focus-visible:bg-primaryDark uppercase"
|
class="tracking-wide !font-bold !text-secondaryDark hover:bg-primaryDark focus-visible:bg-primaryDark uppercase"
|
||||||
:label="t('app.name')"
|
:label="t('app.name')"
|
||||||
@@ -48,12 +42,12 @@
|
|||||||
:label="t('header.save_workspace')"
|
:label="t('header.save_workspace')"
|
||||||
filled
|
filled
|
||||||
class="hidden md:flex"
|
class="hidden md:flex"
|
||||||
@click="invokeAction('modals.login.toggle')"
|
@click="showLogin = true"
|
||||||
/>
|
/>
|
||||||
<ButtonPrimary
|
<ButtonPrimary
|
||||||
v-if="currentUser === null"
|
v-if="currentUser === null"
|
||||||
:label="t('header.login')"
|
:label="t('header.login')"
|
||||||
@click="invokeAction('modals.login.toggle')"
|
@click="showLogin = true"
|
||||||
/>
|
/>
|
||||||
<div v-else class="inline-flex items-center space-x-2">
|
<div v-else class="inline-flex items-center space-x-2">
|
||||||
<ButtonPrimary
|
<ButtonPrimary
|
||||||
@@ -156,6 +150,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<AppAnnouncement v-if="!network.isOnline" />
|
<AppAnnouncement v-if="!network.isOnline" />
|
||||||
|
<FirebaseLogin :show="showLogin" @hide-modal="showLogin = false" />
|
||||||
<TeamsModal :show="showTeamsModal" @hide-modal="showTeamsModal = false" />
|
<TeamsModal :show="showTeamsModal" @hide-modal="showTeamsModal = false" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -171,7 +166,7 @@ import IconUploadCloud from "~icons/lucide/upload-cloud"
|
|||||||
import IconUserPlus from "~icons/lucide/user-plus"
|
import IconUserPlus from "~icons/lucide/user-plus"
|
||||||
import { breakpointsTailwind, useBreakpoints, useNetwork } from "@vueuse/core"
|
import { breakpointsTailwind, useBreakpoints, useNetwork } from "@vueuse/core"
|
||||||
import { pwaDefferedPrompt, installPWA } from "@modules/pwa"
|
import { pwaDefferedPrompt, installPWA } from "@modules/pwa"
|
||||||
import { platform } from "~/platform"
|
import { probableUser$ } from "@helpers/fb/auth"
|
||||||
import { useI18n } from "@composables/i18n"
|
import { useI18n } from "@composables/i18n"
|
||||||
import { useReadonlyStream } from "@composables/stream"
|
import { useReadonlyStream } from "@composables/stream"
|
||||||
import { invokeAction } from "@helpers/actions"
|
import { invokeAction } from "@helpers/actions"
|
||||||
@@ -186,6 +181,7 @@ const t = useI18n()
|
|||||||
|
|
||||||
const showInstallButton = computed(() => !!pwaDefferedPrompt.value)
|
const showInstallButton = computed(() => !!pwaDefferedPrompt.value)
|
||||||
|
|
||||||
|
const showLogin = ref(false)
|
||||||
const showTeamsModal = ref(false)
|
const showTeamsModal = ref(false)
|
||||||
|
|
||||||
const breakpoints = useBreakpoints(breakpointsTailwind)
|
const breakpoints = useBreakpoints(breakpointsTailwind)
|
||||||
@@ -193,10 +189,7 @@ const mdAndLarger = breakpoints.greater("md")
|
|||||||
|
|
||||||
const network = reactive(useNetwork())
|
const network = reactive(useNetwork())
|
||||||
|
|
||||||
const currentUser = useReadonlyStream(
|
const currentUser = useReadonlyStream(probableUser$, null)
|
||||||
platform.auth.getProbableUserStream(),
|
|
||||||
platform.auth.getProbableUser()
|
|
||||||
)
|
|
||||||
|
|
||||||
// Template refs
|
// Template refs
|
||||||
const tippyActions = ref<any | null>(null)
|
const tippyActions = ref<any | null>(null)
|
||||||
|
|||||||
@@ -76,10 +76,10 @@ type PaneEvent = {
|
|||||||
size: number
|
size: number
|
||||||
}
|
}
|
||||||
|
|
||||||
const PANE_MAIN_SIZE = ref(74)
|
const PANE_SIDEBAR_SIZE = ref(25)
|
||||||
const PANE_SIDEBAR_SIZE = ref(26)
|
const PANE_MAIN_SIZE = ref(75)
|
||||||
const PANE_MAIN_TOP_SIZE = ref(42)
|
const PANE_MAIN_TOP_SIZE = ref(45)
|
||||||
const PANE_MAIN_BOTTOM_SIZE = ref(58)
|
const PANE_MAIN_BOTTOM_SIZE = ref(65)
|
||||||
|
|
||||||
if (!COLUMN_LAYOUT.value) {
|
if (!COLUMN_LAYOUT.value) {
|
||||||
PANE_MAIN_TOP_SIZE.value = 50
|
PANE_MAIN_TOP_SIZE.value = 50
|
||||||
|
|||||||
@@ -41,52 +41,47 @@
|
|||||||
</SmartModal>
|
</SmartModal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script lang="ts">
|
||||||
import { watch, ref } from "vue"
|
import { defineComponent } from "vue"
|
||||||
import { useToast } from "@composables/toast"
|
import { useToast } from "@composables/toast"
|
||||||
import { useI18n } from "@composables/i18n"
|
import { useI18n } from "@composables/i18n"
|
||||||
|
|
||||||
const toast = useToast()
|
export default defineComponent({
|
||||||
const t = useI18n()
|
props: {
|
||||||
|
show: Boolean,
|
||||||
const props = withDefaults(
|
loadingState: Boolean,
|
||||||
defineProps<{
|
},
|
||||||
show: boolean
|
emits: ["submit", "hide-modal"],
|
||||||
loadingState: boolean
|
setup() {
|
||||||
}>(),
|
return {
|
||||||
{
|
toast: useToast(),
|
||||||
show: false,
|
t: useI18n(),
|
||||||
loadingState: false,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
const emit = defineEmits<{
|
|
||||||
(e: "submit", name: string): void
|
|
||||||
(e: "hide-modal"): void
|
|
||||||
}>()
|
|
||||||
|
|
||||||
const name = ref("")
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => props.show,
|
|
||||||
(show) => {
|
|
||||||
if (!show) {
|
|
||||||
name.value = ""
|
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
)
|
data() {
|
||||||
|
return {
|
||||||
const addNewCollection = () => {
|
name: null,
|
||||||
if (!name.value) {
|
}
|
||||||
toast.error(t("collection.invalid_name"))
|
},
|
||||||
return
|
watch: {
|
||||||
}
|
show(isShowing: boolean) {
|
||||||
|
if (!isShowing) {
|
||||||
emit("submit", name.value)
|
this.name = null
|
||||||
}
|
}
|
||||||
|
},
|
||||||
const hideModal = () => {
|
},
|
||||||
name.value = ""
|
methods: {
|
||||||
emit("hide-modal")
|
addNewCollection() {
|
||||||
}
|
if (!this.name) {
|
||||||
|
this.toast.error(this.t("collection.invalid_name"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.$emit("submit", this.name)
|
||||||
|
},
|
||||||
|
hideModal() {
|
||||||
|
this.name = null
|
||||||
|
this.$emit("hide-modal")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
v-if="show"
|
v-if="show"
|
||||||
dialog
|
dialog
|
||||||
:title="t('folder.new')"
|
:title="t('folder.new')"
|
||||||
@close="emit('hide-modal')"
|
@close="$emit('hide-modal')"
|
||||||
>
|
>
|
||||||
<template #body>
|
<template #body>
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
@@ -41,51 +41,52 @@
|
|||||||
</SmartModal>
|
</SmartModal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script lang="ts">
|
||||||
import { ref, watch } from "vue"
|
import { defineComponent } from "vue"
|
||||||
import { useI18n } from "@composables/i18n"
|
import { useI18n } from "@composables/i18n"
|
||||||
import { useToast } from "@composables/toast"
|
import { useToast } from "@composables/toast"
|
||||||
|
|
||||||
const toast = useToast()
|
export default defineComponent({
|
||||||
const t = useI18n()
|
props: {
|
||||||
|
show: Boolean,
|
||||||
const props = withDefaults(
|
folder: { type: Object, default: () => ({}) },
|
||||||
defineProps<{
|
folderPath: { type: String, default: null },
|
||||||
show: boolean
|
collectionIndex: { type: Number, default: null },
|
||||||
loadingState: boolean
|
loadingState: Boolean,
|
||||||
}>(),
|
},
|
||||||
{
|
emits: ["hide-modal", "add-folder"],
|
||||||
show: false,
|
setup() {
|
||||||
loadingState: false,
|
return {
|
||||||
}
|
toast: useToast(),
|
||||||
)
|
t: useI18n(),
|
||||||
|
|
||||||
const emit = defineEmits<{
|
|
||||||
(e: "hide-modal"): void
|
|
||||||
(e: "add-folder", name: string): void
|
|
||||||
}>()
|
|
||||||
|
|
||||||
const name = ref("")
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => props.show,
|
|
||||||
(show) => {
|
|
||||||
if (!show) {
|
|
||||||
name.value = ""
|
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
)
|
data() {
|
||||||
|
return {
|
||||||
const addFolder = () => {
|
name: null,
|
||||||
if (name.value.trim() === "") {
|
}
|
||||||
toast.error(t("folder.invalid_name"))
|
},
|
||||||
return
|
watch: {
|
||||||
}
|
show(isShowing: boolean) {
|
||||||
emit("add-folder", name.value)
|
if (!isShowing) this.name = null
|
||||||
}
|
},
|
||||||
|
},
|
||||||
const hideModal = () => {
|
methods: {
|
||||||
name.value = ""
|
addFolder() {
|
||||||
emit("hide-modal")
|
if (!this.name) {
|
||||||
}
|
this.toast.error(this.t("folder.invalid_name"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.$emit("add-folder", {
|
||||||
|
name: this.name,
|
||||||
|
folder: this.folder,
|
||||||
|
path: this.folderPath || `${this.collectionIndex}`,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
hideModal() {
|
||||||
|
this.name = null
|
||||||
|
this.$emit("hide-modal")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -48,20 +48,23 @@ import { getRESTRequest } from "~/newstore/RESTSession"
|
|||||||
const toast = useToast()
|
const toast = useToast()
|
||||||
const t = useI18n()
|
const t = useI18n()
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = defineProps<{
|
||||||
defineProps<{
|
show: boolean
|
||||||
show: boolean
|
loadingState: boolean
|
||||||
loadingState: boolean
|
folder?: object
|
||||||
}>(),
|
folderPath?: string
|
||||||
{
|
}>()
|
||||||
show: false,
|
|
||||||
loadingState: false,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(event: "hide-modal"): void
|
(e: "hide-modal"): void
|
||||||
(event: "add-request", name: string): void
|
(
|
||||||
|
e: "add-request",
|
||||||
|
v: {
|
||||||
|
name: string
|
||||||
|
folder: object | undefined
|
||||||
|
path: string | undefined
|
||||||
|
}
|
||||||
|
): void
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const name = ref("")
|
const name = ref("")
|
||||||
@@ -76,11 +79,15 @@ watch(
|
|||||||
)
|
)
|
||||||
|
|
||||||
const addRequest = () => {
|
const addRequest = () => {
|
||||||
if (name.value.trim() === "") {
|
if (!name.value) {
|
||||||
toast.error(`${t("error.empty_req_name")}`)
|
toast.error(`${t("error.empty_req_name")}`)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
emit("add-request", name.value)
|
emit("add-request", {
|
||||||
|
name: name.value,
|
||||||
|
folder: props.folder,
|
||||||
|
path: props.folderPath,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const hideModal = () => {
|
const hideModal = () => {
|
||||||
|
|||||||
@@ -0,0 +1,159 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<SmartTabs
|
||||||
|
:id="'collections_tab'"
|
||||||
|
v-model="selectedCollectionTab"
|
||||||
|
render-inactive-tabs
|
||||||
|
>
|
||||||
|
<SmartTab
|
||||||
|
:id="'my-collections'"
|
||||||
|
:label="`${t('collection.my_collections')}`"
|
||||||
|
/>
|
||||||
|
<SmartTab
|
||||||
|
:id="'team-collections'"
|
||||||
|
:label="`${t('collection.team_collections')}`"
|
||||||
|
:disabled="!currentUser"
|
||||||
|
>
|
||||||
|
<SmartIntersection @intersecting="onTeamSelectIntersect">
|
||||||
|
<tippy
|
||||||
|
interactive
|
||||||
|
trigger="click"
|
||||||
|
theme="popover"
|
||||||
|
placement="bottom"
|
||||||
|
:on-shown="() => tippyActions.focus()"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
|
:title="`${t('collection.select_team')}`"
|
||||||
|
class="bg-transparent border-b border-dividerLight select-wrapper"
|
||||||
|
>
|
||||||
|
<ButtonSecondary
|
||||||
|
v-if="collectionsType.selectedTeam"
|
||||||
|
:icon="IconUsers"
|
||||||
|
:label="collectionsType.selectedTeam.name"
|
||||||
|
class="flex-1 !justify-start pr-8 rounded-none"
|
||||||
|
/>
|
||||||
|
<ButtonSecondary
|
||||||
|
v-else
|
||||||
|
:label="`${t('collection.select_team')}`"
|
||||||
|
class="flex-1 !justify-start pr-8 rounded-none"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<template #content="{ hide }">
|
||||||
|
<div
|
||||||
|
ref="tippyActions"
|
||||||
|
class="flex flex-col focus:outline-none"
|
||||||
|
tabindex="0"
|
||||||
|
@keyup.escape="hide()"
|
||||||
|
>
|
||||||
|
<SmartItem
|
||||||
|
v-for="(team, index) in myTeams"
|
||||||
|
:key="`team-${index}`"
|
||||||
|
:label="team.name"
|
||||||
|
:info-icon="
|
||||||
|
team.id === collectionsType.selectedTeam?.id
|
||||||
|
? IconDone
|
||||||
|
: undefined
|
||||||
|
"
|
||||||
|
:active-info-icon="
|
||||||
|
team.id === collectionsType.selectedTeam?.id
|
||||||
|
"
|
||||||
|
:icon="IconUsers"
|
||||||
|
@click="
|
||||||
|
() => {
|
||||||
|
updateSelectedTeam(team)
|
||||||
|
hide()
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</tippy>
|
||||||
|
</SmartIntersection>
|
||||||
|
</SmartTab>
|
||||||
|
</SmartTabs>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import IconUsers from "~icons/lucide/users"
|
||||||
|
import IconDone from "~icons/lucide/check"
|
||||||
|
import { ref, watch } from "vue"
|
||||||
|
import { GetMyTeamsQuery, Team } from "~/helpers/backend/graphql"
|
||||||
|
import { currentUserInfo$ } from "~/helpers/teams/BackendUserInfo"
|
||||||
|
import TeamListAdapter from "~/helpers/teams/TeamListAdapter"
|
||||||
|
import { useReadonlyStream } from "@composables/stream"
|
||||||
|
import { onLoggedIn } from "@composables/auth"
|
||||||
|
import { useI18n } from "@composables/i18n"
|
||||||
|
import { useLocalState } from "~/newstore/localstate"
|
||||||
|
|
||||||
|
type TeamData = GetMyTeamsQuery["myTeams"][number]
|
||||||
|
|
||||||
|
type CollectionTabs = "my-collections" | "team-collections"
|
||||||
|
|
||||||
|
const t = useI18n()
|
||||||
|
|
||||||
|
// Template refs
|
||||||
|
const tippyActions = ref<any | null>(null)
|
||||||
|
const selectedCollectionTab = ref<CollectionTabs>("my-collections")
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
collectionsType: {
|
||||||
|
type: "my-collections" | "team-collections"
|
||||||
|
selectedTeam: Team | undefined
|
||||||
|
}
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: "update-collection-type", tabID: string): void
|
||||||
|
(e: "update-selected-team", team: TeamData | undefined): void
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const currentUser = useReadonlyStream(currentUserInfo$, null)
|
||||||
|
|
||||||
|
const adapter = new TeamListAdapter(true)
|
||||||
|
const myTeams = useReadonlyStream(adapter.teamList$, null)
|
||||||
|
const REMEMBERED_TEAM_ID = useLocalState("REMEMBERED_TEAM_ID")
|
||||||
|
let teamListFetched = false
|
||||||
|
|
||||||
|
watch(myTeams, (teams) => {
|
||||||
|
if (teams && !teamListFetched) {
|
||||||
|
teamListFetched = true
|
||||||
|
if (REMEMBERED_TEAM_ID.value && currentUser) {
|
||||||
|
const team = teams.find((t) => t.id === REMEMBERED_TEAM_ID.value)
|
||||||
|
if (team) updateSelectedTeam(team)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
onLoggedIn(() => {
|
||||||
|
adapter.initialize()
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => currentUser.value,
|
||||||
|
(user) => {
|
||||||
|
if (!user) {
|
||||||
|
selectedCollectionTab.value = "my-collections"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const onTeamSelectIntersect = () => {
|
||||||
|
// Load team data as soon as intersection
|
||||||
|
adapter.fetchList()
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateCollectionsType = (tabID: string) => {
|
||||||
|
emit("update-collection-type", tabID)
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateSelectedTeam = (team: TeamData | undefined) => {
|
||||||
|
REMEMBERED_TEAM_ID.value = team?.id
|
||||||
|
emit("update-selected-team", team)
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(selectedCollectionTab, (newValue: string) => {
|
||||||
|
updateCollectionsType(newValue)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
@@ -1,252 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="flex flex-col" :class="[{ 'bg-primaryLight': dragging }]">
|
|
||||||
<div
|
|
||||||
class="flex items-stretch group"
|
|
||||||
@dragover.prevent
|
|
||||||
@drop.prevent="dropEvent"
|
|
||||||
@dragover="dragging = true"
|
|
||||||
@drop="dragging = false"
|
|
||||||
@dragleave="dragging = false"
|
|
||||||
@dragend="dragging = false"
|
|
||||||
@contextmenu.prevent="options?.tippy.show()"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
class="flex items-center justify-center px-4 cursor-pointer"
|
|
||||||
@click="emit('toggle-children')"
|
|
||||||
>
|
|
||||||
<component
|
|
||||||
:is="collectionIcon"
|
|
||||||
class="svg-icons"
|
|
||||||
:class="{ 'text-accent': isSelected }"
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
<span
|
|
||||||
class="flex flex-1 min-w-0 py-2 pr-2 transition cursor-pointer group-hover:text-secondaryDark"
|
|
||||||
@click="emit('toggle-children')"
|
|
||||||
>
|
|
||||||
<span class="truncate" :class="{ 'text-accent': isSelected }">
|
|
||||||
{{ collectionName }}
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
<div v-if="!hasNoTeamAccess" class="flex">
|
|
||||||
<ButtonSecondary
|
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
|
||||||
:icon="IconFilePlus"
|
|
||||||
:title="t('request.new')"
|
|
||||||
class="hidden group-hover:inline-flex"
|
|
||||||
@click="emit('add-request')"
|
|
||||||
/>
|
|
||||||
<ButtonSecondary
|
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
|
||||||
:icon="IconFolderPlus"
|
|
||||||
:title="t('folder.new')"
|
|
||||||
class="hidden group-hover:inline-flex"
|
|
||||||
@click="emit('add-folder')"
|
|
||||||
/>
|
|
||||||
<span>
|
|
||||||
<tippy
|
|
||||||
ref="options"
|
|
||||||
interactive
|
|
||||||
trigger="click"
|
|
||||||
theme="popover"
|
|
||||||
:on-shown="() => tippyActions!.focus()"
|
|
||||||
>
|
|
||||||
<ButtonSecondary
|
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
|
||||||
:title="t('action.more')"
|
|
||||||
:icon="IconMoreVertical"
|
|
||||||
/>
|
|
||||||
<template #content="{ hide }">
|
|
||||||
<div
|
|
||||||
ref="tippyActions"
|
|
||||||
class="flex flex-col focus:outline-none"
|
|
||||||
tabindex="0"
|
|
||||||
@keyup.r="requestAction?.$el.click()"
|
|
||||||
@keyup.n="folderAction?.$el.click()"
|
|
||||||
@keyup.e="edit?.$el.click()"
|
|
||||||
@keyup.delete="deleteAction?.$el.click()"
|
|
||||||
@keyup.x="exportAction?.$el.click()"
|
|
||||||
@keyup.escape="hide()"
|
|
||||||
>
|
|
||||||
<SmartItem
|
|
||||||
ref="requestAction"
|
|
||||||
:icon="IconFilePlus"
|
|
||||||
:label="t('request.new')"
|
|
||||||
:shortcut="['R']"
|
|
||||||
@click="
|
|
||||||
() => {
|
|
||||||
emit('add-request')
|
|
||||||
hide()
|
|
||||||
}
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
<SmartItem
|
|
||||||
ref="folderAction"
|
|
||||||
:icon="IconFolderPlus"
|
|
||||||
:label="t('folder.new')"
|
|
||||||
:shortcut="['N']"
|
|
||||||
@click="
|
|
||||||
() => {
|
|
||||||
emit('add-folder')
|
|
||||||
hide()
|
|
||||||
}
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
<SmartItem
|
|
||||||
ref="edit"
|
|
||||||
:icon="IconEdit"
|
|
||||||
:label="t('action.edit')"
|
|
||||||
:shortcut="['E']"
|
|
||||||
@click="
|
|
||||||
() => {
|
|
||||||
emit('edit-collection')
|
|
||||||
hide()
|
|
||||||
}
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
<SmartItem
|
|
||||||
ref="exportAction"
|
|
||||||
:icon="IconDownload"
|
|
||||||
:label="t('export.title')"
|
|
||||||
:shortcut="['X']"
|
|
||||||
:loading="exportLoading"
|
|
||||||
@click="
|
|
||||||
() => {
|
|
||||||
emit('export-data'),
|
|
||||||
collectionsType === 'my-collections' ? hide() : null
|
|
||||||
}
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
<SmartItem
|
|
||||||
ref="deleteAction"
|
|
||||||
:icon="IconTrash2"
|
|
||||||
:label="t('action.delete')"
|
|
||||||
:shortcut="['⌫']"
|
|
||||||
@click="
|
|
||||||
() => {
|
|
||||||
emit('remove-collection')
|
|
||||||
hide()
|
|
||||||
}
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</tippy>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import IconCheckCircle from "~icons/lucide/check-circle"
|
|
||||||
import IconFolderPlus from "~icons/lucide/folder-plus"
|
|
||||||
import IconFilePlus from "~icons/lucide/file-plus"
|
|
||||||
import IconMoreVertical from "~icons/lucide/more-vertical"
|
|
||||||
import IconDownload from "~icons/lucide/download"
|
|
||||||
import IconTrash2 from "~icons/lucide/trash-2"
|
|
||||||
import IconEdit from "~icons/lucide/edit"
|
|
||||||
import IconFolder from "~icons/lucide/folder"
|
|
||||||
import IconFolderOpen from "~icons/lucide/folder-open"
|
|
||||||
import { PropType, ref, computed, watch } from "vue"
|
|
||||||
import { HoppCollection, HoppRESTRequest } from "@hoppscotch/data"
|
|
||||||
import { useI18n } from "@composables/i18n"
|
|
||||||
import { TippyComponent } from "vue-tippy"
|
|
||||||
import { TeamCollection } from "~/helpers/teams/TeamCollection"
|
|
||||||
|
|
||||||
type CollectionType = "my-collections" | "team-collections"
|
|
||||||
type FolderType = "collection" | "folder"
|
|
||||||
|
|
||||||
const t = useI18n()
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
data: {
|
|
||||||
type: Object as PropType<HoppCollection<HoppRESTRequest> | TeamCollection>,
|
|
||||||
default: () => ({}),
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
collectionsType: {
|
|
||||||
type: String as PropType<CollectionType>,
|
|
||||||
default: "my-collections",
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Collection component can be used for both collections and folders.
|
|
||||||
* folderType is used to determine which one it is.
|
|
||||||
*/
|
|
||||||
folderType: {
|
|
||||||
type: String as PropType<FolderType>,
|
|
||||||
default: "collection",
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
isOpen: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
isSelected: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
exportLoading: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
hasNoTeamAccess: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const emit = defineEmits<{
|
|
||||||
(event: "toggle-children"): void
|
|
||||||
(event: "add-request"): void
|
|
||||||
(event: "add-folder"): void
|
|
||||||
(event: "edit-collection"): void
|
|
||||||
(event: "export-data"): void
|
|
||||||
(event: "remove-collection"): void
|
|
||||||
(event: "drop-event", payload: DataTransfer): void
|
|
||||||
}>()
|
|
||||||
|
|
||||||
const tippyActions = ref<TippyComponent | null>(null)
|
|
||||||
const requestAction = ref<HTMLButtonElement | null>(null)
|
|
||||||
const folderAction = ref<HTMLButtonElement | null>(null)
|
|
||||||
const edit = ref<HTMLButtonElement | null>(null)
|
|
||||||
const deleteAction = ref<HTMLButtonElement | null>(null)
|
|
||||||
const exportAction = ref<HTMLButtonElement | null>(null)
|
|
||||||
const options = ref<TippyComponent | null>(null)
|
|
||||||
|
|
||||||
const dragging = ref(false)
|
|
||||||
|
|
||||||
const collectionIcon = computed(() => {
|
|
||||||
if (props.isSelected) return IconCheckCircle
|
|
||||||
else if (!props.isOpen) return IconFolder
|
|
||||||
else if (props.isOpen) return IconFolderOpen
|
|
||||||
else return IconFolder
|
|
||||||
})
|
|
||||||
|
|
||||||
const collectionName = computed(() => {
|
|
||||||
if ((props.data as HoppCollection<HoppRESTRequest>).name)
|
|
||||||
return (props.data as HoppCollection<HoppRESTRequest>).name
|
|
||||||
else return (props.data as TeamCollection).title
|
|
||||||
})
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => props.exportLoading,
|
|
||||||
(val) => {
|
|
||||||
if (!val) {
|
|
||||||
options.value!.tippy.hide()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
const dropEvent = ({ dataTransfer }: DragEvent) => {
|
|
||||||
if (dataTransfer) {
|
|
||||||
dragging.value = !dragging.value
|
|
||||||
emit("drop-event", dataTransfer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -41,52 +41,46 @@
|
|||||||
</SmartModal>
|
</SmartModal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script lang="ts">
|
||||||
import { ref, watch } from "vue"
|
import { defineComponent } from "vue"
|
||||||
import { useToast } from "@composables/toast"
|
import { useToast } from "@composables/toast"
|
||||||
import { useI18n } from "@composables/i18n"
|
import { useI18n } from "@composables/i18n"
|
||||||
|
|
||||||
const t = useI18n()
|
export default defineComponent({
|
||||||
const toast = useToast()
|
props: {
|
||||||
|
show: Boolean,
|
||||||
const props = withDefaults(
|
editingCollectionName: { type: String, default: null },
|
||||||
defineProps<{
|
loadingState: Boolean,
|
||||||
show: boolean
|
},
|
||||||
loadingState: boolean
|
emits: ["submit", "hide-modal"],
|
||||||
editingCollectionName: string
|
setup() {
|
||||||
}>(),
|
return {
|
||||||
{
|
toast: useToast(),
|
||||||
show: false,
|
t: useI18n(),
|
||||||
loadingState: false,
|
}
|
||||||
editingCollectionName: "",
|
},
|
||||||
}
|
data() {
|
||||||
)
|
return {
|
||||||
|
name: null,
|
||||||
const emit = defineEmits<{
|
}
|
||||||
(e: "submit", name: string): void
|
},
|
||||||
(e: "hide-modal"): void
|
watch: {
|
||||||
}>()
|
editingCollectionName(val) {
|
||||||
|
this.name = val
|
||||||
const name = ref("")
|
},
|
||||||
|
},
|
||||||
watch(
|
methods: {
|
||||||
() => props.editingCollectionName,
|
saveCollection() {
|
||||||
(newName) => {
|
if (!this.name) {
|
||||||
name.value = newName
|
this.toast.error(this.t("collection.invalid_name"))
|
||||||
}
|
return
|
||||||
)
|
}
|
||||||
|
this.$emit("submit", this.name)
|
||||||
const saveCollection = () => {
|
},
|
||||||
if (name.value.trim() === "") {
|
hideModal() {
|
||||||
toast.error(t("collection.invalid_name"))
|
this.name = null
|
||||||
return
|
this.$emit("hide-modal")
|
||||||
}
|
},
|
||||||
|
},
|
||||||
emit("submit", name.value)
|
})
|
||||||
}
|
|
||||||
|
|
||||||
const hideModal = () => {
|
|
||||||
name.value = ""
|
|
||||||
emit("hide-modal")
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
v-if="show"
|
v-if="show"
|
||||||
dialog
|
dialog
|
||||||
:title="t('folder.edit')"
|
:title="t('folder.edit')"
|
||||||
@close="emit('hide-modal')"
|
@close="$emit('hide-modal')"
|
||||||
>
|
>
|
||||||
<template #body>
|
<template #body>
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
@@ -41,52 +41,46 @@
|
|||||||
</SmartModal>
|
</SmartModal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script lang="ts">
|
||||||
import { ref, watch } from "vue"
|
import { defineComponent } from "vue"
|
||||||
import { useI18n } from "@composables/i18n"
|
import { useI18n } from "@composables/i18n"
|
||||||
import { useToast } from "@composables/toast"
|
import { useToast } from "@composables/toast"
|
||||||
|
|
||||||
const t = useI18n()
|
export default defineComponent({
|
||||||
const toast = useToast()
|
props: {
|
||||||
|
show: Boolean,
|
||||||
const props = withDefaults(
|
editingFolderName: { type: String, default: null },
|
||||||
defineProps<{
|
loadingState: Boolean,
|
||||||
show: boolean
|
},
|
||||||
loadingState: boolean
|
emits: ["submit", "hide-modal"],
|
||||||
editingFolderName: string
|
setup() {
|
||||||
}>(),
|
return {
|
||||||
{
|
t: useI18n(),
|
||||||
show: false,
|
toast: useToast(),
|
||||||
loadingState: false,
|
}
|
||||||
editingFolderName: "",
|
},
|
||||||
}
|
data() {
|
||||||
)
|
return {
|
||||||
|
name: null,
|
||||||
const emit = defineEmits<{
|
}
|
||||||
(e: "submit", name: string): void
|
},
|
||||||
(e: "hide-modal"): void
|
watch: {
|
||||||
}>()
|
editingFolderName(val) {
|
||||||
|
this.name = val
|
||||||
const name = ref("")
|
},
|
||||||
|
},
|
||||||
watch(
|
methods: {
|
||||||
() => props.editingFolderName,
|
editFolder() {
|
||||||
(newName) => {
|
if (!this.name) {
|
||||||
name.value = newName
|
this.toast.error(this.t("folder.invalid_name"))
|
||||||
}
|
return
|
||||||
)
|
}
|
||||||
|
this.$emit("submit", this.name)
|
||||||
const editFolder = () => {
|
},
|
||||||
if (name.value.trim() === "") {
|
hideModal() {
|
||||||
toast.error(t("folder.invalid_name"))
|
this.name = null
|
||||||
return
|
this.$emit("hide-modal")
|
||||||
}
|
},
|
||||||
|
},
|
||||||
emit("submit", name.value)
|
})
|
||||||
}
|
|
||||||
|
|
||||||
const hideModal = () => {
|
|
||||||
name.value = ""
|
|
||||||
emit("hide-modal")
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -9,13 +9,13 @@
|
|||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<input
|
<input
|
||||||
id="selectLabelEditReq"
|
id="selectLabelEditReq"
|
||||||
v-model="name"
|
v-model="requestUpdateData.name"
|
||||||
v-focus
|
v-focus
|
||||||
class="input floating-input"
|
class="input floating-input"
|
||||||
placeholder=" "
|
placeholder=" "
|
||||||
type="text"
|
type="text"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
@keyup.enter="editRequest"
|
@keyup.enter="saveRequest"
|
||||||
/>
|
/>
|
||||||
<label for="selectLabelEditReq">
|
<label for="selectLabelEditReq">
|
||||||
{{ t("action.label") }}
|
{{ t("action.label") }}
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
:label="t('action.save')"
|
:label="t('action.save')"
|
||||||
:loading="loadingState"
|
:loading="loadingState"
|
||||||
outline
|
outline
|
||||||
@click="editRequest"
|
@click="saveRequest"
|
||||||
/>
|
/>
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
:label="t('action.cancel')"
|
:label="t('action.cancel')"
|
||||||
@@ -41,52 +41,48 @@
|
|||||||
</SmartModal>
|
</SmartModal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script lang="ts">
|
||||||
import { ref, watch } from "vue"
|
import { defineComponent } from "vue"
|
||||||
import { useI18n } from "@composables/i18n"
|
import { useI18n } from "@composables/i18n"
|
||||||
import { useToast } from "@composables/toast"
|
import { useToast } from "@composables/toast"
|
||||||
|
|
||||||
const toast = useToast()
|
export default defineComponent({
|
||||||
const t = useI18n()
|
props: {
|
||||||
|
show: Boolean,
|
||||||
const props = withDefaults(
|
editingRequestName: { type: String, default: null },
|
||||||
defineProps<{
|
loadingState: Boolean,
|
||||||
show: boolean
|
},
|
||||||
loadingState: boolean
|
emits: ["submit", "hide-modal"],
|
||||||
editingRequestName: string
|
setup() {
|
||||||
}>(),
|
return {
|
||||||
{
|
t: useI18n(),
|
||||||
show: false,
|
toast: useToast(),
|
||||||
loadingState: false,
|
}
|
||||||
editingRequestName: "",
|
},
|
||||||
}
|
data() {
|
||||||
)
|
return {
|
||||||
|
requestUpdateData: {
|
||||||
const emit = defineEmits<{
|
name: null,
|
||||||
(e: "submit", name: string): void
|
},
|
||||||
(e: "hide-modal"): void
|
}
|
||||||
}>()
|
},
|
||||||
|
watch: {
|
||||||
const name = ref("")
|
editingRequestName(val) {
|
||||||
|
this.requestUpdateData.name = val
|
||||||
watch(
|
},
|
||||||
() => props.editingRequestName,
|
},
|
||||||
(newName) => {
|
methods: {
|
||||||
name.value = newName
|
saveRequest() {
|
||||||
}
|
if (!this.requestUpdateData.name) {
|
||||||
)
|
this.toast.error(this.t("request.invalid_name"))
|
||||||
|
return
|
||||||
const editRequest = () => {
|
}
|
||||||
if (name.value.trim() === "") {
|
this.$emit("submit", this.requestUpdateData)
|
||||||
toast.error(t("request.invalid_name"))
|
},
|
||||||
return
|
hideModal() {
|
||||||
}
|
this.requestUpdateData = { name: null }
|
||||||
|
this.$emit("hide-modal")
|
||||||
emit("submit", name.value)
|
},
|
||||||
}
|
},
|
||||||
|
})
|
||||||
const hideModal = () => {
|
|
||||||
name.value = ""
|
|
||||||
emit("hide-modal")
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<SmartModal
|
<SmartModal
|
||||||
v-if="show"
|
v-if="show"
|
||||||
dialog
|
dialog
|
||||||
:title="t('modal.collections')"
|
:title="`${t('modal.collections')}`"
|
||||||
styles="sm:max-w-md"
|
styles="sm:max-w-md"
|
||||||
@close="hideModal"
|
@close="hideModal"
|
||||||
>
|
>
|
||||||
@@ -81,6 +81,7 @@
|
|||||||
<div class="select-wrapper">
|
<div class="select-wrapper">
|
||||||
<select
|
<select
|
||||||
v-model="mySelectedCollectionID"
|
v-model="mySelectedCollectionID"
|
||||||
|
type="text"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
class="select"
|
class="select"
|
||||||
autofocus
|
autofocus
|
||||||
@@ -92,7 +93,6 @@
|
|||||||
v-for="(collection, collectionIndex) in myCollections"
|
v-for="(collection, collectionIndex) in myCollections"
|
||||||
:key="`collection-${collectionIndex}`"
|
:key="`collection-${collectionIndex}`"
|
||||||
:value="collectionIndex"
|
:value="collectionIndex"
|
||||||
class="bg-primary"
|
|
||||||
>
|
>
|
||||||
{{ collection.name }}
|
{{ collection.name }}
|
||||||
</option>
|
</option>
|
||||||
@@ -126,9 +126,8 @@
|
|||||||
v-tippy="{ theme: 'tooltip' }"
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
:title="t('action.download_file')"
|
:title="t('action.download_file')"
|
||||||
:icon="IconDownload"
|
:icon="IconDownload"
|
||||||
:loading="exportingTeamCollections"
|
|
||||||
:label="t('export.as_json')"
|
:label="t('export.as_json')"
|
||||||
@click="emit('export-json-collection')"
|
@click="exportJSON"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
@@ -150,9 +149,12 @@
|
|||||||
: false
|
: false
|
||||||
"
|
"
|
||||||
:icon="IconGithub"
|
:icon="IconGithub"
|
||||||
:loading="creatingGistCollection"
|
|
||||||
:label="t('export.create_secret_gist')"
|
:label="t('export.create_secret_gist')"
|
||||||
@click="emit('create-collection-gist')"
|
@click="
|
||||||
|
() => {
|
||||||
|
createCollectionGist()
|
||||||
|
}
|
||||||
|
"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -165,86 +167,129 @@
|
|||||||
import IconArrowLeft from "~icons/lucide/arrow-left"
|
import IconArrowLeft from "~icons/lucide/arrow-left"
|
||||||
import IconDownload from "~icons/lucide/download"
|
import IconDownload from "~icons/lucide/download"
|
||||||
import IconGithub from "~icons/lucide/github"
|
import IconGithub from "~icons/lucide/github"
|
||||||
import { computed, PropType, ref, watch } from "vue"
|
import { computed, ref, watch } from "vue"
|
||||||
import { pipe } from "fp-ts/function"
|
import { pipe } from "fp-ts/function"
|
||||||
import * as E from "fp-ts/Either"
|
import * as E from "fp-ts/Either"
|
||||||
import { HoppRESTRequest, HoppCollection } from "@hoppscotch/data"
|
import { HoppRESTRequest, HoppCollection } from "@hoppscotch/data"
|
||||||
|
import axios from "axios"
|
||||||
import { useI18n } from "@composables/i18n"
|
import { useI18n } from "@composables/i18n"
|
||||||
import { useReadonlyStream } from "@composables/stream"
|
import { useReadonlyStream } from "@composables/stream"
|
||||||
import { useToast } from "@composables/toast"
|
import { useToast } from "@composables/toast"
|
||||||
import { platform } from "~/platform"
|
import { currentUser$ } from "~/helpers/fb/auth"
|
||||||
import { appendRESTCollections, restCollections$ } from "~/newstore/collections"
|
import { appendRESTCollections, restCollections$ } from "~/newstore/collections"
|
||||||
import { RESTCollectionImporters } from "~/helpers/import-export/import/importers"
|
import { RESTCollectionImporters } from "~/helpers/import-export/import/importers"
|
||||||
import { StepReturnValue } from "~/helpers/import-export/steps"
|
import { StepReturnValue } from "~/helpers/import-export/steps"
|
||||||
|
import { runGQLQuery, runMutation } from "~/helpers/backend/GQLClient"
|
||||||
|
import {
|
||||||
|
ExportAsJsonDocument,
|
||||||
|
ImportFromJsonDocument,
|
||||||
|
} from "~/helpers/backend/graphql"
|
||||||
|
|
||||||
const toast = useToast()
|
const props = defineProps<{
|
||||||
const t = useI18n()
|
show: boolean
|
||||||
|
collectionsType:
|
||||||
type CollectionType = "team-collections" | "my-collections"
|
| {
|
||||||
|
type: "team-collections"
|
||||||
const props = defineProps({
|
selectedTeam: {
|
||||||
show: {
|
id: string
|
||||||
type: Boolean,
|
}
|
||||||
default: false,
|
}
|
||||||
required: true,
|
| { type: "my-collections" }
|
||||||
},
|
}>()
|
||||||
collectionsType: {
|
|
||||||
type: String as PropType<CollectionType>,
|
|
||||||
default: "my-collections",
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
exportingTeamCollections: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
creatingGistCollection: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
importingMyCollections: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: "hide-modal"): void
|
(e: "hide-modal"): void
|
||||||
(e: "update-team-collections"): void
|
(e: "update-team-collections"): void
|
||||||
(e: "export-json-collection"): void
|
|
||||||
(e: "create-collection-gist"): void
|
|
||||||
(e: "import-to-teams", payload: HoppCollection<HoppRESTRequest>[]): void
|
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const hasFile = ref(false)
|
const toast = useToast()
|
||||||
const hasGist = ref(false)
|
const t = useI18n()
|
||||||
|
const myCollections = useReadonlyStream(restCollections$, [])
|
||||||
const importerType = ref<number | null>(null)
|
const currentUser = useReadonlyStream(currentUser$, null)
|
||||||
|
|
||||||
const stepResults = ref<StepReturnValue[]>([])
|
|
||||||
|
|
||||||
|
// Template refs
|
||||||
|
const mode = ref("import_export")
|
||||||
|
const mySelectedCollectionID = ref<undefined | number>(undefined)
|
||||||
|
const collectionJson = ref("")
|
||||||
const inputChooseFileToImportFrom = ref<HTMLInputElement | any>()
|
const inputChooseFileToImportFrom = ref<HTMLInputElement | any>()
|
||||||
const mySelectedCollectionID = ref<number | undefined>(undefined)
|
|
||||||
const inputChooseGistToImportFrom = ref<string>("")
|
const inputChooseGistToImportFrom = ref<string>("")
|
||||||
|
|
||||||
const importerModules = computed(() =>
|
const getJSONCollection = async () => {
|
||||||
RESTCollectionImporters.filter(
|
if (props.collectionsType.type === "my-collections") {
|
||||||
(i) => i.applicableTo?.includes(props.collectionsType) ?? true
|
collectionJson.value = JSON.stringify(myCollections.value, null, 2)
|
||||||
)
|
} else {
|
||||||
)
|
collectionJson.value = pipe(
|
||||||
|
await runGQLQuery({
|
||||||
|
query: ExportAsJsonDocument,
|
||||||
|
variables: {
|
||||||
|
teamID: props.collectionsType.selectedTeam.id,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
E.matchW(
|
||||||
|
// TODO: Handle error case gracefully ?
|
||||||
|
() => {
|
||||||
|
throw new Error("Error exporting collection to JSON")
|
||||||
|
},
|
||||||
|
(x) => x.exportCollectionsToJSON
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const importerModule = computed(() => {
|
return collectionJson.value
|
||||||
if (importerType.value === null) return null
|
}
|
||||||
return importerModules.value[importerType.value]
|
|
||||||
})
|
|
||||||
|
|
||||||
const importerSteps = computed(() => importerModule.value?.steps ?? null)
|
const createCollectionGist = async () => {
|
||||||
|
if (!currentUser.value) {
|
||||||
|
toast.error(t("profile.no_permission").toString())
|
||||||
|
|
||||||
const enableImportButton = computed(
|
return
|
||||||
() => !(stepResults.value.length === importerSteps.value?.length)
|
}
|
||||||
)
|
|
||||||
|
await getJSONCollection()
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await axios.post(
|
||||||
|
"https://api.github.com/gists",
|
||||||
|
{
|
||||||
|
files: {
|
||||||
|
"hoppscotch-collections.json": {
|
||||||
|
content: collectionJson.value,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Authorization: `token ${currentUser.value.accessToken}`,
|
||||||
|
Accept: "application/vnd.github.v3+json",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
toast.success(t("export.gist_created").toString())
|
||||||
|
window.open(res.html_url)
|
||||||
|
} catch (e) {
|
||||||
|
toast.error(t("error.something_went_wrong").toString())
|
||||||
|
console.error(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fileImported = () => {
|
||||||
|
toast.success(t("state.file_imported").toString())
|
||||||
|
hideModal()
|
||||||
|
}
|
||||||
|
|
||||||
|
const failedImport = () => {
|
||||||
|
toast.error(t("import.failed").toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
const hideModal = () => {
|
||||||
|
mode.value = "import_export"
|
||||||
|
mySelectedCollectionID.value = undefined
|
||||||
|
resetImport()
|
||||||
|
emit("hide-modal")
|
||||||
|
}
|
||||||
|
|
||||||
|
const stepResults = ref<StepReturnValue[]>([])
|
||||||
|
|
||||||
watch(mySelectedCollectionID, (newValue) => {
|
watch(mySelectedCollectionID, (newValue) => {
|
||||||
if (newValue === undefined) return
|
if (newValue === undefined) return
|
||||||
@@ -252,9 +297,87 @@ watch(mySelectedCollectionID, (newValue) => {
|
|||||||
stepResults.value.push(newValue)
|
stepResults.value.push(newValue)
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(inputChooseGistToImportFrom, (url) => {
|
const importingMyCollections = ref(false)
|
||||||
|
|
||||||
|
const importToTeams = async (content: HoppCollection<HoppRESTRequest>) => {
|
||||||
|
importingMyCollections.value = true
|
||||||
|
if (props.collectionsType.type !== "team-collections") return
|
||||||
|
|
||||||
|
const result = await runMutation(ImportFromJsonDocument, {
|
||||||
|
jsonString: JSON.stringify(content),
|
||||||
|
teamID: props.collectionsType.selectedTeam.id,
|
||||||
|
})()
|
||||||
|
|
||||||
|
if (E.isLeft(result)) {
|
||||||
|
console.error(result.left)
|
||||||
|
} else {
|
||||||
|
emit("update-team-collections")
|
||||||
|
}
|
||||||
|
|
||||||
|
importingMyCollections.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const exportJSON = async () => {
|
||||||
|
await getJSONCollection()
|
||||||
|
|
||||||
|
const dataToWrite = collectionJson.value
|
||||||
|
const file = new Blob([dataToWrite], { type: "application/json" })
|
||||||
|
const a = document.createElement("a")
|
||||||
|
const url = URL.createObjectURL(file)
|
||||||
|
a.href = url
|
||||||
|
|
||||||
|
// TODO: get uri from meta
|
||||||
|
a.download = `${url.split("/").pop()!.split("#")[0].split("?")[0]}.json`
|
||||||
|
document.body.appendChild(a)
|
||||||
|
a.click()
|
||||||
|
toast.success(t("state.download_started").toString())
|
||||||
|
setTimeout(() => {
|
||||||
|
document.body.removeChild(a)
|
||||||
|
URL.revokeObjectURL(url)
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
const importerModules = computed(() =>
|
||||||
|
RESTCollectionImporters.filter(
|
||||||
|
(i) => i.applicableTo?.includes(props.collectionsType.type) ?? true
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
const importerType = ref<number | null>(null)
|
||||||
|
|
||||||
|
const importerModule = computed(() =>
|
||||||
|
importerType.value !== null ? importerModules.value[importerType.value] : null
|
||||||
|
)
|
||||||
|
|
||||||
|
const importerSteps = computed(() => importerModule.value?.steps ?? null)
|
||||||
|
|
||||||
|
const finishImport = async () => {
|
||||||
|
await importerAction(stepResults.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const importerAction = async (stepResults: any[]) => {
|
||||||
|
if (!importerModule.value) return
|
||||||
|
const result = await importerModule.value?.importer(stepResults as any)()
|
||||||
|
if (E.isLeft(result)) {
|
||||||
|
failedImport()
|
||||||
|
console.error("error", result.left)
|
||||||
|
} else if (E.isRight(result)) {
|
||||||
|
if (props.collectionsType.type === "team-collections") {
|
||||||
|
importToTeams(result.right)
|
||||||
|
fileImported()
|
||||||
|
} else {
|
||||||
|
appendRESTCollections(result.right)
|
||||||
|
fileImported()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasFile = ref(false)
|
||||||
|
const hasGist = ref(false)
|
||||||
|
|
||||||
|
watch(inputChooseGistToImportFrom, (v) => {
|
||||||
stepResults.value = []
|
stepResults.value = []
|
||||||
if (url === "") {
|
if (v === "") {
|
||||||
hasGist.value = false
|
hasGist.value = false
|
||||||
} else {
|
} else {
|
||||||
hasGist.value = true
|
hasGist.value = true
|
||||||
@@ -262,49 +385,17 @@ watch(inputChooseGistToImportFrom, (url) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const myCollections = useReadonlyStream(restCollections$, [])
|
|
||||||
const currentUser = useReadonlyStream(
|
|
||||||
platform.auth.getCurrentUserStream(),
|
|
||||||
platform.auth.getCurrentUser()
|
|
||||||
)
|
|
||||||
|
|
||||||
const importerAction = async (stepResults: StepReturnValue[]) => {
|
|
||||||
if (!importerModule.value) return
|
|
||||||
|
|
||||||
pipe(
|
|
||||||
await importerModule.value.importer(stepResults as any)(),
|
|
||||||
E.match(
|
|
||||||
(err) => {
|
|
||||||
failedImport()
|
|
||||||
console.error("error", err)
|
|
||||||
},
|
|
||||||
(result) => {
|
|
||||||
if (props.collectionsType === "team-collections") {
|
|
||||||
emit("import-to-teams", result)
|
|
||||||
} else {
|
|
||||||
appendRESTCollections(result)
|
|
||||||
fileImported()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const finishImport = async () => {
|
|
||||||
await importerAction(stepResults.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
const onFileChange = () => {
|
const onFileChange = () => {
|
||||||
stepResults.value = []
|
stepResults.value = []
|
||||||
|
if (!inputChooseFileToImportFrom.value[0]) {
|
||||||
const inputFileToImport = inputChooseFileToImportFrom.value[0]
|
|
||||||
|
|
||||||
if (!inputFileToImport) {
|
|
||||||
hasFile.value = false
|
hasFile.value = false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!inputFileToImport.files || inputFileToImport.files.length === 0) {
|
if (
|
||||||
|
!inputChooseFileToImportFrom.value[0].files ||
|
||||||
|
inputChooseFileToImportFrom.value[0].files.length === 0
|
||||||
|
) {
|
||||||
inputChooseFileToImportFrom.value[0].value = ""
|
inputChooseFileToImportFrom.value[0].value = ""
|
||||||
hasFile.value = false
|
hasFile.value = false
|
||||||
toast.show(t("action.choose_file").toString())
|
toast.show(t("action.choose_file").toString())
|
||||||
@@ -312,7 +403,6 @@ const onFileChange = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const reader = new FileReader()
|
const reader = new FileReader()
|
||||||
|
|
||||||
reader.onload = ({ target }) => {
|
reader.onload = ({ target }) => {
|
||||||
const content = target!.result as string | null
|
const content = target!.result as string | null
|
||||||
if (!content) {
|
if (!content) {
|
||||||
@@ -324,29 +414,20 @@ const onFileChange = () => {
|
|||||||
stepResults.value.push(content)
|
stepResults.value.push(content)
|
||||||
hasFile.value = !!content?.length
|
hasFile.value = !!content?.length
|
||||||
}
|
}
|
||||||
|
reader.readAsText(inputChooseFileToImportFrom.value[0].files[0])
|
||||||
reader.readAsText(inputFileToImport.files[0])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const fileImported = () => {
|
const enableImportButton = computed(
|
||||||
toast.success(t("state.file_imported").toString())
|
() => !(stepResults.value.length === importerSteps.value?.length)
|
||||||
hideModal()
|
)
|
||||||
}
|
|
||||||
const failedImport = () => {
|
|
||||||
toast.error(t("import.failed").toString())
|
|
||||||
}
|
|
||||||
const hideModal = () => {
|
|
||||||
resetImport()
|
|
||||||
emit("hide-modal")
|
|
||||||
}
|
|
||||||
|
|
||||||
const resetImport = () => {
|
const resetImport = () => {
|
||||||
importerType.value = null
|
importerType.value = null
|
||||||
hasFile.value = false
|
|
||||||
hasGist.value = false
|
|
||||||
stepResults.value = []
|
stepResults.value = []
|
||||||
inputChooseFileToImportFrom.value = ""
|
inputChooseFileToImportFrom.value = ""
|
||||||
|
hasFile.value = false
|
||||||
inputChooseGistToImportFrom.value = ""
|
inputChooseGistToImportFrom.value = ""
|
||||||
|
hasGist.value = false
|
||||||
mySelectedCollectionID.value = undefined
|
mySelectedCollectionID.value = undefined
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,613 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="flex flex-col flex-1">
|
|
||||||
<div
|
|
||||||
class="sticky z-10 flex justify-between flex-1 border-b bg-primary border-dividerLight"
|
|
||||||
:style="
|
|
||||||
saveRequest
|
|
||||||
? 'top: calc(var(--upper-primary-sticky-fold) - var(--line-height-body))'
|
|
||||||
: 'top: var(--upper-primary-sticky-fold)'
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<ButtonSecondary
|
|
||||||
:icon="IconPlus"
|
|
||||||
:label="t('action.new')"
|
|
||||||
class="!rounded-none"
|
|
||||||
@click="emit('display-modal-add')"
|
|
||||||
/>
|
|
||||||
<span class="flex">
|
|
||||||
<ButtonSecondary
|
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
|
||||||
to="https://docs.hoppscotch.io/features/collections"
|
|
||||||
blank
|
|
||||||
:title="t('app.wiki')"
|
|
||||||
:icon="IconHelpCircle"
|
|
||||||
/>
|
|
||||||
<ButtonSecondary
|
|
||||||
v-if="!saveRequest"
|
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
|
||||||
:icon="IconArchive"
|
|
||||||
:title="t('modal.import_export')"
|
|
||||||
@click="emit('display-modal-import-export')"
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="flex flex-col flex-1">
|
|
||||||
<SmartTree :adapter="myAdapter">
|
|
||||||
<template #content="{ node, toggleChildren, isOpen }">
|
|
||||||
<CollectionsCollection
|
|
||||||
v-if="node.data.type === 'collections'"
|
|
||||||
:data="node.data.data.data"
|
|
||||||
:collections-type="collectionsType.type"
|
|
||||||
:is-open="isOpen"
|
|
||||||
:is-selected="
|
|
||||||
isSelected({
|
|
||||||
collectionIndex: parseInt(node.id),
|
|
||||||
})
|
|
||||||
"
|
|
||||||
folder-type="collection"
|
|
||||||
@add-request="
|
|
||||||
node.data.type === 'collections' &&
|
|
||||||
emit('add-request', {
|
|
||||||
path: node.id,
|
|
||||||
folder: node.data.data.data,
|
|
||||||
})
|
|
||||||
"
|
|
||||||
@add-folder="
|
|
||||||
node.data.type === 'collections' &&
|
|
||||||
emit('add-folder', {
|
|
||||||
path: node.id,
|
|
||||||
folder: node.data.data.data,
|
|
||||||
})
|
|
||||||
"
|
|
||||||
@edit-collection="
|
|
||||||
node.data.type === 'collections' &&
|
|
||||||
emit('edit-collection', {
|
|
||||||
collectionIndex: node.id,
|
|
||||||
collection: node.data.data.data,
|
|
||||||
})
|
|
||||||
"
|
|
||||||
@export-data="
|
|
||||||
node.data.type === 'collections' &&
|
|
||||||
emit('export-data', node.data.data.data)
|
|
||||||
"
|
|
||||||
@remove-collection="emit('remove-collection', node.id)"
|
|
||||||
@drop-event="dropEvent($event, node.id)"
|
|
||||||
@toggle-children="
|
|
||||||
() => {
|
|
||||||
toggleChildren(),
|
|
||||||
saveRequest &&
|
|
||||||
emit('select', {
|
|
||||||
pickedType: 'my-collection',
|
|
||||||
collectionIndex: parseInt(node.id),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
<CollectionsCollection
|
|
||||||
v-if="node.data.type === 'folders'"
|
|
||||||
:data="node.data.data.data"
|
|
||||||
:collections-type="collectionsType.type"
|
|
||||||
:is-open="isOpen"
|
|
||||||
:is-selected="
|
|
||||||
isSelected({
|
|
||||||
folderPath: node.id,
|
|
||||||
})
|
|
||||||
"
|
|
||||||
folder-type="folder"
|
|
||||||
@add-request="
|
|
||||||
node.data.type === 'folders' &&
|
|
||||||
emit('add-request', {
|
|
||||||
path: node.id,
|
|
||||||
folder: node.data.data.data,
|
|
||||||
})
|
|
||||||
"
|
|
||||||
@add-folder="
|
|
||||||
node.data.type === 'folders' &&
|
|
||||||
emit('add-folder', {
|
|
||||||
path: node.id,
|
|
||||||
folder: node.data.data.data,
|
|
||||||
})
|
|
||||||
"
|
|
||||||
@edit-collection="
|
|
||||||
node.data.type === 'folders' &&
|
|
||||||
emit('edit-folder', {
|
|
||||||
folderPath: node.id,
|
|
||||||
folder: node.data.data.data,
|
|
||||||
})
|
|
||||||
"
|
|
||||||
@export-data="
|
|
||||||
node.data.type === 'folders' &&
|
|
||||||
emit('export-data', node.data.data.data)
|
|
||||||
"
|
|
||||||
@remove-collection="emit('remove-folder', node.id)"
|
|
||||||
@drop-event="dropEvent($event, node.id)"
|
|
||||||
@toggle-children="
|
|
||||||
() => {
|
|
||||||
toggleChildren(),
|
|
||||||
saveRequest &&
|
|
||||||
emit('select', {
|
|
||||||
pickedType: 'my-folder',
|
|
||||||
folderPath: node.id,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
<CollectionsRequest
|
|
||||||
v-if="node.data.type === 'requests'"
|
|
||||||
:request="node.data.data.data"
|
|
||||||
:collections-type="collectionsType.type"
|
|
||||||
:save-request="saveRequest"
|
|
||||||
:is-active="
|
|
||||||
isActiveRequest(
|
|
||||||
node.data.data.parentIndex,
|
|
||||||
parseInt(pathToIndex(node.id))
|
|
||||||
)
|
|
||||||
"
|
|
||||||
:is-selected="
|
|
||||||
isSelected({
|
|
||||||
folderPath: node.data.data.parentIndex,
|
|
||||||
requestIndex: parseInt(pathToIndex(node.id)),
|
|
||||||
})
|
|
||||||
"
|
|
||||||
@edit-request="
|
|
||||||
node.data.type === 'requests' &&
|
|
||||||
emit('edit-request', {
|
|
||||||
folderPath: node.data.data.parentIndex,
|
|
||||||
requestIndex: pathToIndex(node.id),
|
|
||||||
request: node.data.data.data,
|
|
||||||
})
|
|
||||||
"
|
|
||||||
@duplicate-request="
|
|
||||||
node.data.type === 'requests' &&
|
|
||||||
emit('duplicate-request', {
|
|
||||||
folderPath: node.data.data.parentIndex,
|
|
||||||
request: node.data.data.data,
|
|
||||||
})
|
|
||||||
"
|
|
||||||
@remove-request="
|
|
||||||
node.data.type === 'requests' &&
|
|
||||||
emit('remove-request', {
|
|
||||||
folderPath: node.data.data.parentIndex,
|
|
||||||
requestIndex: pathToIndex(node.id),
|
|
||||||
})
|
|
||||||
"
|
|
||||||
@select-request="
|
|
||||||
node.data.type === 'requests' &&
|
|
||||||
selectRequest({
|
|
||||||
request: node.data.data.data,
|
|
||||||
folderPath: node.data.data.parentIndex,
|
|
||||||
requestIndex: pathToIndex(node.id),
|
|
||||||
})
|
|
||||||
"
|
|
||||||
@drag-request="
|
|
||||||
dragRequest($event, {
|
|
||||||
folderPath: node.data.data.parentIndex,
|
|
||||||
requestIndex: pathToIndex(node.id),
|
|
||||||
})
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<template #emptyNode="{ node }">
|
|
||||||
<div
|
|
||||||
v-if="filterText.length !== 0 && filteredCollections.length === 0"
|
|
||||||
class="flex flex-col items-center justify-center p-4 text-secondaryLight"
|
|
||||||
>
|
|
||||||
<icon-lucide-search class="pb-2 opacity-75 svg-icons" />
|
|
||||||
<span class="my-2 text-center">
|
|
||||||
{{ t("state.nothing_found") }} "{{ filterText }}"
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div v-else-if="node === null">
|
|
||||||
<div
|
|
||||||
class="flex flex-col items-center justify-center p-4 text-secondaryLight"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
:src="`/images/states/${colorMode.value}/pack.svg`"
|
|
||||||
loading="lazy"
|
|
||||||
class="inline-flex flex-col object-contain object-center w-16 h-16 mb-4"
|
|
||||||
:alt="`${t('empty.collections')}`"
|
|
||||||
/>
|
|
||||||
<span class="pb-4 text-center">
|
|
||||||
{{ t("empty.collections") }}
|
|
||||||
</span>
|
|
||||||
<ButtonSecondary
|
|
||||||
:label="t('add.new')"
|
|
||||||
filled
|
|
||||||
outline
|
|
||||||
@click="emit('display-modal-add')"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-else-if="node.data.type === 'collections'"
|
|
||||||
class="flex flex-col items-center justify-center p-4 text-secondaryLight"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
:src="`/images/states/${colorMode.value}/pack.svg`"
|
|
||||||
loading="lazy"
|
|
||||||
class="inline-flex flex-col object-contain object-center w-16 h-16 mb-4"
|
|
||||||
:alt="`${t('empty.collection')}`"
|
|
||||||
/>
|
|
||||||
<span class="pb-4 text-center">
|
|
||||||
{{ t("empty.collection") }}
|
|
||||||
</span>
|
|
||||||
<ButtonSecondary
|
|
||||||
:label="t('add.new')"
|
|
||||||
filled
|
|
||||||
outline
|
|
||||||
@click="
|
|
||||||
node.data.type === 'collections' &&
|
|
||||||
emit('add-folder', {
|
|
||||||
path: node.id,
|
|
||||||
folder: node.data.data.data,
|
|
||||||
})
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-else-if="node.data.type === 'folders'"
|
|
||||||
class="flex flex-col items-center justify-center p-4 text-secondaryLight"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
:src="`/images/states/${colorMode.value}/pack.svg`"
|
|
||||||
loading="lazy"
|
|
||||||
class="inline-flex flex-col object-contain object-center w-16 h-16 mb-4"
|
|
||||||
:alt="`${t('empty.folder')}`"
|
|
||||||
/>
|
|
||||||
<span class="text-center">
|
|
||||||
{{ t("empty.folder") }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</SmartTree>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import IconArchive from "~icons/lucide/archive"
|
|
||||||
import IconPlus from "~icons/lucide/plus"
|
|
||||||
import IconHelpCircle from "~icons/lucide/help-circle"
|
|
||||||
import { HoppCollection, HoppRESTRequest } from "@hoppscotch/data"
|
|
||||||
import { computed, PropType, Ref, toRef } from "vue"
|
|
||||||
import { GetMyTeamsQuery } from "~/helpers/backend/graphql"
|
|
||||||
import { ChildrenResult, SmartTreeAdapter } from "~/helpers/treeAdapter"
|
|
||||||
import { useI18n } from "@composables/i18n"
|
|
||||||
import { useColorMode } from "@composables/theming"
|
|
||||||
import { useReadonlyStream } from "~/composables/stream"
|
|
||||||
import { restSaveContext$ } from "~/newstore/RESTSession"
|
|
||||||
import { pipe } from "fp-ts/function"
|
|
||||||
import * as O from "fp-ts/Option"
|
|
||||||
import { Picked } from "~/helpers/types/HoppPicked.js"
|
|
||||||
|
|
||||||
export type Collection = {
|
|
||||||
type: "collections"
|
|
||||||
data: {
|
|
||||||
parentIndex: null
|
|
||||||
data: HoppCollection<HoppRESTRequest>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Folder = {
|
|
||||||
type: "folders"
|
|
||||||
data: {
|
|
||||||
parentIndex: string
|
|
||||||
data: HoppCollection<HoppRESTRequest>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Requests = {
|
|
||||||
type: "requests"
|
|
||||||
data: {
|
|
||||||
parentIndex: string
|
|
||||||
data: HoppRESTRequest
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const t = useI18n()
|
|
||||||
const colorMode = useColorMode()
|
|
||||||
|
|
||||||
type SelectedTeam = GetMyTeamsQuery["myTeams"][number] | undefined
|
|
||||||
|
|
||||||
type CollectionType =
|
|
||||||
| {
|
|
||||||
type: "team-collections"
|
|
||||||
selectedTeam: SelectedTeam
|
|
||||||
}
|
|
||||||
| { type: "my-collections"; selectedTeam: undefined }
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
filteredCollections: {
|
|
||||||
type: Array as PropType<HoppCollection<HoppRESTRequest>[]>,
|
|
||||||
default: () => [],
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
collectionsType: {
|
|
||||||
type: Object as PropType<CollectionType>,
|
|
||||||
default: () => ({ type: "my-collections", selectedTeam: undefined }),
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
filterText: {
|
|
||||||
type: String as PropType<string>,
|
|
||||||
default: "",
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
saveRequest: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
picked: {
|
|
||||||
type: Object as PropType<Picked | null>,
|
|
||||||
default: null,
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const emit = defineEmits<{
|
|
||||||
(event: "display-modal-add"): void
|
|
||||||
(
|
|
||||||
event: "add-request",
|
|
||||||
payload: {
|
|
||||||
path: string
|
|
||||||
folder: HoppCollection<HoppRESTRequest>
|
|
||||||
}
|
|
||||||
): void
|
|
||||||
(
|
|
||||||
event: "add-folder",
|
|
||||||
payload: {
|
|
||||||
path: string
|
|
||||||
folder: HoppCollection<HoppRESTRequest>
|
|
||||||
}
|
|
||||||
): void
|
|
||||||
(
|
|
||||||
event: "edit-collection",
|
|
||||||
payload: {
|
|
||||||
collectionIndex: string
|
|
||||||
collection: HoppCollection<HoppRESTRequest>
|
|
||||||
}
|
|
||||||
): void
|
|
||||||
(
|
|
||||||
event: "edit-folder",
|
|
||||||
payload: {
|
|
||||||
folderPath: string
|
|
||||||
folder: HoppCollection<HoppRESTRequest>
|
|
||||||
}
|
|
||||||
): void
|
|
||||||
(
|
|
||||||
event: "edit-request",
|
|
||||||
payload: {
|
|
||||||
folderPath: string
|
|
||||||
requestIndex: string
|
|
||||||
request: HoppRESTRequest
|
|
||||||
}
|
|
||||||
): void
|
|
||||||
(
|
|
||||||
event: "duplicate-request",
|
|
||||||
payload: {
|
|
||||||
folderPath: string
|
|
||||||
request: HoppRESTRequest
|
|
||||||
}
|
|
||||||
): void
|
|
||||||
(event: "export-data", payload: HoppCollection<HoppRESTRequest>): void
|
|
||||||
(event: "remove-collection", payload: string): void
|
|
||||||
(event: "remove-folder", payload: string): void
|
|
||||||
(
|
|
||||||
event: "remove-request",
|
|
||||||
payload: {
|
|
||||||
folderPath: string | null
|
|
||||||
requestIndex: string
|
|
||||||
}
|
|
||||||
): void
|
|
||||||
(
|
|
||||||
event: "select-request",
|
|
||||||
payload: {
|
|
||||||
request: HoppRESTRequest
|
|
||||||
folderPath: string
|
|
||||||
requestIndex: string
|
|
||||||
isActive: boolean
|
|
||||||
}
|
|
||||||
): void
|
|
||||||
(
|
|
||||||
event: "drop-request",
|
|
||||||
payload: {
|
|
||||||
folderPath: string
|
|
||||||
requestIndex: string
|
|
||||||
collectionIndex: string
|
|
||||||
}
|
|
||||||
): void
|
|
||||||
(event: "select", payload: Picked | null): void
|
|
||||||
(event: "display-modal-import-export"): void
|
|
||||||
}>()
|
|
||||||
|
|
||||||
const refFilterCollection = toRef(props, "filteredCollections")
|
|
||||||
|
|
||||||
const pathToIndex = computed(() => {
|
|
||||||
return (path: string) => {
|
|
||||||
const pathArr = path.split("/")
|
|
||||||
return pathArr[pathArr.length - 1]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const isSelected = computed(() => {
|
|
||||||
return ({
|
|
||||||
collectionIndex,
|
|
||||||
folderPath,
|
|
||||||
requestIndex,
|
|
||||||
}: {
|
|
||||||
collectionIndex?: number | undefined
|
|
||||||
folderPath?: string | undefined
|
|
||||||
requestIndex?: number | undefined
|
|
||||||
}) => {
|
|
||||||
if (collectionIndex !== undefined) {
|
|
||||||
return (
|
|
||||||
props.picked &&
|
|
||||||
props.picked.pickedType === "my-collection" &&
|
|
||||||
props.picked.collectionIndex === collectionIndex
|
|
||||||
)
|
|
||||||
} else if (requestIndex !== undefined && folderPath !== undefined) {
|
|
||||||
return (
|
|
||||||
props.picked &&
|
|
||||||
props.picked.pickedType === "my-request" &&
|
|
||||||
props.picked.folderPath === folderPath &&
|
|
||||||
props.picked.requestIndex === requestIndex
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
props.picked &&
|
|
||||||
props.picked.pickedType === "my-folder" &&
|
|
||||||
props.picked.folderPath === folderPath
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const active = useReadonlyStream(restSaveContext$, null)
|
|
||||||
|
|
||||||
const isActiveRequest = computed(() => {
|
|
||||||
return (folderPath: string, requestIndex: number) => {
|
|
||||||
return pipe(
|
|
||||||
active.value,
|
|
||||||
O.fromNullable,
|
|
||||||
O.filter(
|
|
||||||
(active) =>
|
|
||||||
active.originLocation === "user-collection" &&
|
|
||||||
active.folderPath === folderPath &&
|
|
||||||
active.requestIndex === requestIndex
|
|
||||||
),
|
|
||||||
O.isSome
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const selectRequest = (data: {
|
|
||||||
request: HoppRESTRequest
|
|
||||||
folderPath: string
|
|
||||||
requestIndex: string
|
|
||||||
}) => {
|
|
||||||
const { request, folderPath, requestIndex } = data
|
|
||||||
if (props.saveRequest) {
|
|
||||||
emit("select", {
|
|
||||||
pickedType: "my-request",
|
|
||||||
folderPath: folderPath,
|
|
||||||
requestIndex: parseInt(requestIndex),
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
emit("select-request", {
|
|
||||||
request,
|
|
||||||
folderPath,
|
|
||||||
requestIndex,
|
|
||||||
isActive: isActiveRequest.value(folderPath, parseInt(requestIndex)),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const dragRequest = (
|
|
||||||
dataTransfer: DataTransfer,
|
|
||||||
{
|
|
||||||
folderPath,
|
|
||||||
requestIndex,
|
|
||||||
}: { folderPath: string | null; requestIndex: string }
|
|
||||||
) => {
|
|
||||||
if (!folderPath) return
|
|
||||||
dataTransfer.setData("folderPath", folderPath)
|
|
||||||
dataTransfer.setData("requestIndex", requestIndex)
|
|
||||||
}
|
|
||||||
|
|
||||||
const dropEvent = (dataTransfer: DataTransfer, collectionIndex: string) => {
|
|
||||||
const folderPath = dataTransfer.getData("folderPath")
|
|
||||||
const requestIndex = dataTransfer.getData("requestIndex")
|
|
||||||
emit("drop-request", {
|
|
||||||
folderPath,
|
|
||||||
requestIndex,
|
|
||||||
collectionIndex,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
type MyCollectionNode = Collection | Folder | Requests
|
|
||||||
|
|
||||||
class MyCollectionsAdapter implements SmartTreeAdapter<MyCollectionNode> {
|
|
||||||
constructor(public data: Ref<HoppCollection<HoppRESTRequest>[]>) {}
|
|
||||||
|
|
||||||
navigateToFolderWithIndexPath(
|
|
||||||
collections: HoppCollection<HoppRESTRequest>[],
|
|
||||||
indexPaths: number[]
|
|
||||||
) {
|
|
||||||
if (indexPaths.length === 0) return null
|
|
||||||
|
|
||||||
let target = collections[indexPaths.shift() as number]
|
|
||||||
|
|
||||||
while (indexPaths.length > 0)
|
|
||||||
target = target.folders[indexPaths.shift() as number]
|
|
||||||
|
|
||||||
return target !== undefined ? target : null
|
|
||||||
}
|
|
||||||
|
|
||||||
getChildren(id: string | null): Ref<ChildrenResult<MyCollectionNode>> {
|
|
||||||
return computed(() => {
|
|
||||||
if (id === null) {
|
|
||||||
const data = this.data.value.map((item, index) => ({
|
|
||||||
id: index.toString(),
|
|
||||||
data: {
|
|
||||||
type: "collections",
|
|
||||||
data: {
|
|
||||||
parentIndex: null,
|
|
||||||
data: item,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}))
|
|
||||||
return {
|
|
||||||
status: "loaded",
|
|
||||||
data: data,
|
|
||||||
} as ChildrenResult<Collection>
|
|
||||||
}
|
|
||||||
|
|
||||||
const indexPath = id.split("/").map((x) => parseInt(x))
|
|
||||||
|
|
||||||
const item = this.navigateToFolderWithIndexPath(
|
|
||||||
this.data.value,
|
|
||||||
indexPath
|
|
||||||
)
|
|
||||||
|
|
||||||
if (item) {
|
|
||||||
const data = [
|
|
||||||
...item.folders.map((item, index) => ({
|
|
||||||
id: `${id}/${index}`,
|
|
||||||
data: {
|
|
||||||
type: "folders",
|
|
||||||
data: {
|
|
||||||
parentIndex: id,
|
|
||||||
data: item,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})),
|
|
||||||
...item.requests.map((item, index) => ({
|
|
||||||
id: `${id}/${index}`,
|
|
||||||
data: {
|
|
||||||
type: "requests",
|
|
||||||
data: {
|
|
||||||
parentIndex: id,
|
|
||||||
data: item,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})),
|
|
||||||
]
|
|
||||||
|
|
||||||
return {
|
|
||||||
status: "loaded",
|
|
||||||
data: data,
|
|
||||||
} as ChildrenResult<Folder | Requests>
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
status: "loaded",
|
|
||||||
data: [],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const myAdapter: SmartTreeAdapter<MyCollectionNode> = new MyCollectionsAdapter(
|
|
||||||
refFilterCollection
|
|
||||||
)
|
|
||||||
</script>
|
|
||||||
@@ -1,235 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="flex flex-col" :class="[{ 'bg-primaryLight': dragging }]">
|
|
||||||
<div
|
|
||||||
class="flex items-stretch group"
|
|
||||||
draggable="true"
|
|
||||||
@dragstart="dragStart"
|
|
||||||
@dragover.stop
|
|
||||||
@dragleave="dragging = false"
|
|
||||||
@dragend="dragging = false"
|
|
||||||
@contextmenu.prevent="options?.tippy.show()"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
class="flex items-center justify-center w-16 px-2 truncate cursor-pointer"
|
|
||||||
:class="requestLabelColor"
|
|
||||||
@click="selectRequest()"
|
|
||||||
>
|
|
||||||
<component
|
|
||||||
:is="IconCheckCircle"
|
|
||||||
v-if="isSelected"
|
|
||||||
class="svg-icons"
|
|
||||||
:class="{ 'text-accent': isSelected }"
|
|
||||||
/>
|
|
||||||
<span v-else class="font-semibold truncate text-tiny">
|
|
||||||
{{ request.method }}
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
<span
|
|
||||||
class="flex items-center flex-1 min-w-0 py-2 pr-2 cursor-pointer transition group-hover:text-secondaryDark"
|
|
||||||
@click="selectRequest()"
|
|
||||||
>
|
|
||||||
<span class="truncate" :class="{ 'text-accent': isSelected }">
|
|
||||||
{{ request.name }}
|
|
||||||
</span>
|
|
||||||
<span
|
|
||||||
v-if="isActive"
|
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
|
||||||
class="relative h-1.5 w-1.5 flex flex-shrink-0 mx-3"
|
|
||||||
:title="`${t('collection.request_in_use')}`"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
class="absolute inline-flex flex-shrink-0 w-full h-full bg-green-500 rounded-full opacity-75 animate-ping"
|
|
||||||
>
|
|
||||||
</span>
|
|
||||||
<span
|
|
||||||
class="relative inline-flex flex-shrink-0 rounded-full h-1.5 w-1.5 bg-green-500"
|
|
||||||
></span>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
<div v-if="!hasNoTeamAccess" class="flex">
|
|
||||||
<ButtonSecondary
|
|
||||||
v-if="!saveRequest"
|
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
|
||||||
:icon="IconRotateCCW"
|
|
||||||
:title="t('action.restore')"
|
|
||||||
class="hidden group-hover:inline-flex"
|
|
||||||
@click="selectRequest()"
|
|
||||||
/>
|
|
||||||
<span>
|
|
||||||
<tippy
|
|
||||||
ref="options"
|
|
||||||
interactive
|
|
||||||
trigger="click"
|
|
||||||
theme="popover"
|
|
||||||
:on-shown="() => tippyActions!.focus()"
|
|
||||||
>
|
|
||||||
<ButtonSecondary
|
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
|
||||||
:title="t('action.more')"
|
|
||||||
:icon="IconMoreVertical"
|
|
||||||
/>
|
|
||||||
<template #content="{ hide }">
|
|
||||||
<div
|
|
||||||
ref="tippyActions"
|
|
||||||
class="flex flex-col focus:outline-none"
|
|
||||||
tabindex="0"
|
|
||||||
@keyup.e="edit?.$el.click()"
|
|
||||||
@keyup.d="duplicate?.$el.click()"
|
|
||||||
@keyup.delete="deleteAction?.$el.click()"
|
|
||||||
@keyup.escape="hide()"
|
|
||||||
>
|
|
||||||
<SmartItem
|
|
||||||
ref="edit"
|
|
||||||
:icon="IconEdit"
|
|
||||||
:label="t('action.edit')"
|
|
||||||
:shortcut="['E']"
|
|
||||||
@click="
|
|
||||||
() => {
|
|
||||||
emit('edit-request')
|
|
||||||
hide()
|
|
||||||
}
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
<SmartItem
|
|
||||||
ref="duplicate"
|
|
||||||
:icon="IconCopy"
|
|
||||||
:label="t('action.duplicate')"
|
|
||||||
:loading="duplicateLoading"
|
|
||||||
:shortcut="['D']"
|
|
||||||
@click="
|
|
||||||
() => {
|
|
||||||
emit('duplicate-request'),
|
|
||||||
collectionsType === 'my-collections' ? hide() : null
|
|
||||||
}
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
<SmartItem
|
|
||||||
ref="deleteAction"
|
|
||||||
:icon="IconTrash2"
|
|
||||||
:label="t('action.delete')"
|
|
||||||
:shortcut="['⌫']"
|
|
||||||
@click="
|
|
||||||
() => {
|
|
||||||
emit('remove-request')
|
|
||||||
hide()
|
|
||||||
}
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</tippy>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import IconCheckCircle from "~icons/lucide/check-circle"
|
|
||||||
import IconMoreVertical from "~icons/lucide/more-vertical"
|
|
||||||
import IconEdit from "~icons/lucide/edit"
|
|
||||||
import IconCopy from "~icons/lucide/copy"
|
|
||||||
import IconTrash2 from "~icons/lucide/trash-2"
|
|
||||||
import IconRotateCCW from "~icons/lucide/rotate-ccw"
|
|
||||||
import { ref, PropType, watch, computed } from "vue"
|
|
||||||
import { HoppRESTRequest } from "@hoppscotch/data"
|
|
||||||
import { useI18n } from "@composables/i18n"
|
|
||||||
import { TippyComponent } from "vue-tippy"
|
|
||||||
import { pipe } from "fp-ts/function"
|
|
||||||
import * as RR from "fp-ts/ReadonlyRecord"
|
|
||||||
import * as O from "fp-ts/Option"
|
|
||||||
|
|
||||||
type CollectionType = "my-collections" | "team-collections"
|
|
||||||
|
|
||||||
const t = useI18n()
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
request: {
|
|
||||||
type: Object as PropType<HoppRESTRequest>,
|
|
||||||
default: () => ({}),
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
collectionsType: {
|
|
||||||
type: String as PropType<CollectionType>,
|
|
||||||
default: "my-collections",
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
duplicateLoading: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
saveRequest: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
isActive: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
hasNoTeamAccess: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
isSelected: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const emit = defineEmits<{
|
|
||||||
(event: "edit-request"): void
|
|
||||||
(event: "duplicate-request"): void
|
|
||||||
(event: "remove-request"): void
|
|
||||||
(event: "select-request"): void
|
|
||||||
(event: "drag-request", payload: DataTransfer): void
|
|
||||||
}>()
|
|
||||||
|
|
||||||
const tippyActions = ref<TippyComponent | null>(null)
|
|
||||||
const edit = ref<HTMLButtonElement | null>(null)
|
|
||||||
const deleteAction = ref<HTMLButtonElement | null>(null)
|
|
||||||
const options = ref<TippyComponent | null>(null)
|
|
||||||
const duplicate = ref<HTMLButtonElement | null>(null)
|
|
||||||
|
|
||||||
const dragging = ref(false)
|
|
||||||
|
|
||||||
const requestMethodLabels = {
|
|
||||||
get: "text-green-500",
|
|
||||||
post: "text-yellow-500",
|
|
||||||
put: "text-blue-500",
|
|
||||||
delete: "text-red-500",
|
|
||||||
default: "text-gray-500",
|
|
||||||
} as const
|
|
||||||
|
|
||||||
const requestLabelColor = computed(() =>
|
|
||||||
pipe(
|
|
||||||
requestMethodLabels,
|
|
||||||
RR.lookup(props.request.method.toLowerCase()),
|
|
||||||
O.getOrElseW(() => requestMethodLabels.default)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => props.duplicateLoading,
|
|
||||||
(val) => {
|
|
||||||
if (!val) {
|
|
||||||
options.value!.tippy.hide()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
const selectRequest = () => {
|
|
||||||
emit("select-request")
|
|
||||||
}
|
|
||||||
|
|
||||||
const dragStart = ({ dataTransfer }: DragEvent) => {
|
|
||||||
if (dataTransfer) {
|
|
||||||
dragging.value = !dragging.value
|
|
||||||
emit("drag-request", dataTransfer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -27,8 +27,9 @@
|
|||||||
</label>
|
</label>
|
||||||
<CollectionsGraphql
|
<CollectionsGraphql
|
||||||
v-if="mode === 'graphql'"
|
v-if="mode === 'graphql'"
|
||||||
|
:show-coll-actions="false"
|
||||||
:picked="picked"
|
:picked="picked"
|
||||||
:save-request="true"
|
:saving-mode="true"
|
||||||
@select="onSelect"
|
@select="onSelect"
|
||||||
/>
|
/>
|
||||||
<Collections
|
<Collections
|
||||||
@@ -36,8 +37,8 @@
|
|||||||
:picked="picked"
|
:picked="picked"
|
||||||
:save-request="true"
|
:save-request="true"
|
||||||
@select="onSelect"
|
@select="onSelect"
|
||||||
@update-team="updateTeam"
|
@update-collection="updateColl"
|
||||||
@update-collection-type="updateCollectionType"
|
@update-coll-type="onUpdateCollType"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -45,7 +46,6 @@
|
|||||||
<span class="flex space-x-2">
|
<span class="flex space-x-2">
|
||||||
<ButtonPrimary
|
<ButtonPrimary
|
||||||
:label="`${t('action.save')}`"
|
:label="`${t('action.save')}`"
|
||||||
:loading="modalLoadingState"
|
|
||||||
outline
|
outline
|
||||||
@click="saveRequestAs"
|
@click="saveRequestAs"
|
||||||
/>
|
/>
|
||||||
@@ -61,75 +61,99 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useI18n } from "@composables/i18n"
|
|
||||||
import { useToast } from "@composables/toast"
|
|
||||||
import {
|
|
||||||
HoppGQLRequest,
|
|
||||||
HoppRESTRequest,
|
|
||||||
isHoppRESTRequest,
|
|
||||||
} from "@hoppscotch/data"
|
|
||||||
import { pipe } from "fp-ts/function"
|
|
||||||
import * as TE from "fp-ts/TaskEither"
|
|
||||||
import { cloneDeep } from "lodash-es"
|
|
||||||
import { reactive, ref, watch } from "vue"
|
import { reactive, ref, watch } from "vue"
|
||||||
import { GetMyTeamsQuery } from "~/helpers/backend/graphql"
|
import * as E from "fp-ts/Either"
|
||||||
import {
|
import { HoppGQLRequest, isHoppRESTRequest } from "@hoppscotch/data"
|
||||||
createRequestInCollection,
|
import { cloneDeep } from "lodash-es"
|
||||||
updateTeamRequest,
|
|
||||||
} from "~/helpers/backend/mutations/TeamRequest"
|
|
||||||
import { Picked } from "~/helpers/types/HoppPicked"
|
|
||||||
import { getGQLSession, useGQLRequestName } from "~/newstore/GQLSession"
|
|
||||||
import {
|
|
||||||
getRESTRequest,
|
|
||||||
setRESTSaveContext,
|
|
||||||
useRESTRequestName,
|
|
||||||
} from "~/newstore/RESTSession"
|
|
||||||
import {
|
import {
|
||||||
editGraphqlRequest,
|
editGraphqlRequest,
|
||||||
editRESTRequest,
|
editRESTRequest,
|
||||||
saveGraphqlRequestAs,
|
saveGraphqlRequestAs,
|
||||||
saveRESTRequestAs,
|
saveRESTRequestAs,
|
||||||
} from "~/newstore/collections"
|
} from "~/newstore/collections"
|
||||||
import { GQLError } from "~/helpers/backend/GQLClient"
|
import { getGQLSession, useGQLRequestName } from "~/newstore/GQLSession"
|
||||||
|
import {
|
||||||
|
getRESTRequest,
|
||||||
|
setRESTSaveContext,
|
||||||
|
useRESTRequestName,
|
||||||
|
} from "~/newstore/RESTSession"
|
||||||
|
import { useI18n } from "@composables/i18n"
|
||||||
|
import { useToast } from "@composables/toast"
|
||||||
|
import { runMutation } from "~/helpers/backend/GQLClient"
|
||||||
|
import {
|
||||||
|
CreateRequestInCollectionDocument,
|
||||||
|
UpdateRequestDocument,
|
||||||
|
} from "~/helpers/backend/graphql"
|
||||||
|
|
||||||
const t = useI18n()
|
const t = useI18n()
|
||||||
const toast = useToast()
|
|
||||||
|
|
||||||
type SelectedTeam = GetMyTeamsQuery["myTeams"][number] | undefined
|
|
||||||
|
|
||||||
type CollectionType =
|
type CollectionType =
|
||||||
| {
|
| {
|
||||||
type: "team-collections"
|
type: "my-collections"
|
||||||
selectedTeam: SelectedTeam
|
}
|
||||||
|
| {
|
||||||
|
type: "team-collections"
|
||||||
|
// TODO: Figure this type out
|
||||||
|
selectedTeam: {
|
||||||
|
id: string
|
||||||
|
}
|
||||||
}
|
}
|
||||||
| { type: "my-collections"; selectedTeam: undefined }
|
|
||||||
|
|
||||||
const props = withDefaults(
|
type Picked =
|
||||||
defineProps<{
|
| {
|
||||||
show: boolean
|
pickedType: "my-request"
|
||||||
mode: "rest" | "graphql"
|
folderPath: string
|
||||||
}>(),
|
requestIndex: number
|
||||||
{
|
}
|
||||||
show: false,
|
| {
|
||||||
mode: "rest",
|
pickedType: "my-folder"
|
||||||
}
|
folderPath: string
|
||||||
)
|
}
|
||||||
|
| {
|
||||||
|
pickedType: "my-collection"
|
||||||
|
collectionIndex: number
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
pickedType: "teams-request"
|
||||||
|
requestID: string
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
pickedType: "teams-folder"
|
||||||
|
folderID: string
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
pickedType: "teams-collection"
|
||||||
|
collectionID: string
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
pickedType: "gql-my-request"
|
||||||
|
folderPath: string
|
||||||
|
requestIndex: number
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
pickedType: "gql-my-folder"
|
||||||
|
folderPath: string
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
pickedType: "gql-my-collection"
|
||||||
|
collectionIndex: number
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
mode: "rest" | "graphql"
|
||||||
|
show: boolean
|
||||||
|
}>()
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(
|
|
||||||
event: "edit-request",
|
|
||||||
payload: {
|
|
||||||
folderPath: string
|
|
||||||
requestIndex: string
|
|
||||||
request: HoppRESTRequest
|
|
||||||
}
|
|
||||||
): void
|
|
||||||
(e: "hide-modal"): void
|
(e: "hide-modal"): void
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const requestName = ref(
|
const toast = useToast()
|
||||||
|
|
||||||
|
// TODO: Use a better implementation with computed ?
|
||||||
|
// This implementation can't work across updates to mode prop (which won't happen tho)
|
||||||
|
const requestName =
|
||||||
props.mode === "rest" ? useRESTRequestName() : useGQLRequestName()
|
props.mode === "rest" ? useRESTRequestName() : useGQLRequestName()
|
||||||
)
|
|
||||||
|
|
||||||
const requestData = reactive({
|
const requestData = reactive({
|
||||||
name: requestName,
|
name: requestName,
|
||||||
@@ -140,13 +164,11 @@ const requestData = reactive({
|
|||||||
|
|
||||||
const collectionsType = ref<CollectionType>({
|
const collectionsType = ref<CollectionType>({
|
||||||
type: "my-collections",
|
type: "my-collections",
|
||||||
selectedTeam: undefined,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// TODO: Figure this type out
|
||||||
const picked = ref<Picked | null>(null)
|
const picked = ref<Picked | null>(null)
|
||||||
|
|
||||||
const modalLoadingState = ref(false)
|
|
||||||
|
|
||||||
// Resets
|
// Resets
|
||||||
watch(
|
watch(
|
||||||
() => requestData.collectionIndex,
|
() => requestData.collectionIndex,
|
||||||
@@ -162,18 +184,20 @@ watch(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
const updateTeam = (newTeam: SelectedTeam) => {
|
// All the methods
|
||||||
collectionsType.value.selectedTeam = newTeam
|
const onUpdateCollType = (newCollType: CollectionType) => {
|
||||||
|
collectionsType.value = newCollType
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateCollectionType = (type: CollectionType["type"]) => {
|
const onSelect = ({ picked: pickedVal }: { picked: Picked | null }) => {
|
||||||
collectionsType.value.type = type
|
|
||||||
}
|
|
||||||
|
|
||||||
const onSelect = (pickedVal: Picked | null) => {
|
|
||||||
picked.value = pickedVal
|
picked.value = pickedVal
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const hideModal = () => {
|
||||||
|
picked.value = null
|
||||||
|
emit("hide-modal")
|
||||||
|
}
|
||||||
|
|
||||||
const saveRequestAs = async () => {
|
const saveRequestAs = async () => {
|
||||||
if (!requestName.value) {
|
if (!requestName.value) {
|
||||||
toast.error(`${t("error.empty_req_name")}`)
|
toast.error(`${t("error.empty_req_name")}`)
|
||||||
@@ -184,25 +208,35 @@ const saveRequestAs = async () => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clone Deep because objects are shared by reference so updating
|
||||||
|
// just one bit will update other referenced shared instances
|
||||||
const requestUpdated =
|
const requestUpdated =
|
||||||
props.mode === "rest"
|
props.mode === "rest"
|
||||||
? cloneDeep(getRESTRequest())
|
? cloneDeep(getRESTRequest())
|
||||||
: cloneDeep(getGQLSession().request)
|
: cloneDeep(getGQLSession().request)
|
||||||
|
|
||||||
if (picked.value.pickedType === "my-collection") {
|
// // Filter out all REST file inputs
|
||||||
|
// if (this.mode === "rest" && requestUpdated.bodyParams) {
|
||||||
|
// requestUpdated.bodyParams = requestUpdated.bodyParams.map((param) =>
|
||||||
|
// param?.value?.[0] instanceof File ? { ...param, value: "" } : param
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
|
||||||
|
if (picked.value.pickedType === "my-request") {
|
||||||
if (!isHoppRESTRequest(requestUpdated))
|
if (!isHoppRESTRequest(requestUpdated))
|
||||||
throw new Error("requestUpdated is not a REST Request")
|
throw new Error("requestUpdated is not a REST Request")
|
||||||
|
|
||||||
const insertionIndex = saveRESTRequestAs(
|
editRESTRequest(
|
||||||
`${picked.value.collectionIndex}`,
|
picked.value.folderPath,
|
||||||
|
picked.value.requestIndex,
|
||||||
requestUpdated
|
requestUpdated
|
||||||
)
|
)
|
||||||
|
|
||||||
setRESTSaveContext({
|
setRESTSaveContext({
|
||||||
originLocation: "user-collection",
|
originLocation: "user-collection",
|
||||||
folderPath: `${picked.value.collectionIndex}`,
|
folderPath: picked.value.folderPath,
|
||||||
requestIndex: insertionIndex,
|
requestIndex: picked.value.requestIndex,
|
||||||
req: requestUpdated,
|
req: cloneDeep(requestUpdated),
|
||||||
})
|
})
|
||||||
|
|
||||||
requestSaved()
|
requestSaved()
|
||||||
@@ -219,68 +253,114 @@ const saveRequestAs = async () => {
|
|||||||
originLocation: "user-collection",
|
originLocation: "user-collection",
|
||||||
folderPath: picked.value.folderPath,
|
folderPath: picked.value.folderPath,
|
||||||
requestIndex: insertionIndex,
|
requestIndex: insertionIndex,
|
||||||
req: requestUpdated,
|
req: cloneDeep(requestUpdated),
|
||||||
})
|
})
|
||||||
|
|
||||||
requestSaved()
|
requestSaved()
|
||||||
} else if (picked.value.pickedType === "my-request") {
|
} else if (picked.value.pickedType === "my-collection") {
|
||||||
if (!isHoppRESTRequest(requestUpdated))
|
if (!isHoppRESTRequest(requestUpdated))
|
||||||
throw new Error("requestUpdated is not a REST Request")
|
throw new Error("requestUpdated is not a REST Request")
|
||||||
|
|
||||||
editRESTRequest(
|
const insertionIndex = saveRESTRequestAs(
|
||||||
picked.value.folderPath,
|
`${picked.value.collectionIndex}`,
|
||||||
picked.value.requestIndex,
|
|
||||||
requestUpdated
|
requestUpdated
|
||||||
)
|
)
|
||||||
|
|
||||||
setRESTSaveContext({
|
setRESTSaveContext({
|
||||||
originLocation: "user-collection",
|
originLocation: "user-collection",
|
||||||
folderPath: picked.value.folderPath,
|
folderPath: `${picked.value.collectionIndex}`,
|
||||||
requestIndex: picked.value.requestIndex,
|
requestIndex: insertionIndex,
|
||||||
req: requestUpdated,
|
req: cloneDeep(requestUpdated),
|
||||||
})
|
})
|
||||||
|
|
||||||
requestSaved()
|
requestSaved()
|
||||||
} else if (picked.value.pickedType === "teams-collection") {
|
|
||||||
if (!isHoppRESTRequest(requestUpdated))
|
|
||||||
throw new Error("requestUpdated is not a REST Request")
|
|
||||||
|
|
||||||
updateTeamCollectionOrFolder(picked.value.collectionID, requestUpdated)
|
|
||||||
} else if (picked.value.pickedType === "teams-folder") {
|
|
||||||
if (!isHoppRESTRequest(requestUpdated))
|
|
||||||
throw new Error("requestUpdated is not a REST Request")
|
|
||||||
|
|
||||||
updateTeamCollectionOrFolder(picked.value.folderID, requestUpdated)
|
|
||||||
} else if (picked.value.pickedType === "teams-request") {
|
} else if (picked.value.pickedType === "teams-request") {
|
||||||
if (!isHoppRESTRequest(requestUpdated))
|
if (!isHoppRESTRequest(requestUpdated))
|
||||||
throw new Error("requestUpdated is not a REST Request")
|
throw new Error("requestUpdated is not a REST Request")
|
||||||
|
|
||||||
if (
|
if (collectionsType.value.type !== "team-collections")
|
||||||
collectionsType.value.type !== "team-collections" ||
|
|
||||||
!collectionsType.value.selectedTeam
|
|
||||||
)
|
|
||||||
throw new Error("Collections Type mismatch")
|
throw new Error("Collections Type mismatch")
|
||||||
|
|
||||||
modalLoadingState.value = true
|
runMutation(UpdateRequestDocument, {
|
||||||
|
requestID: picked.value.requestID,
|
||||||
|
data: {
|
||||||
|
request: JSON.stringify(requestUpdated),
|
||||||
|
title: requestUpdated.name,
|
||||||
|
},
|
||||||
|
})().then((result) => {
|
||||||
|
if (E.isLeft(result)) {
|
||||||
|
toast.error(`${t("profile.no_permission")}`)
|
||||||
|
throw new Error(`${result.left}`)
|
||||||
|
} else {
|
||||||
|
requestSaved()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const data = {
|
setRESTSaveContext({
|
||||||
request: JSON.stringify(requestUpdated),
|
originLocation: "team-collection",
|
||||||
title: requestUpdated.name,
|
requestID: picked.value.requestID,
|
||||||
|
req: cloneDeep(requestUpdated),
|
||||||
|
})
|
||||||
|
} else if (picked.value.pickedType === "teams-folder") {
|
||||||
|
if (!isHoppRESTRequest(requestUpdated))
|
||||||
|
throw new Error("requestUpdated is not a REST Request")
|
||||||
|
|
||||||
|
if (collectionsType.value.type !== "team-collections")
|
||||||
|
throw new Error("Collections Type mismatch")
|
||||||
|
|
||||||
|
const result = await runMutation(CreateRequestInCollectionDocument, {
|
||||||
|
collectionID: picked.value.folderID,
|
||||||
|
data: {
|
||||||
|
request: JSON.stringify(requestUpdated),
|
||||||
|
teamID: collectionsType.value.selectedTeam.id,
|
||||||
|
title: requestUpdated.name,
|
||||||
|
},
|
||||||
|
})()
|
||||||
|
|
||||||
|
if (E.isLeft(result)) {
|
||||||
|
toast.error(`${t("profile.no_permission")}`)
|
||||||
|
console.error(result.left)
|
||||||
|
} else {
|
||||||
|
setRESTSaveContext({
|
||||||
|
originLocation: "team-collection",
|
||||||
|
requestID: result.right.createRequestInCollection.id,
|
||||||
|
teamID: collectionsType.value.selectedTeam.id,
|
||||||
|
collectionID: picked.value.folderID,
|
||||||
|
req: cloneDeep(requestUpdated),
|
||||||
|
})
|
||||||
|
|
||||||
|
requestSaved()
|
||||||
}
|
}
|
||||||
|
} else if (picked.value.pickedType === "teams-collection") {
|
||||||
|
if (!isHoppRESTRequest(requestUpdated))
|
||||||
|
throw new Error("requestUpdated is not a REST Request")
|
||||||
|
|
||||||
pipe(
|
if (collectionsType.value.type !== "team-collections")
|
||||||
updateTeamRequest(picked.value.requestID, data),
|
throw new Error("Collections Type mismatch")
|
||||||
TE.match(
|
|
||||||
(err: GQLError<string>) => {
|
const result = await runMutation(CreateRequestInCollectionDocument, {
|
||||||
toast.error(`${getErrorMessage(err)}`)
|
collectionID: picked.value.collectionID,
|
||||||
modalLoadingState.value = false
|
data: {
|
||||||
},
|
title: requestUpdated.name,
|
||||||
() => {
|
request: JSON.stringify(requestUpdated),
|
||||||
modalLoadingState.value = false
|
teamID: collectionsType.value.selectedTeam.id,
|
||||||
requestSaved()
|
},
|
||||||
}
|
})()
|
||||||
)
|
|
||||||
)()
|
if (E.isLeft(result)) {
|
||||||
|
toast.error(`${t("profile.no_permission")}`)
|
||||||
|
console.error(result.left)
|
||||||
|
} else {
|
||||||
|
setRESTSaveContext({
|
||||||
|
originLocation: "team-collection",
|
||||||
|
requestID: result.right.createRequestInCollection.id,
|
||||||
|
teamID: collectionsType.value.selectedTeam.id,
|
||||||
|
collectionID: picked.value.collectionID,
|
||||||
|
req: cloneDeep(requestUpdated),
|
||||||
|
})
|
||||||
|
|
||||||
|
requestSaved()
|
||||||
|
}
|
||||||
} else if (picked.value.pickedType === "gql-my-request") {
|
} else if (picked.value.pickedType === "gql-my-request") {
|
||||||
// TODO: Check for GQL request ?
|
// TODO: Check for GQL request ?
|
||||||
editGraphqlRequest(
|
editGraphqlRequest(
|
||||||
@@ -309,81 +389,12 @@ const saveRequestAs = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates a team collection or folder and sets the save context to the updated request
|
|
||||||
* @param collectionID - ID of the collection or folder
|
|
||||||
* @param requestUpdated - Updated request
|
|
||||||
*/
|
|
||||||
const updateTeamCollectionOrFolder = (
|
|
||||||
collectionID: string,
|
|
||||||
requestUpdated: HoppRESTRequest
|
|
||||||
) => {
|
|
||||||
if (
|
|
||||||
collectionsType.value.type !== "team-collections" ||
|
|
||||||
!collectionsType.value.selectedTeam
|
|
||||||
)
|
|
||||||
throw new Error("Collections Type mismatch")
|
|
||||||
|
|
||||||
modalLoadingState.value = true
|
|
||||||
|
|
||||||
const data = {
|
|
||||||
title: requestUpdated.name,
|
|
||||||
request: JSON.stringify(requestUpdated),
|
|
||||||
teamID: collectionsType.value.selectedTeam.id,
|
|
||||||
}
|
|
||||||
pipe(
|
|
||||||
createRequestInCollection(collectionID, data),
|
|
||||||
TE.match(
|
|
||||||
(err: GQLError<string>) => {
|
|
||||||
toast.error(`${getErrorMessage(err)}`)
|
|
||||||
modalLoadingState.value = false
|
|
||||||
},
|
|
||||||
(result) => {
|
|
||||||
const { createRequestInCollection } = result
|
|
||||||
|
|
||||||
setRESTSaveContext({
|
|
||||||
originLocation: "team-collection",
|
|
||||||
requestID: createRequestInCollection.id,
|
|
||||||
collectionID: createRequestInCollection.collection.id,
|
|
||||||
teamID: createRequestInCollection.collection.team.id,
|
|
||||||
req: requestUpdated,
|
|
||||||
})
|
|
||||||
modalLoadingState.value = false
|
|
||||||
requestSaved()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)()
|
|
||||||
}
|
|
||||||
|
|
||||||
const requestSaved = () => {
|
const requestSaved = () => {
|
||||||
toast.success(`${t("request.added")}`)
|
toast.success(`${t("request.added")}`)
|
||||||
hideModal()
|
hideModal()
|
||||||
}
|
}
|
||||||
|
|
||||||
const hideModal = () => {
|
const updateColl = (ev: CollectionType["type"]) => {
|
||||||
picked.value = null
|
collectionsType.value.type = ev
|
||||||
emit("hide-modal")
|
|
||||||
}
|
|
||||||
|
|
||||||
const getErrorMessage = (err: GQLError<string>) => {
|
|
||||||
console.error(err)
|
|
||||||
if (err.type === "network_error") {
|
|
||||||
return t("error.network_error")
|
|
||||||
} else {
|
|
||||||
switch (err.error) {
|
|
||||||
case "team_coll/short_title":
|
|
||||||
return t("collection.name_length_insufficient")
|
|
||||||
case "team/invalid_coll_id":
|
|
||||||
return t("team.invalid_id")
|
|
||||||
case "team/not_required_role":
|
|
||||||
return t("profile.no_permission")
|
|
||||||
case "team_req/not_required_role":
|
|
||||||
return t("profile.no_permission")
|
|
||||||
case "Forbidden resource":
|
|
||||||
return t("profile.no_permission")
|
|
||||||
default:
|
|
||||||
return t("error.something_went_wrong")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,624 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="flex flex-col flex-1">
|
|
||||||
<div
|
|
||||||
class="sticky z-10 flex justify-between flex-1 border-b bg-primary border-dividerLight"
|
|
||||||
:style="
|
|
||||||
saveRequest
|
|
||||||
? 'top: calc(var(--upper-secondary-sticky-fold) - var(--line-height-body))'
|
|
||||||
: 'top: var(--upper-secondary-sticky-fold)'
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<ButtonSecondary
|
|
||||||
v-if="hasNoTeamAccess"
|
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
|
||||||
disabled
|
|
||||||
class="!rounded-none"
|
|
||||||
:icon="IconPlus"
|
|
||||||
:title="t('team.no_access')"
|
|
||||||
:label="t('action.new')"
|
|
||||||
/>
|
|
||||||
<ButtonSecondary
|
|
||||||
v-else
|
|
||||||
:icon="IconPlus"
|
|
||||||
:label="t('action.new')"
|
|
||||||
class="!rounded-none"
|
|
||||||
@click="emit('display-modal-add')"
|
|
||||||
/>
|
|
||||||
<span class="flex">
|
|
||||||
<ButtonSecondary
|
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
|
||||||
to="https://docs.hoppscotch.io/features/collections"
|
|
||||||
blank
|
|
||||||
:title="t('app.wiki')"
|
|
||||||
:icon="IconHelpCircle"
|
|
||||||
/>
|
|
||||||
<ButtonSecondary
|
|
||||||
v-if="!saveRequest"
|
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
|
||||||
:disabled="
|
|
||||||
collectionsType.type === 'team-collections' &&
|
|
||||||
collectionsType.selectedTeam === undefined
|
|
||||||
"
|
|
||||||
:icon="IconArchive"
|
|
||||||
:title="t('modal.import_export')"
|
|
||||||
@click="emit('display-modal-import-export')"
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="flex flex-col overflow-hidden">
|
|
||||||
<SmartTree :adapter="teamAdapter">
|
|
||||||
<template #content="{ node, toggleChildren, isOpen }">
|
|
||||||
<CollectionsCollection
|
|
||||||
v-if="node.data.type === 'collections'"
|
|
||||||
:data="node.data.data.data"
|
|
||||||
:collections-type="collectionsType.type"
|
|
||||||
:is-open="isOpen"
|
|
||||||
:export-loading="exportLoading"
|
|
||||||
:has-no-team-access="hasNoTeamAccess"
|
|
||||||
:is-selected="
|
|
||||||
isSelected({
|
|
||||||
collectionID: node.id,
|
|
||||||
})
|
|
||||||
"
|
|
||||||
folder-type="collection"
|
|
||||||
@add-request="
|
|
||||||
node.data.type === 'collections' &&
|
|
||||||
emit('add-request', {
|
|
||||||
path: node.id,
|
|
||||||
folder: node.data.data.data,
|
|
||||||
})
|
|
||||||
"
|
|
||||||
@add-folder="
|
|
||||||
node.data.type === 'collections' &&
|
|
||||||
emit('add-folder', {
|
|
||||||
path: node.id,
|
|
||||||
folder: node.data.data.data,
|
|
||||||
})
|
|
||||||
"
|
|
||||||
@edit-collection="
|
|
||||||
node.data.type === 'collections' &&
|
|
||||||
emit('edit-collection', {
|
|
||||||
collectionIndex: node.id,
|
|
||||||
collection: node.data.data.data,
|
|
||||||
})
|
|
||||||
"
|
|
||||||
@export-data="
|
|
||||||
node.data.type === 'collections' &&
|
|
||||||
emit('export-data', node.data.data.data)
|
|
||||||
"
|
|
||||||
@remove-collection="emit('remove-collection', node.id)"
|
|
||||||
@toggle-children="
|
|
||||||
() => {
|
|
||||||
toggleChildren(),
|
|
||||||
saveRequest &&
|
|
||||||
emit('select', {
|
|
||||||
pickedType: 'teams-collection',
|
|
||||||
collectionID: node.id,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
<CollectionsCollection
|
|
||||||
v-if="node.data.type === 'folders'"
|
|
||||||
:data="node.data.data.data"
|
|
||||||
:collections-type="collectionsType.type"
|
|
||||||
:is-open="isOpen"
|
|
||||||
:export-loading="exportLoading"
|
|
||||||
:has-no-team-access="hasNoTeamAccess"
|
|
||||||
:is-selected="
|
|
||||||
isSelected({
|
|
||||||
folderID: node.data.data.data.id,
|
|
||||||
})
|
|
||||||
"
|
|
||||||
folder-type="folder"
|
|
||||||
@add-request="
|
|
||||||
node.data.type === 'folders' &&
|
|
||||||
emit('add-request', {
|
|
||||||
path: node.id,
|
|
||||||
folder: node.data.data.data,
|
|
||||||
})
|
|
||||||
"
|
|
||||||
@add-folder="
|
|
||||||
node.data.type === 'folders' &&
|
|
||||||
emit('add-folder', {
|
|
||||||
path: node.id,
|
|
||||||
folder: node.data.data.data,
|
|
||||||
})
|
|
||||||
"
|
|
||||||
@edit-collection="
|
|
||||||
node.data.type === 'folders' &&
|
|
||||||
emit('edit-folder', {
|
|
||||||
folder: node.data.data.data,
|
|
||||||
})
|
|
||||||
"
|
|
||||||
@export-data="
|
|
||||||
node.data.type === 'folders' &&
|
|
||||||
emit('export-data', node.data.data.data)
|
|
||||||
"
|
|
||||||
@remove-collection="
|
|
||||||
node.data.type === 'folders' &&
|
|
||||||
emit('remove-folder', node.data.data.data.id)
|
|
||||||
"
|
|
||||||
@toggle-children="
|
|
||||||
() => {
|
|
||||||
toggleChildren(),
|
|
||||||
saveRequest &&
|
|
||||||
emit('select', {
|
|
||||||
pickedType: 'teams-folder',
|
|
||||||
folderID: node.data.data.data.id,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
<CollectionsRequest
|
|
||||||
v-if="node.data.type === 'requests'"
|
|
||||||
:request="node.data.data.data.request"
|
|
||||||
:collections-type="collectionsType.type"
|
|
||||||
:duplicate-loading="duplicateLoading"
|
|
||||||
:is-active="isActiveRequest(node.data.data.data.id)"
|
|
||||||
:has-no-team-access="hasNoTeamAccess"
|
|
||||||
:is-selected="
|
|
||||||
isSelected({
|
|
||||||
requestID: node.data.data.data.id,
|
|
||||||
})
|
|
||||||
"
|
|
||||||
@edit-request="
|
|
||||||
node.data.type === 'requests' &&
|
|
||||||
emit('edit-request', {
|
|
||||||
requestIndex: node.data.data.data.id,
|
|
||||||
request: node.data.data.data.request,
|
|
||||||
})
|
|
||||||
"
|
|
||||||
@duplicate-request="
|
|
||||||
node.data.type === 'requests' &&
|
|
||||||
emit('duplicate-request', {
|
|
||||||
folderPath: node.data.data.parentIndex,
|
|
||||||
request: node.data.data.data.request,
|
|
||||||
})
|
|
||||||
"
|
|
||||||
@remove-request="
|
|
||||||
node.data.type === 'requests' &&
|
|
||||||
emit('remove-request', {
|
|
||||||
folderPath: null,
|
|
||||||
requestIndex: node.data.data.data.id,
|
|
||||||
})
|
|
||||||
"
|
|
||||||
@select-request="
|
|
||||||
node.data.type === 'requests' &&
|
|
||||||
selectRequest({
|
|
||||||
request: node.data.data.data.request,
|
|
||||||
requestIndex: node.data.data.data.id,
|
|
||||||
})
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<template #emptyNode="{ node }">
|
|
||||||
<div v-if="node === null">
|
|
||||||
<div
|
|
||||||
class="flex flex-col items-center justify-center p-4 text-secondaryLight"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
:src="`/images/states/${colorMode.value}/pack.svg`"
|
|
||||||
loading="lazy"
|
|
||||||
class="inline-flex flex-col object-contain object-center w-16 h-16 mb-4"
|
|
||||||
:alt="`${t('empty.collections')}`"
|
|
||||||
/>
|
|
||||||
<span class="pb-4 text-center">
|
|
||||||
{{ t("empty.collections") }}
|
|
||||||
</span>
|
|
||||||
<ButtonSecondary
|
|
||||||
v-if="hasNoTeamAccess"
|
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
|
||||||
disabled
|
|
||||||
filled
|
|
||||||
outline
|
|
||||||
:title="t('team.no_access')"
|
|
||||||
:label="t('add.new')"
|
|
||||||
/>
|
|
||||||
<ButtonSecondary
|
|
||||||
v-else
|
|
||||||
:label="t('add.new')"
|
|
||||||
filled
|
|
||||||
outline
|
|
||||||
@click="emit('display-modal-add')"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-else-if="node.data.type === 'collections'"
|
|
||||||
class="flex flex-col items-center justify-center p-4 text-secondaryLight"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
:src="`/images/states/${colorMode.value}/pack.svg`"
|
|
||||||
loading="lazy"
|
|
||||||
class="inline-flex flex-col object-contain object-center w-16 h-16 mb-4"
|
|
||||||
:alt="`${t('empty.collection')}`"
|
|
||||||
/>
|
|
||||||
<span class="pb-4 text-center">
|
|
||||||
{{ t("empty.collection") }}
|
|
||||||
</span>
|
|
||||||
<ButtonSecondary
|
|
||||||
v-if="hasNoTeamAccess"
|
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
|
||||||
disabled
|
|
||||||
filled
|
|
||||||
outline
|
|
||||||
:title="t('team.no_access')"
|
|
||||||
:label="t('add.new')"
|
|
||||||
/>
|
|
||||||
<ButtonSecondary
|
|
||||||
v-else
|
|
||||||
:label="t('add.new')"
|
|
||||||
filled
|
|
||||||
outline
|
|
||||||
@click="
|
|
||||||
node.data.type === 'collections' &&
|
|
||||||
emit('add-folder', {
|
|
||||||
path: node.id,
|
|
||||||
folder: node.data.data.data,
|
|
||||||
})
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-else-if="node.data.type === 'folders'"
|
|
||||||
class="flex flex-col items-center justify-center p-4 text-secondaryLight"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
:src="`/images/states/${colorMode.value}/pack.svg`"
|
|
||||||
loading="lazy"
|
|
||||||
class="inline-flex flex-col object-contain object-center w-16 h-16 mb-4"
|
|
||||||
:alt="`${t('empty.folder')}`"
|
|
||||||
/>
|
|
||||||
<span class="text-center">
|
|
||||||
{{ t("empty.folder") }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</SmartTree>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import IconArchive from "~icons/lucide/archive"
|
|
||||||
import IconPlus from "~icons/lucide/plus"
|
|
||||||
import IconHelpCircle from "~icons/lucide/help-circle"
|
|
||||||
import { computed, PropType, Ref, toRef } from "vue"
|
|
||||||
import { GetMyTeamsQuery } from "~/helpers/backend/graphql"
|
|
||||||
import { useI18n } from "@composables/i18n"
|
|
||||||
import { useColorMode } from "@composables/theming"
|
|
||||||
import { TeamCollection } from "~/helpers/teams/TeamCollection"
|
|
||||||
import { TeamRequest } from "~/helpers/teams/TeamRequest"
|
|
||||||
import { ChildrenResult, SmartTreeAdapter } from "~/helpers/treeAdapter"
|
|
||||||
import { cloneDeep } from "lodash-es"
|
|
||||||
import { HoppRESTRequest } from "@hoppscotch/data"
|
|
||||||
import { useReadonlyStream } from "~/composables/stream"
|
|
||||||
import { restSaveContext$ } from "~/newstore/RESTSession"
|
|
||||||
import { pipe } from "fp-ts/function"
|
|
||||||
import * as O from "fp-ts/Option"
|
|
||||||
import { Picked } from "~/helpers/types/HoppPicked.js"
|
|
||||||
|
|
||||||
const t = useI18n()
|
|
||||||
const colorMode = useColorMode()
|
|
||||||
|
|
||||||
type SelectedTeam = GetMyTeamsQuery["myTeams"][number] | undefined
|
|
||||||
|
|
||||||
type CollectionType =
|
|
||||||
| {
|
|
||||||
type: "team-collections"
|
|
||||||
selectedTeam: SelectedTeam
|
|
||||||
}
|
|
||||||
| { type: "my-collections"; selectedTeam: undefined }
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
collectionsType: {
|
|
||||||
type: Object as PropType<CollectionType>,
|
|
||||||
default: () => ({ type: "my-collections", selectedTeam: undefined }),
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
teamCollectionList: {
|
|
||||||
type: Array as PropType<TeamCollection[]>,
|
|
||||||
default: () => [],
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
teamLoadingCollections: {
|
|
||||||
type: Array as PropType<string[]>,
|
|
||||||
default: () => [],
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
saveRequest: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
exportLoading: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
duplicateLoading: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
picked: {
|
|
||||||
type: Object as PropType<Picked | null>,
|
|
||||||
default: null,
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const emit = defineEmits<{
|
|
||||||
(
|
|
||||||
event: "add-request",
|
|
||||||
payload: {
|
|
||||||
path: string
|
|
||||||
folder: TeamCollection
|
|
||||||
}
|
|
||||||
): void
|
|
||||||
(
|
|
||||||
event: "add-folder",
|
|
||||||
payload: {
|
|
||||||
path: string
|
|
||||||
folder: TeamCollection
|
|
||||||
}
|
|
||||||
): void
|
|
||||||
(
|
|
||||||
event: "edit-collection",
|
|
||||||
payload: {
|
|
||||||
collectionIndex: string
|
|
||||||
collection: TeamCollection
|
|
||||||
}
|
|
||||||
): void
|
|
||||||
(
|
|
||||||
event: "edit-folder",
|
|
||||||
payload: {
|
|
||||||
folder: TeamCollection
|
|
||||||
}
|
|
||||||
): void
|
|
||||||
(
|
|
||||||
event: "edit-request",
|
|
||||||
payload: {
|
|
||||||
requestIndex: string
|
|
||||||
request: HoppRESTRequest
|
|
||||||
}
|
|
||||||
): void
|
|
||||||
(
|
|
||||||
event: "duplicate-request",
|
|
||||||
payload: {
|
|
||||||
folderPath: string
|
|
||||||
request: HoppRESTRequest
|
|
||||||
}
|
|
||||||
): void
|
|
||||||
(event: "export-data", payload: TeamCollection): void
|
|
||||||
(event: "remove-collection", payload: string): void
|
|
||||||
(event: "remove-folder", payload: string): void
|
|
||||||
(
|
|
||||||
event: "remove-request",
|
|
||||||
payload: {
|
|
||||||
folderPath: string | null
|
|
||||||
requestIndex: string
|
|
||||||
}
|
|
||||||
): void
|
|
||||||
(
|
|
||||||
event: "select-request",
|
|
||||||
payload: {
|
|
||||||
request: HoppRESTRequest
|
|
||||||
requestIndex: string
|
|
||||||
isActive: boolean
|
|
||||||
folderPath?: string | undefined
|
|
||||||
}
|
|
||||||
): void
|
|
||||||
(event: "select", payload: Picked | null): void
|
|
||||||
(event: "expand-team-collection", payload: string): void
|
|
||||||
(event: "display-modal-add"): void
|
|
||||||
(event: "display-modal-import-export"): void
|
|
||||||
}>()
|
|
||||||
|
|
||||||
const teamCollectionsList = toRef(props, "teamCollectionList")
|
|
||||||
|
|
||||||
const hasNoTeamAccess = computed(
|
|
||||||
() =>
|
|
||||||
props.collectionsType.type === "team-collections" &&
|
|
||||||
(props.collectionsType.selectedTeam === undefined ||
|
|
||||||
props.collectionsType.selectedTeam.myRole === "VIEWER")
|
|
||||||
)
|
|
||||||
|
|
||||||
const isSelected = computed(() => {
|
|
||||||
return ({
|
|
||||||
collectionID,
|
|
||||||
folderID,
|
|
||||||
requestID,
|
|
||||||
}: {
|
|
||||||
collectionID?: string | undefined
|
|
||||||
folderID?: string | undefined
|
|
||||||
requestID?: string | undefined
|
|
||||||
}) => {
|
|
||||||
if (collectionID !== undefined) {
|
|
||||||
return (
|
|
||||||
props.picked &&
|
|
||||||
props.picked.pickedType === "teams-collection" &&
|
|
||||||
props.picked.collectionID === collectionID
|
|
||||||
)
|
|
||||||
} else if (requestID !== undefined) {
|
|
||||||
return (
|
|
||||||
props.picked &&
|
|
||||||
props.picked.pickedType === "teams-request" &&
|
|
||||||
props.picked.requestID === requestID
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
props.picked &&
|
|
||||||
props.picked.pickedType === "teams-folder" &&
|
|
||||||
props.picked.folderID === folderID
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const active = useReadonlyStream(restSaveContext$, null)
|
|
||||||
|
|
||||||
const isActiveRequest = computed(() => {
|
|
||||||
return (requestID: string) => {
|
|
||||||
return pipe(
|
|
||||||
active.value,
|
|
||||||
O.fromNullable,
|
|
||||||
O.filter(
|
|
||||||
(active) =>
|
|
||||||
active.originLocation === "team-collection" &&
|
|
||||||
active.requestID === requestID
|
|
||||||
),
|
|
||||||
O.isSome
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const selectRequest = (data: {
|
|
||||||
request: HoppRESTRequest
|
|
||||||
requestIndex: string
|
|
||||||
}) => {
|
|
||||||
const { request, requestIndex } = data
|
|
||||||
if (props.saveRequest) {
|
|
||||||
emit("select", {
|
|
||||||
pickedType: "teams-request",
|
|
||||||
requestID: requestIndex,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
emit("select-request", {
|
|
||||||
request: request,
|
|
||||||
requestIndex: requestIndex,
|
|
||||||
isActive: isActiveRequest.value(requestIndex),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type TeamCollections = {
|
|
||||||
type: "collections"
|
|
||||||
data: {
|
|
||||||
parentIndex: null
|
|
||||||
data: TeamCollection
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type TeamFolder = {
|
|
||||||
type: "folders"
|
|
||||||
data: {
|
|
||||||
parentIndex: string
|
|
||||||
data: TeamCollection
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type TeamRequests = {
|
|
||||||
type: "requests"
|
|
||||||
data: {
|
|
||||||
parentIndex: string
|
|
||||||
data: TeamRequest
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type TeamCollectionNode = TeamCollections | TeamFolder | TeamRequests
|
|
||||||
|
|
||||||
class TeamCollectionsAdapter implements SmartTreeAdapter<TeamCollectionNode> {
|
|
||||||
constructor(public data: Ref<TeamCollection[]>) {}
|
|
||||||
|
|
||||||
findCollInTree(
|
|
||||||
tree: TeamCollection[],
|
|
||||||
targetID: string
|
|
||||||
): TeamCollection | null {
|
|
||||||
for (const coll of tree) {
|
|
||||||
// If the direct child matched, then return that
|
|
||||||
if (coll.id === targetID) return coll
|
|
||||||
|
|
||||||
// Else run it in the children
|
|
||||||
if (coll.children) {
|
|
||||||
const result = this.findCollInTree(coll.children, targetID)
|
|
||||||
if (result) return result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If nothing matched, return null
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
getChildren(id: string | null): Ref<ChildrenResult<TeamCollectionNode>> {
|
|
||||||
return computed(() => {
|
|
||||||
if (id === null) {
|
|
||||||
if (props.teamLoadingCollections.includes("root")) {
|
|
||||||
return {
|
|
||||||
status: "loading",
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const data = this.data.value.map((item) => ({
|
|
||||||
id: item.id,
|
|
||||||
data: {
|
|
||||||
type: "collections",
|
|
||||||
data: {
|
|
||||||
parentIndex: null,
|
|
||||||
data: item,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}))
|
|
||||||
return {
|
|
||||||
status: "loaded",
|
|
||||||
data: cloneDeep(data),
|
|
||||||
} as ChildrenResult<TeamCollections>
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const parsedID = id.split("/")[id.split("/").length - 1]
|
|
||||||
|
|
||||||
!props.teamLoadingCollections.includes(parsedID) &&
|
|
||||||
emit("expand-team-collection", parsedID)
|
|
||||||
|
|
||||||
if (props.teamLoadingCollections.includes(parsedID)) {
|
|
||||||
return {
|
|
||||||
status: "loading",
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const items = this.findCollInTree(this.data.value, parsedID)
|
|
||||||
if (items) {
|
|
||||||
const data = [
|
|
||||||
...(items.children
|
|
||||||
? items.children.map((item) => ({
|
|
||||||
id: `${id}/${item.id}`,
|
|
||||||
data: {
|
|
||||||
type: "folders",
|
|
||||||
data: {
|
|
||||||
parentIndex: parsedID,
|
|
||||||
data: item,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}))
|
|
||||||
: []),
|
|
||||||
...(items.requests
|
|
||||||
? items.requests.map((item) => ({
|
|
||||||
id: `${id}/${item.id}`,
|
|
||||||
data: {
|
|
||||||
type: "requests",
|
|
||||||
data: {
|
|
||||||
parentIndex: parsedID,
|
|
||||||
data: item,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}))
|
|
||||||
: []),
|
|
||||||
]
|
|
||||||
return {
|
|
||||||
status: "loaded",
|
|
||||||
data: cloneDeep(data),
|
|
||||||
} as ChildrenResult<TeamFolder | TeamRequests>
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
status: "loaded",
|
|
||||||
data: [],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const teamAdapter: SmartTreeAdapter<TeamCollectionNode> =
|
|
||||||
new TeamCollectionsAdapter(teamCollectionsList)
|
|
||||||
</script>
|
|
||||||
@@ -1,167 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="flex flex-1">
|
|
||||||
<SmartIntersection
|
|
||||||
class="flex flex-col flex-1"
|
|
||||||
@intersecting="onTeamSelectIntersect"
|
|
||||||
>
|
|
||||||
<tippy
|
|
||||||
interactive
|
|
||||||
trigger="click"
|
|
||||||
theme="popover"
|
|
||||||
placement="bottom"
|
|
||||||
:on-shown="() => tippyActions!.focus()"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
|
||||||
:title="`${t('collection.select_team')}`"
|
|
||||||
class="bg-transparent border-b border-dividerLight select-wrapper"
|
|
||||||
>
|
|
||||||
<ButtonSecondary
|
|
||||||
v-if="collectionsType.selectedTeam"
|
|
||||||
:icon="IconUsers"
|
|
||||||
:label="collectionsType.selectedTeam.name"
|
|
||||||
class="flex-1 !justify-start pr-8 rounded-none"
|
|
||||||
/>
|
|
||||||
<ButtonSecondary
|
|
||||||
v-else
|
|
||||||
:label="`${t('collection.select_team')}`"
|
|
||||||
class="flex-1 !justify-start pr-8 rounded-none"
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
<template #content="{ hide }">
|
|
||||||
<div
|
|
||||||
ref="tippyActions"
|
|
||||||
class="flex flex-col focus:outline-none"
|
|
||||||
tabindex="0"
|
|
||||||
@keyup.escape="hide()"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
v-if="isTeamListLoading && myTeams.length === 0"
|
|
||||||
class="flex flex-col items-center justify-center flex-1 p-2"
|
|
||||||
>
|
|
||||||
<SmartSpinner class="my-2" />
|
|
||||||
<span class="text-secondaryLight">{{ t("state.loading") }}</span>
|
|
||||||
</div>
|
|
||||||
<div v-else-if="myTeams.length > 0" class="flex flex-col">
|
|
||||||
<SmartItem
|
|
||||||
v-for="(team, index) in myTeams"
|
|
||||||
:key="`team-${index}`"
|
|
||||||
:label="team.name"
|
|
||||||
:info-icon="
|
|
||||||
team.id === collectionsType.selectedTeam?.id
|
|
||||||
? IconDone
|
|
||||||
: undefined
|
|
||||||
"
|
|
||||||
:active-info-icon="team.id === collectionsType.selectedTeam?.id"
|
|
||||||
:icon="IconUsers"
|
|
||||||
@click="
|
|
||||||
() => {
|
|
||||||
updateSelectedTeam(team)
|
|
||||||
hide()
|
|
||||||
}
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
<hr />
|
|
||||||
<SmartItem
|
|
||||||
:icon="IconPlus"
|
|
||||||
:label="t('team.create_new')"
|
|
||||||
@click="
|
|
||||||
() => {
|
|
||||||
displayTeamModalAdd(true)
|
|
||||||
hide()
|
|
||||||
}
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-else
|
|
||||||
class="flex flex-col items-center justify-center p-2 text-secondaryLight"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
:src="`/images/states/${colorMode.value}/add_group.svg`"
|
|
||||||
loading="lazy"
|
|
||||||
class="inline-flex flex-col object-contain object-center mb-4 w-14 h-14"
|
|
||||||
:alt="`${t('empty.teams')}`"
|
|
||||||
/>
|
|
||||||
<span class="pb-4 text-center">
|
|
||||||
{{ t("empty.teams") }}
|
|
||||||
</span>
|
|
||||||
<ButtonSecondary
|
|
||||||
:label="t('team.create_new')"
|
|
||||||
filled
|
|
||||||
outline
|
|
||||||
@click="
|
|
||||||
() => {
|
|
||||||
displayTeamModalAdd(true)
|
|
||||||
hide()
|
|
||||||
}
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</tippy>
|
|
||||||
</SmartIntersection>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import IconUsers from "~icons/lucide/users"
|
|
||||||
import IconDone from "~icons/lucide/check"
|
|
||||||
import { PropType, ref } from "vue"
|
|
||||||
import { GetMyTeamsQuery } from "~/helpers/backend/graphql"
|
|
||||||
import { TippyComponent } from "vue-tippy"
|
|
||||||
import { useI18n } from "@composables/i18n"
|
|
||||||
import { useColorMode } from "@composables/theming"
|
|
||||||
import IconPlus from "~icons/lucide/plus"
|
|
||||||
|
|
||||||
const t = useI18n()
|
|
||||||
const colorMode = useColorMode()
|
|
||||||
|
|
||||||
type SelectedTeam = GetMyTeamsQuery["myTeams"][number] | undefined
|
|
||||||
|
|
||||||
type CollectionType =
|
|
||||||
| {
|
|
||||||
type: "team-collections"
|
|
||||||
selectedTeam: SelectedTeam
|
|
||||||
}
|
|
||||||
| { type: "my-collections"; selectedTeam: undefined }
|
|
||||||
|
|
||||||
defineProps({
|
|
||||||
collectionsType: {
|
|
||||||
type: Object as PropType<CollectionType>,
|
|
||||||
default: () => ({ type: "my-collections", selectedTeam: undefined }),
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
myTeams: {
|
|
||||||
type: Array as PropType<GetMyTeamsQuery["myTeams"]>,
|
|
||||||
default: () => [],
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
isTeamListLoading: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const tippyActions = ref<TippyComponent | null>(null)
|
|
||||||
|
|
||||||
const emit = defineEmits<{
|
|
||||||
(e: "update-selected-team", payload: SelectedTeam): void
|
|
||||||
(e: "team-select-intersect", payload: boolean): void
|
|
||||||
(e: "display-team-modal-add", payload: boolean): void
|
|
||||||
}>()
|
|
||||||
|
|
||||||
const updateSelectedTeam = (team: SelectedTeam) => {
|
|
||||||
emit("update-selected-team", team)
|
|
||||||
}
|
|
||||||
|
|
||||||
const onTeamSelectIntersect = () => {
|
|
||||||
emit("team-select-intersect", true)
|
|
||||||
}
|
|
||||||
|
|
||||||
const displayTeamModalAdd = (display: boolean) => {
|
|
||||||
emit("display-team-modal-add", display)
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -143,7 +143,7 @@
|
|||||||
v-for="(folder, index) in collection.folders"
|
v-for="(folder, index) in collection.folders"
|
||||||
:key="`folder-${String(index)}`"
|
:key="`folder-${String(index)}`"
|
||||||
:picked="picked"
|
:picked="picked"
|
||||||
:save-request="saveRequest"
|
:saving-mode="savingMode"
|
||||||
:folder="folder"
|
:folder="folder"
|
||||||
:folder-index="index"
|
:folder-index="index"
|
||||||
:folder-path="`${collectionIndex}/${String(index)}`"
|
:folder-path="`${collectionIndex}/${String(index)}`"
|
||||||
@@ -160,7 +160,7 @@
|
|||||||
v-for="(request, index) in collection.requests"
|
v-for="(request, index) in collection.requests"
|
||||||
:key="`request-${String(index)}`"
|
:key="`request-${String(index)}`"
|
||||||
:picked="picked"
|
:picked="picked"
|
||||||
:save-request="saveRequest"
|
:saving-mode="savingMode"
|
||||||
:request="request"
|
:request="request"
|
||||||
:collection-index="collectionIndex"
|
:collection-index="collectionIndex"
|
||||||
:folder-index="-1"
|
:folder-index="-1"
|
||||||
@@ -183,19 +183,9 @@
|
|||||||
class="inline-flex flex-col object-contain object-center w-16 h-16 mb-4"
|
class="inline-flex flex-col object-contain object-center w-16 h-16 mb-4"
|
||||||
:alt="`${t('empty.collection')}`"
|
:alt="`${t('empty.collection')}`"
|
||||||
/>
|
/>
|
||||||
<span class="pb-4 text-center">
|
<span class="text-center">
|
||||||
{{ t("empty.collection") }}
|
{{ t("empty.collection") }}
|
||||||
</span>
|
</span>
|
||||||
<ButtonSecondary
|
|
||||||
:label="t('add.new')"
|
|
||||||
filled
|
|
||||||
outline
|
|
||||||
@click="
|
|
||||||
emit('add-folder', {
|
|
||||||
path: `${collectionIndex}`,
|
|
||||||
})
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -225,12 +215,11 @@ import {
|
|||||||
removeGraphqlCollection,
|
removeGraphqlCollection,
|
||||||
moveGraphqlRequest,
|
moveGraphqlRequest,
|
||||||
} from "~/newstore/collections"
|
} from "~/newstore/collections"
|
||||||
import { Picked } from "~/helpers/types/HoppPicked"
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
picked: { type: Object, default: null },
|
picked: { type: Object, default: null },
|
||||||
// Whether the viewing context is related to picking (activates 'select' events)
|
// Whether the viewing context is related to picking (activates 'select' events)
|
||||||
saveRequest: { type: Boolean, default: false },
|
savingMode: { type: Boolean, default: false },
|
||||||
collectionIndex: { type: Number, default: null },
|
collectionIndex: { type: Number, default: null },
|
||||||
collection: { type: Object, default: () => ({}) },
|
collection: { type: Object, default: () => ({}) },
|
||||||
isFiltered: Boolean,
|
isFiltered: Boolean,
|
||||||
@@ -242,7 +231,7 @@ const t = useI18n()
|
|||||||
|
|
||||||
// TODO: improve types plz
|
// TODO: improve types plz
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: "select", i: Picked | null): void
|
(e: "select", i: { picked: any }): void
|
||||||
(e: "edit-request", i: any): void
|
(e: "edit-request", i: any): void
|
||||||
(e: "duplicate-request", i: any): void
|
(e: "duplicate-request", i: any): void
|
||||||
(e: "add-request", i: any): void
|
(e: "add-request", i: any): void
|
||||||
@@ -278,13 +267,15 @@ const collectionIcon = computed(() => {
|
|||||||
|
|
||||||
const pick = () => {
|
const pick = () => {
|
||||||
emit("select", {
|
emit("select", {
|
||||||
pickedType: "gql-my-collection",
|
picked: {
|
||||||
collectionIndex: props.collectionIndex,
|
pickedType: "gql-my-collection",
|
||||||
|
collectionIndex: props.collectionIndex,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const toggleShowChildren = () => {
|
const toggleShowChildren = () => {
|
||||||
if (props.saveRequest) {
|
if (props.savingMode) {
|
||||||
pick()
|
pick()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -297,7 +288,7 @@ const removeCollection = () => {
|
|||||||
props.picked?.pickedType === "gql-my-collection" &&
|
props.picked?.pickedType === "gql-my-collection" &&
|
||||||
props.picked?.collectionIndex === props.collectionIndex
|
props.picked?.collectionIndex === props.collectionIndex
|
||||||
) {
|
) {
|
||||||
emit("select", null)
|
emit("select", { picked: null })
|
||||||
}
|
}
|
||||||
removeGraphqlCollection(props.collectionIndex)
|
removeGraphqlCollection(props.collectionIndex)
|
||||||
toast.success(`${t("state.deleted")}`)
|
toast.success(`${t("state.deleted")}`)
|
||||||
|
|||||||
@@ -132,7 +132,7 @@
|
|||||||
v-for="(subFolder, subFolderIndex) in folder.folders"
|
v-for="(subFolder, subFolderIndex) in folder.folders"
|
||||||
:key="`subFolder-${String(subFolderIndex)}`"
|
:key="`subFolder-${String(subFolderIndex)}`"
|
||||||
:picked="picked"
|
:picked="picked"
|
||||||
:save-request="saveRequest"
|
:saving-mode="savingMode"
|
||||||
:folder="subFolder"
|
:folder="subFolder"
|
||||||
:folder-index="subFolderIndex"
|
:folder-index="subFolderIndex"
|
||||||
:folder-path="`${folderPath}/${String(subFolderIndex)}`"
|
:folder-path="`${folderPath}/${String(subFolderIndex)}`"
|
||||||
@@ -149,7 +149,7 @@
|
|||||||
v-for="(request, index) in folder.requests"
|
v-for="(request, index) in folder.requests"
|
||||||
:key="`request-${String(index)}`"
|
:key="`request-${String(index)}`"
|
||||||
:picked="picked"
|
:picked="picked"
|
||||||
:save-request="saveRequest"
|
:saving-mode="savingMode"
|
||||||
:request="request"
|
:request="request"
|
||||||
:collection-index="collectionIndex"
|
:collection-index="collectionIndex"
|
||||||
:folder-index="folderIndex"
|
:folder-index="folderIndex"
|
||||||
@@ -212,7 +212,7 @@ const colorMode = useColorMode()
|
|||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
picked: { type: Object, default: null },
|
picked: { type: Object, default: null },
|
||||||
// Whether the request is in a selectable mode (activates 'select' event)
|
// Whether the request is in a selectable mode (activates 'select' event)
|
||||||
saveRequest: { type: Boolean, default: false },
|
savingMode: { type: Boolean, default: false },
|
||||||
folder: { type: Object, default: () => ({}) },
|
folder: { type: Object, default: () => ({}) },
|
||||||
folderIndex: { type: Number, default: null },
|
folderIndex: { type: Number, default: null },
|
||||||
collectionIndex: { type: Number, default: null },
|
collectionIndex: { type: Number, default: null },
|
||||||
@@ -263,7 +263,7 @@ const pick = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const toggleShowChildren = () => {
|
const toggleShowChildren = () => {
|
||||||
if (props.saveRequest) {
|
if (props.savingMode) {
|
||||||
pick()
|
pick()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ import IconFolderPlus from "~icons/lucide/folder-plus"
|
|||||||
import IconDownload from "~icons/lucide/download"
|
import IconDownload from "~icons/lucide/download"
|
||||||
import IconGithub from "~icons/lucide/github"
|
import IconGithub from "~icons/lucide/github"
|
||||||
import { computed, ref } from "vue"
|
import { computed, ref } from "vue"
|
||||||
import { platform } from "~/platform"
|
import { currentUser$ } from "~/helpers/fb/auth"
|
||||||
import { useI18n } from "@composables/i18n"
|
import { useI18n } from "@composables/i18n"
|
||||||
import { useReadonlyStream } from "@composables/stream"
|
import { useReadonlyStream } from "@composables/stream"
|
||||||
import { useToast } from "@composables/toast"
|
import { useToast } from "@composables/toast"
|
||||||
@@ -120,10 +120,7 @@ const emit = defineEmits<{
|
|||||||
const toast = useToast()
|
const toast = useToast()
|
||||||
const t = useI18n()
|
const t = useI18n()
|
||||||
const collections = useReadonlyStream(graphqlCollections$, [])
|
const collections = useReadonlyStream(graphqlCollections$, [])
|
||||||
const currentUser = useReadonlyStream(
|
const currentUser = useReadonlyStream(currentUser$, null)
|
||||||
platform.auth.getCurrentUserStream(),
|
|
||||||
platform.auth.getCurrentUser()
|
|
||||||
)
|
|
||||||
|
|
||||||
// Template refs
|
// Template refs
|
||||||
const tippyActions = ref<any | null>(null)
|
const tippyActions = ref<any | null>(null)
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
</span>
|
</span>
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
v-if="!saveRequest"
|
v-if="!savingMode"
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
:icon="IconRotateCCW"
|
:icon="IconRotateCCW"
|
||||||
:title="t('action.restore')"
|
:title="t('action.restore')"
|
||||||
@@ -148,7 +148,7 @@ const props = defineProps({
|
|||||||
// Whether the object is selected (show the tick mark)
|
// Whether the object is selected (show the tick mark)
|
||||||
picked: { type: Object, default: null },
|
picked: { type: Object, default: null },
|
||||||
// Whether the request is being saved (activate 'select' event)
|
// Whether the request is being saved (activate 'select' event)
|
||||||
saveRequest: { type: Boolean, default: false },
|
savingMode: { type: Boolean, default: false },
|
||||||
request: { type: Object as PropType<HoppGQLRequest>, default: () => ({}) },
|
request: { type: Object as PropType<HoppGQLRequest>, default: () => ({}) },
|
||||||
folderPath: { type: String, default: null },
|
folderPath: { type: String, default: null },
|
||||||
requestIndex: { type: Number, default: null },
|
requestIndex: { type: Number, default: null },
|
||||||
@@ -169,14 +169,16 @@ const isSelected = computed(
|
|||||||
|
|
||||||
const pick = () => {
|
const pick = () => {
|
||||||
emit("select", {
|
emit("select", {
|
||||||
pickedType: "gql-my-request",
|
picked: {
|
||||||
folderPath: props.folderPath,
|
pickedType: "gql-my-request",
|
||||||
requestIndex: props.requestIndex,
|
folderPath: props.folderPath,
|
||||||
|
requestIndex: props.requestIndex,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectRequest = () => {
|
const selectRequest = () => {
|
||||||
if (props.saveRequest) {
|
if (props.savingMode) {
|
||||||
pick()
|
pick()
|
||||||
} else {
|
} else {
|
||||||
setGQLSession({
|
setGQLSession({
|
||||||
@@ -211,7 +213,7 @@ const removeRequest = () => {
|
|||||||
props.picked.folderPath === props.folderPath &&
|
props.picked.folderPath === props.folderPath &&
|
||||||
props.picked.requestIndex === props.requestIndex
|
props.picked.requestIndex === props.requestIndex
|
||||||
) {
|
) {
|
||||||
emit("select", null)
|
emit("select", { picked: null })
|
||||||
}
|
}
|
||||||
|
|
||||||
removeGraphqlRequest(props.folderPath, props.requestIndex)
|
removeGraphqlRequest(props.folderPath, props.requestIndex)
|
||||||
|
|||||||
@@ -1,21 +1,18 @@
|
|||||||
<template>
|
<template>
|
||||||
<div :class="{ 'rounded border border-divider': saveRequest }">
|
<div :class="{ 'rounded border border-divider': savingMode }">
|
||||||
<div
|
<div
|
||||||
class="sticky z-10 flex flex-col flex-shrink-0 overflow-x-auto rounded-t bg-primary"
|
class="sticky top-0 z-10 flex flex-col flex-shrink-0 overflow-x-auto border-b divide-y divide-dividerLight border-dividerLight"
|
||||||
:style="
|
:class="{ 'bg-primary': !savingMode }"
|
||||||
saveRequest ? 'top: calc(-1 * var(--line-height-body))' : 'top: 0'
|
|
||||||
"
|
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
|
v-if="showCollActions"
|
||||||
v-model="filterText"
|
v-model="filterText"
|
||||||
type="search"
|
type="search"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
:placeholder="t('action.search')"
|
:placeholder="t('action.search')"
|
||||||
class="py-2 pl-4 pr-2 bg-transparent"
|
class="flex px-4 py-2 bg-transparent"
|
||||||
/>
|
/>
|
||||||
<div
|
<div class="flex justify-between flex-1">
|
||||||
class="flex justify-between flex-1 flex-shrink-0 border-y bg-primary border-dividerLight"
|
|
||||||
>
|
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
:icon="IconPlus"
|
:icon="IconPlus"
|
||||||
:label="t('action.new')"
|
:label="t('action.new')"
|
||||||
@@ -31,7 +28,7 @@
|
|||||||
:icon="IconHelpCircle"
|
:icon="IconHelpCircle"
|
||||||
/>
|
/>
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
v-if="!saveRequest"
|
v-if="showCollActions"
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
:title="t('modal.import_export')"
|
:title="t('modal.import_export')"
|
||||||
:icon="IconArchive"
|
:icon="IconArchive"
|
||||||
@@ -40,7 +37,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col">
|
<div class="flex-col">
|
||||||
<CollectionsGraphqlCollection
|
<CollectionsGraphqlCollection
|
||||||
v-for="(collection, index) in filteredCollections"
|
v-for="(collection, index) in filteredCollections"
|
||||||
:key="`collection-${index}`"
|
:key="`collection-${index}`"
|
||||||
@@ -49,7 +46,7 @@
|
|||||||
:collection-index="index"
|
:collection-index="index"
|
||||||
:collection="collection"
|
:collection="collection"
|
||||||
:is-filtered="filterText.length > 0"
|
:is-filtered="filterText.length > 0"
|
||||||
:save-request="saveRequest"
|
:saving-mode="savingMode"
|
||||||
@edit-collection="editCollection(collection, index)"
|
@edit-collection="editCollection(collection, index)"
|
||||||
@add-request="addRequest($event)"
|
@add-request="addRequest($event)"
|
||||||
@add-folder="addFolder($event)"
|
@add-folder="addFolder($event)"
|
||||||
@@ -157,8 +154,10 @@ import { useColorMode } from "@composables/theming"
|
|||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
// Whether to activate the ability to pick items (activates 'select' events)
|
// Whether to activate the ability to pick items (activates 'select' events)
|
||||||
saveRequest: { type: Boolean, default: false },
|
savingMode: { type: Boolean, default: false },
|
||||||
picked: { type: Object, default: null },
|
picked: { type: Object, default: null },
|
||||||
|
// Whether to show the 'New' and 'Import/Export' actions
|
||||||
|
showCollActions: { type: Boolean, default: true },
|
||||||
},
|
},
|
||||||
emits: ["select", "use-collection"],
|
emits: ["select", "use-collection"],
|
||||||
setup() {
|
setup() {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,354 @@
|
|||||||
|
<template>
|
||||||
|
<div class="flex flex-col" :class="[{ 'bg-primaryLight': dragging }]">
|
||||||
|
<div
|
||||||
|
class="flex items-stretch group"
|
||||||
|
@dragover.prevent
|
||||||
|
@drop.prevent="dropEvent"
|
||||||
|
@dragover="dragging = true"
|
||||||
|
@drop="dragging = false"
|
||||||
|
@dragleave="dragging = false"
|
||||||
|
@dragend="dragging = false"
|
||||||
|
@contextmenu.prevent="options.tippy.show()"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="flex items-center justify-center px-4 cursor-pointer"
|
||||||
|
@click="toggleShowChildren()"
|
||||||
|
>
|
||||||
|
<component
|
||||||
|
:is="getCollectionIcon"
|
||||||
|
class="svg-icons"
|
||||||
|
:class="{ 'text-accent': isSelected }"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
class="flex flex-1 min-w-0 py-2 pr-2 cursor-pointer transition group-hover:text-secondaryDark"
|
||||||
|
@click="toggleShowChildren()"
|
||||||
|
>
|
||||||
|
<span class="truncate" :class="{ 'text-accent': isSelected }">
|
||||||
|
{{ collection.name }}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<div class="flex">
|
||||||
|
<ButtonSecondary
|
||||||
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
|
:icon="IconFilePlus"
|
||||||
|
:title="t('request.new')"
|
||||||
|
class="hidden group-hover:inline-flex"
|
||||||
|
@click="
|
||||||
|
$emit('add-request', {
|
||||||
|
path: `${collectionIndex}`,
|
||||||
|
})
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
<ButtonSecondary
|
||||||
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
|
:icon="IconFolderPlus"
|
||||||
|
:title="t('folder.new')"
|
||||||
|
class="hidden group-hover:inline-flex"
|
||||||
|
@click="
|
||||||
|
$emit('add-folder', {
|
||||||
|
folder: collection,
|
||||||
|
path: `${collectionIndex}`,
|
||||||
|
})
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
<span>
|
||||||
|
<tippy
|
||||||
|
ref="options"
|
||||||
|
interactive
|
||||||
|
trigger="click"
|
||||||
|
theme="popover"
|
||||||
|
:on-shown="() => tippyActions.focus()"
|
||||||
|
>
|
||||||
|
<ButtonSecondary
|
||||||
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
|
:title="t('action.more')"
|
||||||
|
:icon="IconMoreVertical"
|
||||||
|
/>
|
||||||
|
<template #content="{ hide }">
|
||||||
|
<div
|
||||||
|
ref="tippyActions"
|
||||||
|
class="flex flex-col focus:outline-none"
|
||||||
|
tabindex="0"
|
||||||
|
@keyup.r="requestAction.$el.click()"
|
||||||
|
@keyup.n="folderAction.$el.click()"
|
||||||
|
@keyup.e="edit.$el.click()"
|
||||||
|
@keyup.delete="deleteAction.$el.click()"
|
||||||
|
@keyup.x="exportAction.$el.click()"
|
||||||
|
@keyup.escape="hide()"
|
||||||
|
>
|
||||||
|
<SmartItem
|
||||||
|
ref="requestAction"
|
||||||
|
:icon="IconFilePlus"
|
||||||
|
:label="t('request.new')"
|
||||||
|
:shortcut="['R']"
|
||||||
|
@click="
|
||||||
|
() => {
|
||||||
|
$emit('add-request', {
|
||||||
|
path: `${collectionIndex}`,
|
||||||
|
})
|
||||||
|
hide()
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
<SmartItem
|
||||||
|
ref="folderAction"
|
||||||
|
:icon="IconFolderPlus"
|
||||||
|
:label="t('folder.new')"
|
||||||
|
:shortcut="['N']"
|
||||||
|
@click="
|
||||||
|
() => {
|
||||||
|
$emit('add-folder', {
|
||||||
|
folder: collection,
|
||||||
|
path: `${collectionIndex}`,
|
||||||
|
})
|
||||||
|
hide()
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
<SmartItem
|
||||||
|
ref="edit"
|
||||||
|
:icon="IconEdit"
|
||||||
|
:label="t('action.edit')"
|
||||||
|
:shortcut="['E']"
|
||||||
|
@click="
|
||||||
|
() => {
|
||||||
|
$emit('edit-collection')
|
||||||
|
hide()
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
<SmartItem
|
||||||
|
ref="exportAction"
|
||||||
|
:icon="IconDownload"
|
||||||
|
:label="t('export.title')"
|
||||||
|
:shortcut="['X']"
|
||||||
|
@click="
|
||||||
|
() => {
|
||||||
|
exportCollection()
|
||||||
|
hide()
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
<SmartItem
|
||||||
|
ref="deleteAction"
|
||||||
|
:icon="IconTrash2"
|
||||||
|
:label="t('action.delete')"
|
||||||
|
:shortcut="['⌫']"
|
||||||
|
@click="
|
||||||
|
() => {
|
||||||
|
removeCollection()
|
||||||
|
hide()
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</tippy>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="showChildren || isFiltered" class="flex">
|
||||||
|
<div
|
||||||
|
class="bg-dividerLight cursor-nsResize flex ml-5.5 transform transition w-1 hover:bg-dividerDark hover:scale-x-125"
|
||||||
|
@click="toggleShowChildren()"
|
||||||
|
></div>
|
||||||
|
<div class="flex flex-col flex-1 truncate">
|
||||||
|
<CollectionsMyFolder
|
||||||
|
v-for="(folder, index) in collection.folders"
|
||||||
|
:key="`folder-${index}`"
|
||||||
|
:folder="folder"
|
||||||
|
:folder-index="index"
|
||||||
|
:folder-path="`${collectionIndex}/${index}`"
|
||||||
|
:collection-index="collectionIndex"
|
||||||
|
:save-request="saveRequest"
|
||||||
|
:collections-type="collectionsType"
|
||||||
|
:is-filtered="isFiltered"
|
||||||
|
:picked="picked"
|
||||||
|
@add-request="$emit('add-request', $event)"
|
||||||
|
@add-folder="$emit('add-folder', $event)"
|
||||||
|
@edit-folder="$emit('edit-folder', $event)"
|
||||||
|
@edit-request="$emit('edit-request', $event)"
|
||||||
|
@duplicate-request="$emit('duplicate-request', $event)"
|
||||||
|
@select="$emit('select', $event)"
|
||||||
|
@remove-request="$emit('remove-request', $event)"
|
||||||
|
@remove-folder="$emit('remove-folder', $event)"
|
||||||
|
/>
|
||||||
|
<CollectionsMyRequest
|
||||||
|
v-for="(request, index) in collection.requests"
|
||||||
|
:key="`request-${index}`"
|
||||||
|
:request="request"
|
||||||
|
:collection-index="collectionIndex"
|
||||||
|
:folder-index="-1"
|
||||||
|
:folder-name="collection.name"
|
||||||
|
:folder-path="`${collectionIndex}`"
|
||||||
|
:request-index="index"
|
||||||
|
:save-request="saveRequest"
|
||||||
|
:collections-type="collectionsType"
|
||||||
|
:picked="picked"
|
||||||
|
@edit-request="$emit('edit-request', $event)"
|
||||||
|
@duplicate-request="$emit('duplicate-request', $event)"
|
||||||
|
@select="$emit('select', $event)"
|
||||||
|
@remove-request="$emit('remove-request', $event)"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
v-if="
|
||||||
|
(collection.folders == undefined ||
|
||||||
|
collection.folders.length === 0) &&
|
||||||
|
(collection.requests == undefined ||
|
||||||
|
collection.requests.length === 0)
|
||||||
|
"
|
||||||
|
class="flex flex-col items-center justify-center p-4 text-secondaryLight"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
:src="`/images/states/${colorMode.value}/pack.svg`"
|
||||||
|
loading="lazy"
|
||||||
|
class="inline-flex flex-col object-contain object-center w-16 h-16 mb-4"
|
||||||
|
:alt="`${t('empty.collection')}`"
|
||||||
|
/>
|
||||||
|
<span class="text-center">
|
||||||
|
{{ t("empty.collection") }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import IconCircle from "~icons/lucide/circle"
|
||||||
|
import IconCheckCircle from "~icons/lucide/check-circle"
|
||||||
|
import IconFolderPlus from "~icons/lucide/folder-plus"
|
||||||
|
import IconFilePlus from "~icons/lucide/file-plus"
|
||||||
|
import IconMoreVertical from "~icons/lucide/more-vertical"
|
||||||
|
import IconDownload from "~icons/lucide/download"
|
||||||
|
import IconTrash2 from "~icons/lucide/trash-2"
|
||||||
|
import IconEdit from "~icons/lucide/edit"
|
||||||
|
import IconFolder from "~icons/lucide/folder"
|
||||||
|
import IconFolderOpen from "~icons/lucide/folder-open"
|
||||||
|
import { useColorMode } from "@composables/theming"
|
||||||
|
import { useI18n } from "@composables/i18n"
|
||||||
|
import { useToast } from "@composables/toast"
|
||||||
|
import { defineComponent, ref, markRaw } from "vue"
|
||||||
|
import { moveRESTRequest } from "~/newstore/collections"
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
props: {
|
||||||
|
collectionIndex: { type: Number, default: null },
|
||||||
|
collection: { type: Object, default: () => ({}) },
|
||||||
|
isFiltered: Boolean,
|
||||||
|
saveRequest: Boolean,
|
||||||
|
collectionsType: { type: Object, default: () => ({}) },
|
||||||
|
picked: { type: Object, default: () => ({}) },
|
||||||
|
},
|
||||||
|
emits: [
|
||||||
|
"select",
|
||||||
|
"expand-collection",
|
||||||
|
"add-collection",
|
||||||
|
"remove-collection",
|
||||||
|
"add-folder",
|
||||||
|
"add-request",
|
||||||
|
"edit-folder",
|
||||||
|
"edit-request",
|
||||||
|
"duplicate-request",
|
||||||
|
"remove-folder",
|
||||||
|
"remove-request",
|
||||||
|
"select-collection",
|
||||||
|
"unselect-collection",
|
||||||
|
"edit-collection",
|
||||||
|
],
|
||||||
|
setup() {
|
||||||
|
return {
|
||||||
|
colorMode: useColorMode(),
|
||||||
|
toast: useToast(),
|
||||||
|
t: useI18n(),
|
||||||
|
|
||||||
|
// Template refs
|
||||||
|
tippyActions: ref<any | null>(null),
|
||||||
|
options: ref<any | null>(null),
|
||||||
|
requestAction: ref<any | null>(null),
|
||||||
|
folderAction: ref<any | null>(null),
|
||||||
|
edit: ref<any | null>(null),
|
||||||
|
deleteAction: ref<any | null>(null),
|
||||||
|
exportAction: ref<any | null>(null),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
IconCircle: markRaw(IconCircle),
|
||||||
|
IconCheckCircle: markRaw(IconCheckCircle),
|
||||||
|
IconFilePlus: markRaw(IconFilePlus),
|
||||||
|
IconFolderPlus: markRaw(IconFolderPlus),
|
||||||
|
IconMoreVertical: markRaw(IconMoreVertical),
|
||||||
|
IconEdit: markRaw(IconEdit),
|
||||||
|
IconDownload: markRaw(IconDownload),
|
||||||
|
IconTrash2: markRaw(IconTrash2),
|
||||||
|
|
||||||
|
showChildren: false,
|
||||||
|
dragging: false,
|
||||||
|
selectedFolder: {},
|
||||||
|
prevCursor: "",
|
||||||
|
cursor: "",
|
||||||
|
pageNo: 0,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
isSelected(): boolean {
|
||||||
|
return (
|
||||||
|
this.picked &&
|
||||||
|
this.picked.pickedType === "my-collection" &&
|
||||||
|
this.picked.collectionIndex === this.collectionIndex
|
||||||
|
)
|
||||||
|
},
|
||||||
|
getCollectionIcon() {
|
||||||
|
if (this.isSelected) return IconCheckCircle
|
||||||
|
else if (!this.showChildren && !this.isFiltered) return IconFolder
|
||||||
|
else if (this.showChildren || this.isFiltered) return IconFolderOpen
|
||||||
|
else return IconFolder
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
exportCollection() {
|
||||||
|
const collectionJSON = JSON.stringify(this.collection)
|
||||||
|
|
||||||
|
const file = new Blob([collectionJSON], { type: "application/json" })
|
||||||
|
const a = document.createElement("a")
|
||||||
|
const url = URL.createObjectURL(file)
|
||||||
|
a.href = url
|
||||||
|
|
||||||
|
a.download = `${this.collection.name}.json`
|
||||||
|
document.body.appendChild(a)
|
||||||
|
a.click()
|
||||||
|
this.toast.success(this.t("state.download_started").toString())
|
||||||
|
setTimeout(() => {
|
||||||
|
document.body.removeChild(a)
|
||||||
|
URL.revokeObjectURL(url)
|
||||||
|
}, 1000)
|
||||||
|
},
|
||||||
|
toggleShowChildren() {
|
||||||
|
if (this.$props.saveRequest)
|
||||||
|
this.$emit("select", {
|
||||||
|
picked: {
|
||||||
|
pickedType: "my-collection",
|
||||||
|
collectionIndex: this.collectionIndex,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
this.$emit("expand-collection", this.collection.id)
|
||||||
|
this.showChildren = !this.showChildren
|
||||||
|
},
|
||||||
|
removeCollection() {
|
||||||
|
this.$emit("remove-collection", {
|
||||||
|
collectionIndex: this.collectionIndex,
|
||||||
|
collectionID: this.collection.id,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
dropEvent({ dataTransfer }: any) {
|
||||||
|
this.dragging = !this.dragging
|
||||||
|
const folderPath = dataTransfer.getData("folderPath")
|
||||||
|
const requestIndex = dataTransfer.getData("requestIndex")
|
||||||
|
moveRESTRequest(folderPath, requestIndex, `${this.collectionIndex}`)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,340 @@
|
|||||||
|
<template>
|
||||||
|
<div class="flex flex-col" :class="[{ 'bg-primaryLight': dragging }]">
|
||||||
|
<div
|
||||||
|
class="flex items-stretch group"
|
||||||
|
@dragover.prevent
|
||||||
|
@drop.prevent="dropEvent"
|
||||||
|
@dragover="dragging = true"
|
||||||
|
@drop="dragging = false"
|
||||||
|
@dragleave="dragging = false"
|
||||||
|
@dragend="dragging = false"
|
||||||
|
@contextmenu.prevent="options.tippy.show()"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="flex items-center justify-center px-4 cursor-pointer"
|
||||||
|
@click="toggleShowChildren()"
|
||||||
|
>
|
||||||
|
<component
|
||||||
|
:is="getCollectionIcon"
|
||||||
|
class="svg-icons"
|
||||||
|
:class="{ 'text-accent': isSelected }"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
class="flex flex-1 min-w-0 py-2 pr-2 cursor-pointer transition group-hover:text-secondaryDark"
|
||||||
|
@click="toggleShowChildren()"
|
||||||
|
>
|
||||||
|
<span class="truncate" :class="{ 'text-accent': isSelected }">
|
||||||
|
{{ folder.name ? folder.name : folder.title }}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<div class="flex">
|
||||||
|
<ButtonSecondary
|
||||||
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
|
:icon="IconFilePlus"
|
||||||
|
:title="t('request.new')"
|
||||||
|
class="hidden group-hover:inline-flex"
|
||||||
|
@click="$emit('add-request', { path: folderPath })"
|
||||||
|
/>
|
||||||
|
<ButtonSecondary
|
||||||
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
|
:icon="IconFolderPlus"
|
||||||
|
:title="t('folder.new')"
|
||||||
|
class="hidden group-hover:inline-flex"
|
||||||
|
@click="$emit('add-folder', { folder, path: folderPath })"
|
||||||
|
/>
|
||||||
|
<span>
|
||||||
|
<tippy
|
||||||
|
ref="options"
|
||||||
|
interactive
|
||||||
|
trigger="click"
|
||||||
|
theme="popover"
|
||||||
|
:on-shown="() => tippyActions.focus()"
|
||||||
|
>
|
||||||
|
<ButtonSecondary
|
||||||
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
|
:title="t('action.more')"
|
||||||
|
:icon="IconMoreVertical"
|
||||||
|
/>
|
||||||
|
<template #content="{ hide }">
|
||||||
|
<div
|
||||||
|
ref="tippyActions"
|
||||||
|
class="flex flex-col focus:outline-none"
|
||||||
|
tabindex="0"
|
||||||
|
@keyup.r="requestAction.$el.click()"
|
||||||
|
@keyup.n="folderAction.$el.click()"
|
||||||
|
@keyup.e="edit.$el.click()"
|
||||||
|
@keyup.delete="deleteAction.$el.click()"
|
||||||
|
@keyup.x="exportAction.$el.click()"
|
||||||
|
@keyup.escape="hide()"
|
||||||
|
>
|
||||||
|
<SmartItem
|
||||||
|
ref="requestAction"
|
||||||
|
:icon="IconFilePlus"
|
||||||
|
:label="t('request.new')"
|
||||||
|
:shortcut="['R']"
|
||||||
|
@click="
|
||||||
|
() => {
|
||||||
|
$emit('add-request', { path: folderPath })
|
||||||
|
hide()
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
<SmartItem
|
||||||
|
ref="folderAction"
|
||||||
|
:icon="IconFolderPlus"
|
||||||
|
:label="t('folder.new')"
|
||||||
|
:shortcut="['N']"
|
||||||
|
@click="
|
||||||
|
() => {
|
||||||
|
$emit('add-folder', { folder, path: folderPath })
|
||||||
|
hide()
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
<SmartItem
|
||||||
|
ref="edit"
|
||||||
|
:icon="IconEdit"
|
||||||
|
:label="t('action.edit')"
|
||||||
|
:shortcut="['E']"
|
||||||
|
@click="
|
||||||
|
() => {
|
||||||
|
$emit('edit-folder', {
|
||||||
|
folder,
|
||||||
|
folderIndex,
|
||||||
|
collectionIndex,
|
||||||
|
folderPath,
|
||||||
|
})
|
||||||
|
hide()
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
<SmartItem
|
||||||
|
ref="exportAction"
|
||||||
|
:icon="IconDownload"
|
||||||
|
:label="t('export.title')"
|
||||||
|
:shortcut="['X']"
|
||||||
|
@click="
|
||||||
|
() => {
|
||||||
|
exportFolder()
|
||||||
|
hide()
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
<SmartItem
|
||||||
|
ref="deleteAction"
|
||||||
|
:icon="IconTrash2"
|
||||||
|
:label="t('action.delete')"
|
||||||
|
:shortcut="['⌫']"
|
||||||
|
@click="
|
||||||
|
() => {
|
||||||
|
removeFolder()
|
||||||
|
hide()
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</tippy>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="showChildren || isFiltered" class="flex">
|
||||||
|
<div
|
||||||
|
class="bg-dividerLight cursor-nsResize flex ml-5.5 transform transition w-1 hover:bg-dividerDark hover:scale-x-125"
|
||||||
|
@click="toggleShowChildren()"
|
||||||
|
></div>
|
||||||
|
<div class="flex flex-col flex-1 truncate">
|
||||||
|
<!-- Referring to this component only (this is recursive) -->
|
||||||
|
<Folder
|
||||||
|
v-for="(subFolder, subFolderIndex) in folder.folders"
|
||||||
|
:key="`subFolder-${subFolderIndex}`"
|
||||||
|
:folder="subFolder"
|
||||||
|
:folder-index="subFolderIndex"
|
||||||
|
:collection-index="collectionIndex"
|
||||||
|
:save-request="saveRequest"
|
||||||
|
:collections-type="collectionsType"
|
||||||
|
:folder-path="`${folderPath}/${subFolderIndex}`"
|
||||||
|
:picked="picked"
|
||||||
|
@add-request="$emit('add-request', $event)"
|
||||||
|
@add-folder="$emit('add-folder', $event)"
|
||||||
|
@edit-folder="$emit('edit-folder', $event)"
|
||||||
|
@edit-request="$emit('edit-request', $event)"
|
||||||
|
@duplicate-request="$emit('duplicate-request', $event)"
|
||||||
|
@update-team-collections="$emit('update-team-collections')"
|
||||||
|
@select="$emit('select', $event)"
|
||||||
|
@remove-request="$emit('remove-request', $event)"
|
||||||
|
@remove-folder="$emit('remove-folder', $event)"
|
||||||
|
/>
|
||||||
|
<CollectionsMyRequest
|
||||||
|
v-for="(request, index) in folder.requests"
|
||||||
|
:key="`request-${index}`"
|
||||||
|
:request="request"
|
||||||
|
:collection-index="collectionIndex"
|
||||||
|
:folder-index="folderIndex"
|
||||||
|
:folder-name="folder.name"
|
||||||
|
:folder-path="folderPath"
|
||||||
|
:request-index="index"
|
||||||
|
:picked="picked"
|
||||||
|
:save-request="saveRequest"
|
||||||
|
:collections-type="collectionsType"
|
||||||
|
@edit-request="$emit('edit-request', $event)"
|
||||||
|
@duplicate-request="$emit('duplicate-request', $event)"
|
||||||
|
@select="$emit('select', $event)"
|
||||||
|
@remove-request="$emit('remove-request', $event)"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
v-if="
|
||||||
|
folder.folders &&
|
||||||
|
folder.folders.length === 0 &&
|
||||||
|
folder.requests &&
|
||||||
|
folder.requests.length === 0
|
||||||
|
"
|
||||||
|
class="flex flex-col items-center justify-center p-4 text-secondaryLight"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
:src="`/images/states/${colorMode.value}/pack.svg`"
|
||||||
|
loading="lazy"
|
||||||
|
class="inline-flex flex-col object-contain object-center w-16 h-16 mb-4"
|
||||||
|
:alt="`${t('empty.folder')}`"
|
||||||
|
/>
|
||||||
|
<span class="text-center">
|
||||||
|
{{ t("empty.folder") }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import IconFilePlus from "~icons/lucide/file-plus"
|
||||||
|
import IconFolderPlus from "~icons/lucide/folder-plus"
|
||||||
|
import IconMoreVertical from "~icons/lucide/more-vertical"
|
||||||
|
import IconEdit from "~icons/lucide/edit"
|
||||||
|
import IconDownload from "~icons/lucide/download"
|
||||||
|
import IconTrash2 from "~icons/lucide/trash-2"
|
||||||
|
import IconFolder from "~icons/lucide/folder"
|
||||||
|
import IconCheckCircle from "~icons/lucide/check-circle"
|
||||||
|
import IconFolderOpen from "~icons/lucide/folder-open"
|
||||||
|
import { defineComponent, ref } from "vue"
|
||||||
|
import { useColorMode } from "@composables/theming"
|
||||||
|
import { useToast } from "@composables/toast"
|
||||||
|
import { useI18n } from "@composables/i18n"
|
||||||
|
import { moveRESTRequest } from "~/newstore/collections"
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: "Folder",
|
||||||
|
props: {
|
||||||
|
folder: { type: Object, default: () => ({}) },
|
||||||
|
folderIndex: { type: Number, default: null },
|
||||||
|
collectionIndex: { type: Number, default: null },
|
||||||
|
folderPath: { type: String, default: null },
|
||||||
|
saveRequest: Boolean,
|
||||||
|
isFiltered: Boolean,
|
||||||
|
collectionsType: { type: Object, default: () => ({}) },
|
||||||
|
picked: { type: Object, default: () => ({}) },
|
||||||
|
},
|
||||||
|
emits: [
|
||||||
|
"add-request",
|
||||||
|
"add-folder",
|
||||||
|
"edit-folder",
|
||||||
|
"update-team",
|
||||||
|
"remove-folder",
|
||||||
|
"edit-request",
|
||||||
|
"duplicate-request",
|
||||||
|
"select",
|
||||||
|
"remove-request",
|
||||||
|
"update-team-collections",
|
||||||
|
],
|
||||||
|
setup() {
|
||||||
|
const t = useI18n()
|
||||||
|
|
||||||
|
return {
|
||||||
|
// Template refs
|
||||||
|
tippyActions: ref<any | null>(null),
|
||||||
|
options: ref<any | null>(null),
|
||||||
|
requestAction: ref<any | null>(null),
|
||||||
|
folderAction: ref<any | null>(null),
|
||||||
|
edit: ref<any | null>(null),
|
||||||
|
deleteAction: ref<any | null>(null),
|
||||||
|
exportAction: ref<any | null>(null),
|
||||||
|
t,
|
||||||
|
toast: useToast(),
|
||||||
|
colorMode: useColorMode(),
|
||||||
|
IconFilePlus,
|
||||||
|
IconFolderPlus,
|
||||||
|
IconMoreVertical,
|
||||||
|
IconEdit,
|
||||||
|
IconDownload,
|
||||||
|
IconTrash2,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
showChildren: false,
|
||||||
|
dragging: false,
|
||||||
|
prevCursor: "",
|
||||||
|
cursor: "",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
isSelected(): boolean {
|
||||||
|
return (
|
||||||
|
this.picked &&
|
||||||
|
this.picked.pickedType === "my-folder" &&
|
||||||
|
this.picked.folderPath === this.folderPath
|
||||||
|
)
|
||||||
|
},
|
||||||
|
getCollectionIcon() {
|
||||||
|
if (this.isSelected) return IconCheckCircle
|
||||||
|
else if (!this.showChildren && !this.isFiltered) return IconFolder
|
||||||
|
else if (this.showChildren || this.isFiltered) return IconFolderOpen
|
||||||
|
else return IconFolder
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
exportFolder() {
|
||||||
|
const folderJSON = JSON.stringify(this.folder)
|
||||||
|
|
||||||
|
const file = new Blob([folderJSON], { type: "application/json" })
|
||||||
|
const a = document.createElement("a")
|
||||||
|
const url = URL.createObjectURL(file)
|
||||||
|
a.href = url
|
||||||
|
|
||||||
|
a.download = `${this.folder.name}.json`
|
||||||
|
document.body.appendChild(a)
|
||||||
|
a.click()
|
||||||
|
this.toast.success(this.t("state.download_started").toString())
|
||||||
|
setTimeout(() => {
|
||||||
|
document.body.removeChild(a)
|
||||||
|
URL.revokeObjectURL(url)
|
||||||
|
}, 1000)
|
||||||
|
},
|
||||||
|
toggleShowChildren() {
|
||||||
|
if (this.$props.saveRequest)
|
||||||
|
this.$emit("select", {
|
||||||
|
picked: {
|
||||||
|
pickedType: "my-folder",
|
||||||
|
collectionIndex: this.collectionIndex,
|
||||||
|
folderName: this.folder.name,
|
||||||
|
folderPath: this.folderPath,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
this.showChildren = !this.showChildren
|
||||||
|
},
|
||||||
|
removeFolder() {
|
||||||
|
this.$emit("remove-folder", {
|
||||||
|
folder: this.folder,
|
||||||
|
folderPath: this.folderPath,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
dropEvent({ dataTransfer }) {
|
||||||
|
this.dragging = !this.dragging
|
||||||
|
const folderPath = dataTransfer.getData("folderPath")
|
||||||
|
const requestIndex = dataTransfer.getData("requestIndex")
|
||||||
|
moveRESTRequest(folderPath, requestIndex, this.folderPath)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,433 @@
|
|||||||
|
<template>
|
||||||
|
<div class="flex flex-col" :class="[{ 'bg-primaryLight': dragging }]">
|
||||||
|
<div
|
||||||
|
class="flex items-stretch group"
|
||||||
|
draggable="true"
|
||||||
|
@dragstart="dragStart"
|
||||||
|
@dragover.stop
|
||||||
|
@dragleave="dragging = false"
|
||||||
|
@dragend="dragging = false"
|
||||||
|
@contextmenu.prevent="options.tippy.show()"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="flex items-center justify-center w-16 px-2 truncate cursor-pointer"
|
||||||
|
:class="getRequestLabelColor(request.method)"
|
||||||
|
@click="selectRequest()"
|
||||||
|
>
|
||||||
|
<component
|
||||||
|
:is="IconCheckCircle"
|
||||||
|
v-if="isSelected"
|
||||||
|
class="svg-icons"
|
||||||
|
:class="{ 'text-accent': isSelected }"
|
||||||
|
/>
|
||||||
|
<span v-else class="font-semibold truncate text-tiny">
|
||||||
|
{{ request.method }}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
class="flex items-center flex-1 min-w-0 py-2 pr-2 cursor-pointer transition group-hover:text-secondaryDark"
|
||||||
|
@click="selectRequest()"
|
||||||
|
>
|
||||||
|
<span class="truncate" :class="{ 'text-accent': isSelected }">
|
||||||
|
{{ request.name }}
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
v-if="isActive"
|
||||||
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
|
class="relative h-1.5 w-1.5 flex flex-shrink-0 mx-3"
|
||||||
|
:title="`${t('collection.request_in_use')}`"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="absolute inline-flex flex-shrink-0 w-full h-full bg-green-500 rounded-full opacity-75 animate-ping"
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
class="relative inline-flex flex-shrink-0 rounded-full h-1.5 w-1.5 bg-green-500"
|
||||||
|
></span>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<div class="flex">
|
||||||
|
<ButtonSecondary
|
||||||
|
v-if="!saveRequest"
|
||||||
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
|
:icon="IconRotateCCW"
|
||||||
|
:title="t('action.restore')"
|
||||||
|
class="hidden group-hover:inline-flex"
|
||||||
|
@click="selectRequest()"
|
||||||
|
/>
|
||||||
|
<span>
|
||||||
|
<tippy
|
||||||
|
ref="options"
|
||||||
|
interactive
|
||||||
|
trigger="click"
|
||||||
|
theme="popover"
|
||||||
|
:on-shown="() => tippyActions.focus()"
|
||||||
|
>
|
||||||
|
<ButtonSecondary
|
||||||
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
|
:title="t('action.more')"
|
||||||
|
:icon="IconMoreVertical"
|
||||||
|
/>
|
||||||
|
<template #content="{ hide }">
|
||||||
|
<div
|
||||||
|
ref="tippyActions"
|
||||||
|
class="flex flex-col focus:outline-none"
|
||||||
|
tabindex="0"
|
||||||
|
@keyup.e="edit.$el.click()"
|
||||||
|
@keyup.d="duplicate.$el.click()"
|
||||||
|
@keyup.delete="deleteAction.$el.click()"
|
||||||
|
@keyup.escape="hide()"
|
||||||
|
>
|
||||||
|
<SmartItem
|
||||||
|
ref="edit"
|
||||||
|
:icon="IconEdit"
|
||||||
|
:label="t('action.edit')"
|
||||||
|
:shortcut="['E']"
|
||||||
|
@click="
|
||||||
|
() => {
|
||||||
|
emit('edit-request', {
|
||||||
|
collectionIndex,
|
||||||
|
folderIndex,
|
||||||
|
folderName,
|
||||||
|
request,
|
||||||
|
requestIndex,
|
||||||
|
folderPath,
|
||||||
|
})
|
||||||
|
hide()
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
<SmartItem
|
||||||
|
ref="duplicate"
|
||||||
|
:icon="IconCopy"
|
||||||
|
:label="t('action.duplicate')"
|
||||||
|
:shortcut="['D']"
|
||||||
|
@click="
|
||||||
|
() => {
|
||||||
|
emit('duplicate-request', {
|
||||||
|
collectionIndex,
|
||||||
|
folderIndex,
|
||||||
|
folderName,
|
||||||
|
request,
|
||||||
|
requestIndex,
|
||||||
|
folderPath,
|
||||||
|
})
|
||||||
|
hide()
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
<SmartItem
|
||||||
|
ref="deleteAction"
|
||||||
|
:icon="IconTrash2"
|
||||||
|
:label="t('action.delete')"
|
||||||
|
:shortcut="['⌫']"
|
||||||
|
@click="
|
||||||
|
() => {
|
||||||
|
removeRequest()
|
||||||
|
hide()
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</tippy>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<HttpReqChangeConfirmModal
|
||||||
|
:show="confirmChange"
|
||||||
|
@hide-modal="confirmChange = false"
|
||||||
|
@save-change="saveRequestChange"
|
||||||
|
@discard-change="discardRequestChange"
|
||||||
|
/>
|
||||||
|
<CollectionsSaveRequest
|
||||||
|
mode="rest"
|
||||||
|
:show="showSaveRequestModal"
|
||||||
|
@hide-modal="showSaveRequestModal = false"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import IconCheckCircle from "~icons/lucide/check-circle"
|
||||||
|
import IconMoreVertical from "~icons/lucide/more-vertical"
|
||||||
|
import IconEdit from "~icons/lucide/edit"
|
||||||
|
import IconCopy from "~icons/lucide/copy"
|
||||||
|
import IconTrash2 from "~icons/lucide/trash-2"
|
||||||
|
import IconRotateCCW from "~icons/lucide/rotate-ccw"
|
||||||
|
import { ref, computed } from "vue"
|
||||||
|
import {
|
||||||
|
HoppRESTRequest,
|
||||||
|
safelyExtractRESTRequest,
|
||||||
|
translateToNewRequest,
|
||||||
|
isEqualHoppRESTRequest,
|
||||||
|
} from "@hoppscotch/data"
|
||||||
|
import * as E from "fp-ts/Either"
|
||||||
|
import { cloneDeep } from "lodash-es"
|
||||||
|
import { useI18n } from "@composables/i18n"
|
||||||
|
import { useToast } from "@composables/toast"
|
||||||
|
import { useReadonlyStream } from "@composables/stream"
|
||||||
|
import {
|
||||||
|
getDefaultRESTRequest,
|
||||||
|
getRESTRequest,
|
||||||
|
restSaveContext$,
|
||||||
|
setRESTRequest,
|
||||||
|
setRESTSaveContext,
|
||||||
|
getRESTSaveContext,
|
||||||
|
} from "~/newstore/RESTSession"
|
||||||
|
import { editRESTRequest } from "~/newstore/collections"
|
||||||
|
import { runMutation } from "~/helpers/backend/GQLClient"
|
||||||
|
import { UpdateRequestDocument } from "~/helpers/backend/graphql"
|
||||||
|
import { HoppRequestSaveContext } from "~/helpers/types/HoppRequestSaveContext"
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
request: HoppRESTRequest
|
||||||
|
collectionIndex: number
|
||||||
|
folderIndex: number
|
||||||
|
folderName: string
|
||||||
|
requestIndex: number
|
||||||
|
saveRequest: boolean
|
||||||
|
collectionsType: object
|
||||||
|
folderPath: string
|
||||||
|
picked?: {
|
||||||
|
pickedType: string
|
||||||
|
collectionIndex: number
|
||||||
|
folderPath: string
|
||||||
|
folderName: string
|
||||||
|
requestIndex: number
|
||||||
|
}
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(
|
||||||
|
e: "select",
|
||||||
|
data:
|
||||||
|
| {
|
||||||
|
picked: {
|
||||||
|
pickedType: string
|
||||||
|
collectionIndex: number
|
||||||
|
folderPath: string
|
||||||
|
folderName: string
|
||||||
|
requestIndex: number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
| undefined
|
||||||
|
): void
|
||||||
|
|
||||||
|
(
|
||||||
|
e: "remove-request",
|
||||||
|
data: {
|
||||||
|
folderPath: string
|
||||||
|
requestIndex: number
|
||||||
|
}
|
||||||
|
): void
|
||||||
|
|
||||||
|
(
|
||||||
|
e: "duplicate-request",
|
||||||
|
data: {
|
||||||
|
collectionIndex: number
|
||||||
|
folderIndex: number
|
||||||
|
folderName: string
|
||||||
|
request: HoppRESTRequest
|
||||||
|
folderPath: string
|
||||||
|
requestIndex: number
|
||||||
|
}
|
||||||
|
): void
|
||||||
|
|
||||||
|
(
|
||||||
|
e: "edit-request",
|
||||||
|
data: {
|
||||||
|
collectionIndex: number
|
||||||
|
folderIndex: number
|
||||||
|
folderName: string
|
||||||
|
request: HoppRESTRequest
|
||||||
|
folderPath: string
|
||||||
|
requestIndex: number
|
||||||
|
}
|
||||||
|
): void
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const t = useI18n()
|
||||||
|
const toast = useToast()
|
||||||
|
|
||||||
|
const dragging = ref(false)
|
||||||
|
const requestMethodLabels = {
|
||||||
|
get: "text-green-500",
|
||||||
|
post: "text-yellow-500",
|
||||||
|
put: "text-blue-500",
|
||||||
|
delete: "text-red-500",
|
||||||
|
default: "text-gray-500",
|
||||||
|
}
|
||||||
|
const confirmChange = ref(false)
|
||||||
|
const showSaveRequestModal = ref(false)
|
||||||
|
|
||||||
|
// Template refs
|
||||||
|
const tippyActions = ref<any | null>(null)
|
||||||
|
const options = ref<any | null>(null)
|
||||||
|
const edit = ref<any | null>(null)
|
||||||
|
const duplicate = ref<any | null>(null)
|
||||||
|
const deleteAction = ref<any | null>(null)
|
||||||
|
|
||||||
|
const active = useReadonlyStream(restSaveContext$, null)
|
||||||
|
|
||||||
|
const isSelected = computed(
|
||||||
|
() =>
|
||||||
|
props.picked &&
|
||||||
|
props.picked.pickedType === "my-request" &&
|
||||||
|
props.picked.folderPath === props.folderPath &&
|
||||||
|
props.picked.requestIndex === props.requestIndex
|
||||||
|
)
|
||||||
|
|
||||||
|
const isActive = computed(
|
||||||
|
() =>
|
||||||
|
active.value &&
|
||||||
|
active.value.originLocation === "user-collection" &&
|
||||||
|
active.value.folderPath === props.folderPath &&
|
||||||
|
active.value.requestIndex === props.requestIndex
|
||||||
|
)
|
||||||
|
|
||||||
|
const dragStart = ({ dataTransfer }: DragEvent) => {
|
||||||
|
if (dataTransfer) {
|
||||||
|
dragging.value = !dragging.value
|
||||||
|
dataTransfer.setData("folderPath", props.folderPath)
|
||||||
|
dataTransfer.setData("requestIndex", props.requestIndex.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const removeRequest = () => {
|
||||||
|
emit("remove-request", {
|
||||||
|
folderPath: props.folderPath,
|
||||||
|
requestIndex: props.requestIndex,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const getRequestLabelColor = (method: string) =>
|
||||||
|
requestMethodLabels[
|
||||||
|
method.toLowerCase() as keyof typeof requestMethodLabels
|
||||||
|
] || requestMethodLabels.default
|
||||||
|
|
||||||
|
const setRestReq = (request: any) => {
|
||||||
|
setRESTRequest(
|
||||||
|
cloneDeep(
|
||||||
|
safelyExtractRESTRequest(
|
||||||
|
translateToNewRequest(request),
|
||||||
|
getDefaultRESTRequest()
|
||||||
|
)
|
||||||
|
),
|
||||||
|
{
|
||||||
|
originLocation: "user-collection",
|
||||||
|
folderPath: props.folderPath,
|
||||||
|
requestIndex: props.requestIndex,
|
||||||
|
req: cloneDeep(request),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Loads request from the save once, checks for unsaved changes, but ignores default values */
|
||||||
|
const selectRequest = () => {
|
||||||
|
// Check if this is a save as request popup, if so we don't need to prompt the confirm change popup.
|
||||||
|
if (props.saveRequest) {
|
||||||
|
emit("select", {
|
||||||
|
picked: {
|
||||||
|
pickedType: "my-request",
|
||||||
|
collectionIndex: props.collectionIndex,
|
||||||
|
folderPath: props.folderPath,
|
||||||
|
folderName: props.folderName,
|
||||||
|
requestIndex: props.requestIndex,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
} else if (isEqualHoppRESTRequest(props.request, getDefaultRESTRequest())) {
|
||||||
|
confirmChange.value = false
|
||||||
|
setRestReq(props.request)
|
||||||
|
} else if (!active.value) {
|
||||||
|
// If the current request is the same as the request to be loaded in, there is no data loss
|
||||||
|
const currentReq = getRESTRequest()
|
||||||
|
|
||||||
|
if (isEqualHoppRESTRequest(currentReq, props.request)) {
|
||||||
|
setRestReq(props.request)
|
||||||
|
} else {
|
||||||
|
confirmChange.value = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const currentReqWithNoChange = active.value.req
|
||||||
|
const currentFullReq = getRESTRequest()
|
||||||
|
|
||||||
|
// Check if whether user clicked the same request or not
|
||||||
|
if (!isActive.value && currentReqWithNoChange !== undefined) {
|
||||||
|
// Check if there is any changes done on the current request
|
||||||
|
if (isEqualHoppRESTRequest(currentReqWithNoChange, currentFullReq)) {
|
||||||
|
setRestReq(props.request)
|
||||||
|
} else {
|
||||||
|
confirmChange.value = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setRESTSaveContext(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Save current request to the collection */
|
||||||
|
const saveRequestChange = () => {
|
||||||
|
const saveCtx = getRESTSaveContext()
|
||||||
|
saveCurrentRequest(saveCtx)
|
||||||
|
confirmChange.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Discard changes and change the current request and context */
|
||||||
|
const discardRequestChange = () => {
|
||||||
|
setRestReq(props.request)
|
||||||
|
if (!isActive.value) {
|
||||||
|
setRESTSaveContext({
|
||||||
|
originLocation: "user-collection",
|
||||||
|
folderPath: props.folderPath,
|
||||||
|
requestIndex: props.requestIndex,
|
||||||
|
req: cloneDeep(props.request),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
confirmChange.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const saveCurrentRequest = (saveCtx: HoppRequestSaveContext | null) => {
|
||||||
|
if (!saveCtx) {
|
||||||
|
showSaveRequestModal.value = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (saveCtx.originLocation === "user-collection") {
|
||||||
|
try {
|
||||||
|
editRESTRequest(
|
||||||
|
saveCtx.folderPath,
|
||||||
|
saveCtx.requestIndex,
|
||||||
|
getRESTRequest()
|
||||||
|
)
|
||||||
|
setRestReq(props.request)
|
||||||
|
toast.success(`${t("request.saved")}`)
|
||||||
|
} catch (e) {
|
||||||
|
setRESTSaveContext(null)
|
||||||
|
saveCurrentRequest(saveCtx)
|
||||||
|
}
|
||||||
|
} else if (saveCtx.originLocation === "team-collection") {
|
||||||
|
const req = getRESTRequest()
|
||||||
|
try {
|
||||||
|
runMutation(UpdateRequestDocument, {
|
||||||
|
requestID: saveCtx.requestID,
|
||||||
|
data: {
|
||||||
|
title: req.name,
|
||||||
|
request: JSON.stringify(req),
|
||||||
|
},
|
||||||
|
})().then((result) => {
|
||||||
|
if (E.isLeft(result)) {
|
||||||
|
toast.error(`${t("profile.no_permission")}`)
|
||||||
|
} else {
|
||||||
|
toast.success(`${t("request.saved")}`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
setRestReq(props.request)
|
||||||
|
} catch (error) {
|
||||||
|
showSaveRequestModal.value = true
|
||||||
|
toast.error(`${t("error.something_went_wrong")}`)
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,408 @@
|
|||||||
|
<template>
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<div
|
||||||
|
class="flex items-stretch group"
|
||||||
|
@dragover.prevent
|
||||||
|
@drop.prevent="dropEvent"
|
||||||
|
@dragover="dragging = true"
|
||||||
|
@drop="dragging = false"
|
||||||
|
@dragleave="dragging = false"
|
||||||
|
@dragend="dragging = false"
|
||||||
|
@contextmenu.prevent="options!.tippy.show()"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="flex items-center justify-center px-4 cursor-pointer"
|
||||||
|
@click="toggleShowChildren()"
|
||||||
|
>
|
||||||
|
<component
|
||||||
|
:is="getCollectionIcon"
|
||||||
|
class="svg-icons"
|
||||||
|
:class="{ 'text-accent': isSelected }"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
class="flex flex-1 min-w-0 py-2 pr-2 cursor-pointer transition group-hover:text-secondaryDark"
|
||||||
|
@click="toggleShowChildren()"
|
||||||
|
>
|
||||||
|
<span class="truncate" :class="{ 'text-accent': isSelected }">
|
||||||
|
{{ collection.title }}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<div class="flex">
|
||||||
|
<ButtonSecondary
|
||||||
|
v-if="collectionsType.selectedTeam.myRole !== 'VIEWER'"
|
||||||
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
|
:icon="IconFilePlus"
|
||||||
|
:title="t('request.new')"
|
||||||
|
class="hidden group-hover:inline-flex"
|
||||||
|
@click="
|
||||||
|
$emit('add-request', {
|
||||||
|
folder: collection,
|
||||||
|
path: `${collectionIndex}`,
|
||||||
|
})
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
<ButtonSecondary
|
||||||
|
v-if="collectionsType.selectedTeam.myRole !== 'VIEWER'"
|
||||||
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
|
:icon="IconFolderPlus"
|
||||||
|
:title="t('folder.new')"
|
||||||
|
class="hidden group-hover:inline-flex"
|
||||||
|
@click="
|
||||||
|
$emit('add-folder', {
|
||||||
|
folder: collection,
|
||||||
|
path: `${collectionIndex}`,
|
||||||
|
})
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
<span>
|
||||||
|
<tippy
|
||||||
|
v-if="collectionsType.selectedTeam.myRole !== 'VIEWER'"
|
||||||
|
ref="options"
|
||||||
|
interactive
|
||||||
|
trigger="click"
|
||||||
|
theme="popover"
|
||||||
|
:on-shown="() => tippyActions!.focus()"
|
||||||
|
>
|
||||||
|
<ButtonSecondary
|
||||||
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
|
:title="t('action.more')"
|
||||||
|
:icon="IconMoreVertical"
|
||||||
|
/>
|
||||||
|
<template #content="{ hide }">
|
||||||
|
<div
|
||||||
|
ref="tippyActions"
|
||||||
|
class="flex flex-col focus:outline-none"
|
||||||
|
tabindex="0"
|
||||||
|
@keyup.r="requestAction!.$el.click()"
|
||||||
|
@keyup.n="folderAction!.$el.click()"
|
||||||
|
@keyup.e="edit!.$el.click()"
|
||||||
|
@keyup.delete="deleteAction!.$el.click()"
|
||||||
|
@keyup.x="exportAction!.$el.click()"
|
||||||
|
@keyup.escape="hide()"
|
||||||
|
>
|
||||||
|
<SmartItem
|
||||||
|
ref="requestAction"
|
||||||
|
:icon="IconFilePlus"
|
||||||
|
:label="t('request.new')"
|
||||||
|
:shortcut="['R']"
|
||||||
|
@click="
|
||||||
|
() => {
|
||||||
|
$emit('add-request', {
|
||||||
|
folder: collection,
|
||||||
|
path: `${collectionIndex}`,
|
||||||
|
})
|
||||||
|
hide()
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
<SmartItem
|
||||||
|
ref="folderAction"
|
||||||
|
:icon="IconFolderPlus"
|
||||||
|
:label="t('folder.new')"
|
||||||
|
:shortcut="['N']"
|
||||||
|
@click="
|
||||||
|
() => {
|
||||||
|
$emit('add-folder', {
|
||||||
|
folder: collection,
|
||||||
|
path: `${collectionIndex}`,
|
||||||
|
})
|
||||||
|
hide()
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
<SmartItem
|
||||||
|
ref="edit"
|
||||||
|
:icon="IconEdit"
|
||||||
|
:label="t('action.edit')"
|
||||||
|
:shortcut="['E']"
|
||||||
|
@click="
|
||||||
|
() => {
|
||||||
|
$emit('edit-collection')
|
||||||
|
hide()
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
<SmartItem
|
||||||
|
ref="exportAction"
|
||||||
|
:icon="IconDownload"
|
||||||
|
:label="t('export.title')"
|
||||||
|
:shortcut="['X']"
|
||||||
|
:loading="exportLoading"
|
||||||
|
@click="exportCollection"
|
||||||
|
/>
|
||||||
|
<SmartItem
|
||||||
|
ref="deleteAction"
|
||||||
|
:icon="IconTrash2"
|
||||||
|
:label="t('action.delete')"
|
||||||
|
:shortcut="['⌫']"
|
||||||
|
@click="
|
||||||
|
() => {
|
||||||
|
removeCollection()
|
||||||
|
hide()
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</tippy>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="showChildren || isFiltered" class="flex">
|
||||||
|
<div
|
||||||
|
class="bg-dividerLight cursor-nsResize flex ml-5.5 transform transition w-1 hover:bg-dividerDark hover:scale-x-125"
|
||||||
|
@click="toggleShowChildren()"
|
||||||
|
></div>
|
||||||
|
<div class="flex flex-col flex-1 truncate">
|
||||||
|
<CollectionsTeamsFolder
|
||||||
|
v-for="(folder, index) in collection.children"
|
||||||
|
:key="`folder-${index}`"
|
||||||
|
:folder="folder"
|
||||||
|
:folder-index="index"
|
||||||
|
:folder-path="`${collectionIndex}/${index}`"
|
||||||
|
:collection-index="collectionIndex"
|
||||||
|
:save-request="saveRequest"
|
||||||
|
:collections-type="collectionsType"
|
||||||
|
:is-filtered="isFiltered"
|
||||||
|
:picked="picked"
|
||||||
|
:loading-collection-i-ds="loadingCollectionIDs"
|
||||||
|
@add-request="$emit('add-request', $event)"
|
||||||
|
@add-folder="$emit('add-folder', $event)"
|
||||||
|
@edit-folder="$emit('edit-folder', $event)"
|
||||||
|
@edit-request="$emit('edit-request', $event)"
|
||||||
|
@select="$emit('select', $event)"
|
||||||
|
@expand-collection="expandCollection"
|
||||||
|
@remove-request="$emit('remove-request', $event)"
|
||||||
|
@remove-folder="$emit('remove-folder', $event)"
|
||||||
|
@duplicate-request="$emit('duplicate-request', $event)"
|
||||||
|
/>
|
||||||
|
<CollectionsTeamsRequest
|
||||||
|
v-for="(request, index) in collection.requests"
|
||||||
|
:key="`request-${index}`"
|
||||||
|
:request="request.request"
|
||||||
|
:collection-index="collectionIndex"
|
||||||
|
:folder-index="-1"
|
||||||
|
:folder-name="collection.name"
|
||||||
|
:request-index="request.id"
|
||||||
|
:save-request="saveRequest"
|
||||||
|
:collection-i-d="collection.id"
|
||||||
|
:collections-type="collectionsType"
|
||||||
|
:picked="picked"
|
||||||
|
@edit-request="editRequest($event)"
|
||||||
|
@select="$emit('select', $event)"
|
||||||
|
@remove-request="$emit('remove-request', $event)"
|
||||||
|
@duplicate-request="$emit('duplicate-request', $event)"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
v-if="loadingCollectionIDs.includes(collection.id)"
|
||||||
|
class="flex flex-col items-center justify-center p-4"
|
||||||
|
>
|
||||||
|
<SmartSpinner class="my-4" />
|
||||||
|
<span class="text-secondaryLight">{{ t("state.loading") }}</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-else-if="
|
||||||
|
(collection.children == undefined ||
|
||||||
|
collection.children.length === 0) &&
|
||||||
|
(collection.requests == undefined ||
|
||||||
|
collection.requests.length === 0)
|
||||||
|
"
|
||||||
|
class="flex flex-col items-center justify-center p-4 text-secondaryLight"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
:src="`/images/states/${colorMode.value}/pack.svg`"
|
||||||
|
loading="lazy"
|
||||||
|
class="inline-flex flex-col object-contain object-center w-16 h-16 mb-4"
|
||||||
|
:alt="`${t('empty.collection')}`"
|
||||||
|
/>
|
||||||
|
<span class="text-center">
|
||||||
|
{{ t("empty.collection") }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import IconMoreVertical from "~icons/lucide/more-vertical"
|
||||||
|
import IconTrash2 from "~icons/lucide/trash-2"
|
||||||
|
import IconDownload from "~icons/lucide/download"
|
||||||
|
import IconEdit from "~icons/lucide/edit"
|
||||||
|
import IconFolderPlus from "~icons/lucide/folder-plus"
|
||||||
|
import IconFilePlus from "~icons/lucide/file-plus"
|
||||||
|
import IconCircle from "~icons/lucide/circle"
|
||||||
|
import IconCheckCircle from "~icons/lucide/check-circle"
|
||||||
|
import IconFolder from "~icons/lucide/folder"
|
||||||
|
import IconFolderOpen from "~icons/lucide/folder-open"
|
||||||
|
import { defineComponent, ref } from "vue"
|
||||||
|
import * as E from "fp-ts/Either"
|
||||||
|
import {
|
||||||
|
getCompleteCollectionTree,
|
||||||
|
teamCollToHoppRESTColl,
|
||||||
|
} from "~/helpers/backend/helpers"
|
||||||
|
import { moveRESTTeamRequest } from "~/helpers/backend/mutations/TeamRequest"
|
||||||
|
import { useColorMode } from "@composables/theming"
|
||||||
|
import { useI18n } from "@composables/i18n"
|
||||||
|
import { useToast } from "@composables/toast"
|
||||||
|
import { TippyComponent } from "vue-tippy"
|
||||||
|
import SmartItem from "@components/smart/Item.vue"
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
props: {
|
||||||
|
collectionIndex: { type: Number, default: null },
|
||||||
|
collection: { type: Object, default: () => ({}) },
|
||||||
|
isFiltered: Boolean,
|
||||||
|
saveRequest: Boolean,
|
||||||
|
collectionsType: { type: Object, default: () => ({}) },
|
||||||
|
picked: { type: Object, default: () => ({}) },
|
||||||
|
loadingCollectionIDs: { type: Array, default: () => [] },
|
||||||
|
},
|
||||||
|
emits: [
|
||||||
|
"edit-collection",
|
||||||
|
"add-request",
|
||||||
|
"add-folder",
|
||||||
|
"edit-folder",
|
||||||
|
"edit-request",
|
||||||
|
"remove-folder",
|
||||||
|
"select",
|
||||||
|
"remove-request",
|
||||||
|
"duplicate-request",
|
||||||
|
"expand-collection",
|
||||||
|
"remove-collection",
|
||||||
|
],
|
||||||
|
setup() {
|
||||||
|
const t = useI18n()
|
||||||
|
|
||||||
|
return {
|
||||||
|
// Template refs
|
||||||
|
tippyActions: ref<TippyComponent | null>(null),
|
||||||
|
options: ref<TippyComponent | null>(null),
|
||||||
|
requestAction: ref<typeof SmartItem | null>(null),
|
||||||
|
folderAction: ref<typeof SmartItem | null>(null),
|
||||||
|
edit: ref<typeof SmartItem | null>(null),
|
||||||
|
deleteAction: ref<typeof SmartItem | null>(null),
|
||||||
|
exportAction: ref<typeof SmartItem | null>(null),
|
||||||
|
exportLoading: ref<boolean>(false),
|
||||||
|
t,
|
||||||
|
toast: useToast(),
|
||||||
|
colorMode: useColorMode(),
|
||||||
|
|
||||||
|
IconCheckCircle,
|
||||||
|
IconCircle,
|
||||||
|
IconFilePlus,
|
||||||
|
IconFolderPlus,
|
||||||
|
IconEdit,
|
||||||
|
IconDownload,
|
||||||
|
IconTrash2,
|
||||||
|
IconMoreVertical,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
showChildren: false,
|
||||||
|
dragging: false,
|
||||||
|
selectedFolder: {},
|
||||||
|
prevCursor: "",
|
||||||
|
cursor: "",
|
||||||
|
pageNo: 0,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
isSelected(): boolean {
|
||||||
|
return (
|
||||||
|
this.picked &&
|
||||||
|
this.picked.pickedType === "teams-collection" &&
|
||||||
|
this.picked.collectionID === this.collection.id
|
||||||
|
)
|
||||||
|
},
|
||||||
|
getCollectionIcon() {
|
||||||
|
if (this.isSelected) return IconCheckCircle
|
||||||
|
else if (!this.showChildren && !this.isFiltered) return IconFolder
|
||||||
|
else if (this.showChildren || this.isFiltered) return IconFolderOpen
|
||||||
|
else return IconFolder
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async exportCollection() {
|
||||||
|
this.exportLoading = true
|
||||||
|
|
||||||
|
const result = await getCompleteCollectionTree(this.collection.id)()
|
||||||
|
|
||||||
|
if (E.isLeft(result)) {
|
||||||
|
this.toast.error(this.t("error.something_went_wrong").toString())
|
||||||
|
console.log(result.left)
|
||||||
|
this.exportLoading = false
|
||||||
|
this.options!.tippy.hide()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const hoppColl = teamCollToHoppRESTColl(result.right)
|
||||||
|
|
||||||
|
const collectionJSON = JSON.stringify(hoppColl)
|
||||||
|
|
||||||
|
const file = new Blob([collectionJSON], { type: "application/json" })
|
||||||
|
const a = document.createElement("a")
|
||||||
|
const url = URL.createObjectURL(file)
|
||||||
|
a.href = url
|
||||||
|
|
||||||
|
a.download = `${hoppColl.name}.json`
|
||||||
|
document.body.appendChild(a)
|
||||||
|
a.click()
|
||||||
|
this.toast.success(this.t("state.download_started").toString())
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
document.body.removeChild(a)
|
||||||
|
URL.revokeObjectURL(url)
|
||||||
|
}, 1000)
|
||||||
|
|
||||||
|
this.exportLoading = false
|
||||||
|
|
||||||
|
this.options!.tippy.hide()
|
||||||
|
},
|
||||||
|
editRequest(event: any) {
|
||||||
|
this.$emit("edit-request", event)
|
||||||
|
if (this.$props.saveRequest)
|
||||||
|
this.$emit("select", {
|
||||||
|
picked: {
|
||||||
|
pickedType: "teams-collection",
|
||||||
|
collectionID: this.collection.id,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
toggleShowChildren() {
|
||||||
|
if (this.$props.saveRequest)
|
||||||
|
this.$emit("select", {
|
||||||
|
picked: {
|
||||||
|
pickedType: "teams-collection",
|
||||||
|
collectionID: this.collection.id,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
this.$emit("expand-collection", this.collection.id)
|
||||||
|
this.showChildren = !this.showChildren
|
||||||
|
},
|
||||||
|
removeCollection() {
|
||||||
|
this.$emit("remove-collection", {
|
||||||
|
collectionIndex: this.collectionIndex,
|
||||||
|
collectionID: this.collection.id,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
expandCollection(collectionID: string) {
|
||||||
|
this.$emit("expand-collection", collectionID)
|
||||||
|
},
|
||||||
|
async dropEvent({ dataTransfer }: any) {
|
||||||
|
this.dragging = !this.dragging
|
||||||
|
const requestIndex = dataTransfer.getData("requestIndex")
|
||||||
|
const moveRequestResult = await moveRESTTeamRequest(
|
||||||
|
requestIndex,
|
||||||
|
this.collection.id
|
||||||
|
)()
|
||||||
|
if (E.isLeft(moveRequestResult))
|
||||||
|
this.toast.error(`${this.t("error.something_went_wrong")}`)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,383 @@
|
|||||||
|
<template>
|
||||||
|
<div class="flex flex-col" :class="[{ 'bg-primaryLight': dragging }]">
|
||||||
|
<div
|
||||||
|
class="flex items-stretch group"
|
||||||
|
@dragover.prevent
|
||||||
|
@drop.prevent="dropEvent"
|
||||||
|
@dragover="dragging = true"
|
||||||
|
@drop="dragging = false"
|
||||||
|
@dragleave="dragging = false"
|
||||||
|
@dragend="dragging = false"
|
||||||
|
@contextmenu.prevent="options!.tippy.show()"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="flex items-center justify-center px-4 cursor-pointer"
|
||||||
|
@click="toggleShowChildren()"
|
||||||
|
>
|
||||||
|
<component
|
||||||
|
:is="getCollectionIcon"
|
||||||
|
class="svg-icons"
|
||||||
|
:class="{ 'text-accent': isSelected }"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
class="flex flex-1 min-w-0 py-2 pr-2 cursor-pointer transition group-hover:text-secondaryDark"
|
||||||
|
@click="toggleShowChildren()"
|
||||||
|
>
|
||||||
|
<span class="truncate" :class="{ 'text-accent': isSelected }">
|
||||||
|
{{ folder.name ? folder.name : folder.title }}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<div class="flex">
|
||||||
|
<ButtonSecondary
|
||||||
|
v-if="collectionsType.selectedTeam.myRole !== 'VIEWER'"
|
||||||
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
|
:icon="IconFilePlus"
|
||||||
|
:title="t('request.new')"
|
||||||
|
class="hidden group-hover:inline-flex"
|
||||||
|
@click="$emit('add-request', { folder, path: folderPath })"
|
||||||
|
/>
|
||||||
|
<ButtonSecondary
|
||||||
|
v-if="collectionsType.selectedTeam.myRole !== 'VIEWER'"
|
||||||
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
|
:icon="IconFolderPlus"
|
||||||
|
:title="t('folder.new')"
|
||||||
|
class="hidden group-hover:inline-flex"
|
||||||
|
@click="$emit('add-folder', { folder, path: folderPath })"
|
||||||
|
/>
|
||||||
|
<span>
|
||||||
|
<tippy
|
||||||
|
v-if="collectionsType.selectedTeam.myRole !== 'VIEWER'"
|
||||||
|
ref="options"
|
||||||
|
interactive
|
||||||
|
trigger="click"
|
||||||
|
theme="popover"
|
||||||
|
:on-shown="() => tippyActions!.focus()"
|
||||||
|
>
|
||||||
|
<ButtonSecondary
|
||||||
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
|
:title="t('action.more')"
|
||||||
|
:icon="IconMoreVertical"
|
||||||
|
/>
|
||||||
|
<template #content="{ hide }">
|
||||||
|
<div
|
||||||
|
ref="tippyActions"
|
||||||
|
class="flex flex-col focus:outline-none"
|
||||||
|
tabindex="0"
|
||||||
|
@keyup.r="requestAction!.$el.click()"
|
||||||
|
@keyup.n="folderAction!.$el.click()"
|
||||||
|
@keyup.e="edit!.$el.click()"
|
||||||
|
@keyup.delete="deleteAction!.$el.click()"
|
||||||
|
@keyup.x="exportAction!.$el.click()"
|
||||||
|
@keyup.escape="hide()"
|
||||||
|
>
|
||||||
|
<SmartItem
|
||||||
|
ref="requestAction"
|
||||||
|
:icon="IconFilePlus"
|
||||||
|
:label="t('request.new')"
|
||||||
|
:shortcut="['R']"
|
||||||
|
@click="
|
||||||
|
() => {
|
||||||
|
$emit('add-request', { folder, path: folderPath })
|
||||||
|
hide()
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
<SmartItem
|
||||||
|
ref="folderAction"
|
||||||
|
:icon="IconFolderPlus"
|
||||||
|
:label="t('folder.new')"
|
||||||
|
:shortcut="['N']"
|
||||||
|
@click="
|
||||||
|
() => {
|
||||||
|
$emit('add-folder', { folder, path: folderPath })
|
||||||
|
hide()
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
<SmartItem
|
||||||
|
ref="edit"
|
||||||
|
:icon="IconEdit"
|
||||||
|
:label="t('action.edit')"
|
||||||
|
:shortcut="['E']"
|
||||||
|
@click="
|
||||||
|
() => {
|
||||||
|
$emit('edit-folder', {
|
||||||
|
folder,
|
||||||
|
folderIndex,
|
||||||
|
collectionIndex,
|
||||||
|
folderPath: '',
|
||||||
|
})
|
||||||
|
hide()
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
<SmartItem
|
||||||
|
ref="exportAction"
|
||||||
|
:icon="IconDownload"
|
||||||
|
:label="t('export.title')"
|
||||||
|
:shortcut="['X']"
|
||||||
|
:loading="exportLoading"
|
||||||
|
@click="exportFolder"
|
||||||
|
/>
|
||||||
|
<SmartItem
|
||||||
|
ref="deleteAction"
|
||||||
|
:icon="IconTrash2"
|
||||||
|
:label="t('action.delete')"
|
||||||
|
:shortcut="['⌫']"
|
||||||
|
@click="
|
||||||
|
() => {
|
||||||
|
removeFolder()
|
||||||
|
hide()
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</tippy>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="showChildren || isFiltered" class="flex">
|
||||||
|
<div
|
||||||
|
class="bg-dividerLight cursor-nsResize flex ml-5.5 transform transition w-1 hover:bg-dividerDark hover:scale-x-125"
|
||||||
|
@click="toggleShowChildren()"
|
||||||
|
></div>
|
||||||
|
<div class="flex flex-col flex-1 truncate">
|
||||||
|
<!-- Referring to this component only (this is recursive) -->
|
||||||
|
<Folder
|
||||||
|
v-for="(subFolder, subFolderIndex) in folder.children"
|
||||||
|
:key="`subFolder-${subFolderIndex}`"
|
||||||
|
:folder="subFolder"
|
||||||
|
:folder-index="subFolderIndex"
|
||||||
|
:collection-index="collectionIndex"
|
||||||
|
:save-request="saveRequest"
|
||||||
|
:collections-type="collectionsType"
|
||||||
|
:folder-path="`${folderPath}/${subFolderIndex}`"
|
||||||
|
:picked="picked"
|
||||||
|
:loading-collection-i-ds="loadingCollectionIDs"
|
||||||
|
@add-request="$emit('add-request', $event)"
|
||||||
|
@add-folder="$emit('add-folder', $event)"
|
||||||
|
@edit-folder="$emit('edit-folder', $event)"
|
||||||
|
@edit-request="$emit('edit-request', $event)"
|
||||||
|
@update-team-collections="$emit('update-team-collections')"
|
||||||
|
@select="$emit('select', $event)"
|
||||||
|
@expand-collection="expandCollection"
|
||||||
|
@remove-request="$emit('remove-request', $event)"
|
||||||
|
@remove-folder="$emit('remove-folder', $event)"
|
||||||
|
@duplicate-request="$emit('duplicate-request', $event)"
|
||||||
|
/>
|
||||||
|
<CollectionsTeamsRequest
|
||||||
|
v-for="(request, index) in folder.requests"
|
||||||
|
:key="`request-${index}`"
|
||||||
|
:request="request.request"
|
||||||
|
:collection-index="collectionIndex"
|
||||||
|
:folder-index="folderIndex"
|
||||||
|
:folder-name="folder.name"
|
||||||
|
:request-index="request.id"
|
||||||
|
:save-request="saveRequest"
|
||||||
|
:collections-type="collectionsType"
|
||||||
|
:picked="picked"
|
||||||
|
:collection-i-d="folder.id"
|
||||||
|
@edit-request="$emit('edit-request', $event)"
|
||||||
|
@select="$emit('select', $event)"
|
||||||
|
@remove-request="$emit('remove-request', $event)"
|
||||||
|
@duplicate-request="$emit('duplicate-request', $event)"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
v-if="loadingCollectionIDs.includes(folder.id)"
|
||||||
|
class="flex flex-col items-center justify-center p-4"
|
||||||
|
>
|
||||||
|
<SmartSpinner class="my-4" />
|
||||||
|
<span class="text-secondaryLight">{{ t("state.loading") }}</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-else-if="
|
||||||
|
(folder.children == undefined || folder.children.length === 0) &&
|
||||||
|
(folder.requests == undefined || folder.requests.length === 0)
|
||||||
|
"
|
||||||
|
class="flex flex-col items-center justify-center p-4 text-secondaryLight"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
:src="`/images/states/${colorMode.value}/pack.svg`"
|
||||||
|
loading="lazy"
|
||||||
|
class="inline-flex flex-col object-contain object-center w-16 h-16 mb-4"
|
||||||
|
:alt="`${t('empty.folder')}`"
|
||||||
|
/>
|
||||||
|
<span class="text-center">
|
||||||
|
{{ t("empty.folder") }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import IconMoreVertical from "~icons/lucide/more-vertical"
|
||||||
|
import IconEdit from "~icons/lucide/edit"
|
||||||
|
import IconDownload from "~icons/lucide/download"
|
||||||
|
import IconTrash2 from "~icons/lucide/trash-2"
|
||||||
|
import IconFilePlus from "~icons/lucide/file-plus"
|
||||||
|
import IconCheckCircle from "~icons/lucide/check-circle"
|
||||||
|
import IconFolderPlus from "~icons/lucide/folder-plus"
|
||||||
|
import IconFolder from "~icons/lucide/folder"
|
||||||
|
import IconFolderOpen from "~icons/lucide/folder-open"
|
||||||
|
import { defineComponent, ref } from "vue"
|
||||||
|
import * as E from "fp-ts/Either"
|
||||||
|
import {
|
||||||
|
getCompleteCollectionTree,
|
||||||
|
teamCollToHoppRESTColl,
|
||||||
|
} from "~/helpers/backend/helpers"
|
||||||
|
import { moveRESTTeamRequest } from "~/helpers/backend/mutations/TeamRequest"
|
||||||
|
import { useI18n } from "@composables/i18n"
|
||||||
|
import { useToast } from "@composables/toast"
|
||||||
|
import { useColorMode } from "@composables/theming"
|
||||||
|
import { TippyComponent } from "vue-tippy"
|
||||||
|
import SmartItem from "@components/smart/Item.vue"
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: "Folder",
|
||||||
|
props: {
|
||||||
|
folder: { type: Object, default: () => ({}) },
|
||||||
|
folderIndex: { type: Number, default: null },
|
||||||
|
collectionIndex: { type: Number, default: null },
|
||||||
|
folderPath: { type: String, default: null },
|
||||||
|
saveRequest: Boolean,
|
||||||
|
isFiltered: Boolean,
|
||||||
|
collectionsType: { type: Object, default: () => ({}) },
|
||||||
|
picked: { type: Object, default: () => ({}) },
|
||||||
|
loadingCollectionIDs: { type: Array, default: () => [] },
|
||||||
|
},
|
||||||
|
emits: [
|
||||||
|
"add-request",
|
||||||
|
"add-folder",
|
||||||
|
"edit-folder",
|
||||||
|
"update-team-collections",
|
||||||
|
"edit-request",
|
||||||
|
"remove-request",
|
||||||
|
"duplicate-request",
|
||||||
|
"select",
|
||||||
|
"remove-folder",
|
||||||
|
"expand-collection",
|
||||||
|
],
|
||||||
|
setup() {
|
||||||
|
return {
|
||||||
|
// Template refs
|
||||||
|
tippyActions: ref<TippyComponent | null>(null),
|
||||||
|
options: ref<TippyComponent | null>(null),
|
||||||
|
requestAction: ref<typeof SmartItem | null>(null),
|
||||||
|
folderAction: ref<typeof SmartItem | null>(null),
|
||||||
|
edit: ref<typeof SmartItem | null>(null),
|
||||||
|
deleteAction: ref<typeof SmartItem | null>(null),
|
||||||
|
exportAction: ref<typeof SmartItem | null>(null),
|
||||||
|
exportLoading: ref<boolean>(false),
|
||||||
|
toast: useToast(),
|
||||||
|
t: useI18n(),
|
||||||
|
colorMode: useColorMode(),
|
||||||
|
IconFilePlus,
|
||||||
|
IconFolderPlus,
|
||||||
|
IconCheckCircle,
|
||||||
|
IconFolder,
|
||||||
|
IconFolderOpen,
|
||||||
|
IconMoreVertical,
|
||||||
|
IconEdit,
|
||||||
|
IconDownload,
|
||||||
|
IconTrash2,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
showChildren: false,
|
||||||
|
dragging: false,
|
||||||
|
prevCursor: "",
|
||||||
|
cursor: "",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
isSelected(): boolean {
|
||||||
|
return (
|
||||||
|
this.picked &&
|
||||||
|
this.picked.pickedType === "teams-folder" &&
|
||||||
|
this.picked.folderID === this.folder.id
|
||||||
|
)
|
||||||
|
},
|
||||||
|
getCollectionIcon() {
|
||||||
|
if (this.isSelected) return IconCheckCircle
|
||||||
|
else if (!this.showChildren && !this.isFiltered) return IconFolder
|
||||||
|
else if (this.showChildren || this.isFiltered) return IconFolderOpen
|
||||||
|
else return IconFolder
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async exportFolder() {
|
||||||
|
this.exportLoading = true
|
||||||
|
|
||||||
|
const result = await getCompleteCollectionTree(this.folder.id)()
|
||||||
|
|
||||||
|
if (E.isLeft(result)) {
|
||||||
|
this.toast.error(this.t("error.something_went_wrong").toString())
|
||||||
|
console.log(result.left)
|
||||||
|
this.exportLoading = false
|
||||||
|
this.options!.tippy.hide()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const hoppColl = teamCollToHoppRESTColl(result.right)
|
||||||
|
|
||||||
|
const collectionJSON = JSON.stringify(hoppColl)
|
||||||
|
|
||||||
|
const file = new Blob([collectionJSON], { type: "application/json" })
|
||||||
|
const a = document.createElement("a")
|
||||||
|
const url = URL.createObjectURL(file)
|
||||||
|
a.href = url
|
||||||
|
|
||||||
|
a.download = `${hoppColl.name}.json`
|
||||||
|
document.body.appendChild(a)
|
||||||
|
a.click()
|
||||||
|
this.toast.success(this.t("state.download_started").toString())
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
document.body.removeChild(a)
|
||||||
|
URL.revokeObjectURL(url)
|
||||||
|
}, 1000)
|
||||||
|
|
||||||
|
this.exportLoading = false
|
||||||
|
|
||||||
|
this.options!.tippy.hide()
|
||||||
|
},
|
||||||
|
toggleShowChildren() {
|
||||||
|
if (this.$props.saveRequest)
|
||||||
|
this.$emit("select", {
|
||||||
|
picked: {
|
||||||
|
pickedType: "teams-folder",
|
||||||
|
folderID: this.folder.id,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
this.$emit("expand-collection", this.$props.folder.id)
|
||||||
|
this.showChildren = !this.showChildren
|
||||||
|
},
|
||||||
|
removeFolder() {
|
||||||
|
this.$emit("remove-folder", {
|
||||||
|
collectionsType: this.collectionsType,
|
||||||
|
folder: this.folder,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
expandCollection(collectionID: number) {
|
||||||
|
this.$emit("expand-collection", collectionID)
|
||||||
|
},
|
||||||
|
async dropEvent({ dataTransfer }: any) {
|
||||||
|
this.dragging = !this.dragging
|
||||||
|
const requestIndex = dataTransfer.getData("requestIndex")
|
||||||
|
const moveRequestResult = await moveRESTTeamRequest(
|
||||||
|
requestIndex,
|
||||||
|
this.folder.id
|
||||||
|
)()
|
||||||
|
if (E.isLeft(moveRequestResult))
|
||||||
|
this.toast.error(`${this.t("error.something_went_wrong")}`)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,405 @@
|
|||||||
|
<template>
|
||||||
|
<div class="flex flex-col" :class="[{ 'bg-primaryLight': dragging }]">
|
||||||
|
<div
|
||||||
|
class="flex items-stretch group"
|
||||||
|
draggable="true"
|
||||||
|
@dragstart="dragStart"
|
||||||
|
@dragover.stop
|
||||||
|
@dragleave="dragging = false"
|
||||||
|
@dragend="dragging = false"
|
||||||
|
@contextmenu.prevent="options.tippy.show()"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="flex items-center justify-center w-16 px-2 truncate cursor-pointer"
|
||||||
|
:class="getRequestLabelColor(request.method)"
|
||||||
|
@click="selectRequest()"
|
||||||
|
>
|
||||||
|
<component
|
||||||
|
:is="IconCheckCircle"
|
||||||
|
v-if="isSelected"
|
||||||
|
class="svg-icons"
|
||||||
|
:class="{ 'text-accent': isSelected }"
|
||||||
|
/>
|
||||||
|
<span v-else class="font-semibold truncate text-tiny">
|
||||||
|
{{ request.method }}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
class="flex items-center flex-1 min-w-0 py-2 pr-2 cursor-pointer transition group-hover:text-secondaryDark"
|
||||||
|
@click="selectRequest()"
|
||||||
|
>
|
||||||
|
<span class="truncate" :class="{ 'text-accent': isSelected }">
|
||||||
|
{{ request.name }}
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
v-if="isActive"
|
||||||
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
|
class="relative h-1.5 w-1.5 flex flex-shrink-0 mx-3"
|
||||||
|
:title="`${t('collection.request_in_use')}`"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="absolute inline-flex flex-shrink-0 w-full h-full bg-green-500 rounded-full opacity-75 animate-ping"
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
class="relative inline-flex flex-shrink-0 rounded-full h-1.5 w-1.5 bg-green-500"
|
||||||
|
></span>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<div class="flex">
|
||||||
|
<ButtonSecondary
|
||||||
|
v-if="!saveRequest"
|
||||||
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
|
:icon="IconRotateCCW"
|
||||||
|
:title="t('action.restore')"
|
||||||
|
class="hidden group-hover:inline-flex"
|
||||||
|
@click="selectRequest()"
|
||||||
|
/>
|
||||||
|
<span>
|
||||||
|
<tippy
|
||||||
|
v-if="collectionsType.selectedTeam.myRole !== 'VIEWER'"
|
||||||
|
ref="options"
|
||||||
|
interactive
|
||||||
|
trigger="click"
|
||||||
|
theme="popover"
|
||||||
|
:on-shown="() => tippyActions.focus()"
|
||||||
|
>
|
||||||
|
<ButtonSecondary
|
||||||
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
|
:title="t('action.more')"
|
||||||
|
:icon="IconMoreVertical"
|
||||||
|
/>
|
||||||
|
<template #content="{ hide }">
|
||||||
|
<div
|
||||||
|
ref="tippyActions"
|
||||||
|
class="flex flex-col focus:outline-none"
|
||||||
|
tabindex="0"
|
||||||
|
@keyup.e="edit.$el.click()"
|
||||||
|
@keyup.d="duplicate.$el.click()"
|
||||||
|
@keyup.delete="deleteAction.$el.click()"
|
||||||
|
@keyup.escape="hide()"
|
||||||
|
>
|
||||||
|
<SmartItem
|
||||||
|
ref="edit"
|
||||||
|
:icon="IconEdit"
|
||||||
|
:label="t('action.edit')"
|
||||||
|
:shortcut="['E']"
|
||||||
|
@click="
|
||||||
|
() => {
|
||||||
|
emit('edit-request', {
|
||||||
|
collectionIndex,
|
||||||
|
folderIndex,
|
||||||
|
folderName,
|
||||||
|
request,
|
||||||
|
requestIndex,
|
||||||
|
})
|
||||||
|
hide()
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
<SmartItem
|
||||||
|
ref="duplicate"
|
||||||
|
:icon="IconCopy"
|
||||||
|
:label="t('action.duplicate')"
|
||||||
|
:shortcut="['D']"
|
||||||
|
@click="
|
||||||
|
() => {
|
||||||
|
emit('duplicate-request', {
|
||||||
|
request,
|
||||||
|
requestIndex,
|
||||||
|
collectionID,
|
||||||
|
})
|
||||||
|
hide()
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
<SmartItem
|
||||||
|
ref="deleteAction"
|
||||||
|
:icon="IconTrash2"
|
||||||
|
:label="t('action.delete')"
|
||||||
|
:shortcut="['⌫']"
|
||||||
|
@click="
|
||||||
|
() => {
|
||||||
|
removeRequest()
|
||||||
|
hide()
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</tippy>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<HttpReqChangeConfirmModal
|
||||||
|
:show="confirmChange"
|
||||||
|
@hide-modal="confirmChange = false"
|
||||||
|
@save-change="saveRequestChange"
|
||||||
|
@discard-change="discardRequestChange"
|
||||||
|
/>
|
||||||
|
<CollectionsSaveRequest
|
||||||
|
mode="rest"
|
||||||
|
:show="showSaveRequestModal"
|
||||||
|
@hide-modal="showSaveRequestModal = false"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import IconMoreVertical from "~icons/lucide/more-vertical"
|
||||||
|
import IconCheckCircle from "~icons/lucide/check-circle"
|
||||||
|
import IconRotateCCW from "~icons/lucide/rotate-ccw"
|
||||||
|
import IconEdit from "~icons/lucide/edit"
|
||||||
|
import IconCopy from "~icons/lucide/copy"
|
||||||
|
import IconTrash2 from "~icons/lucide/trash-2"
|
||||||
|
import { ref, computed } from "vue"
|
||||||
|
import {
|
||||||
|
HoppRESTRequest,
|
||||||
|
isEqualHoppRESTRequest,
|
||||||
|
safelyExtractRESTRequest,
|
||||||
|
translateToNewRequest,
|
||||||
|
} from "@hoppscotch/data"
|
||||||
|
import * as E from "fp-ts/Either"
|
||||||
|
import { useI18n } from "@composables/i18n"
|
||||||
|
import { useToast } from "@composables/toast"
|
||||||
|
import { useReadonlyStream } from "@composables/stream"
|
||||||
|
import {
|
||||||
|
getDefaultRESTRequest,
|
||||||
|
restSaveContext$,
|
||||||
|
setRESTRequest,
|
||||||
|
setRESTSaveContext,
|
||||||
|
getRESTSaveContext,
|
||||||
|
getRESTRequest,
|
||||||
|
} from "~/newstore/RESTSession"
|
||||||
|
import { editRESTRequest } from "~/newstore/collections"
|
||||||
|
import { runMutation } from "~/helpers/backend/GQLClient"
|
||||||
|
import { Team, UpdateRequestDocument } from "~/helpers/backend/graphql"
|
||||||
|
import { HoppRequestSaveContext } from "~/helpers/types/HoppRequestSaveContext"
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
request: HoppRESTRequest
|
||||||
|
collectionIndex: number
|
||||||
|
folderIndex: number
|
||||||
|
folderName?: string
|
||||||
|
requestIndex: string
|
||||||
|
saveRequest: boolean
|
||||||
|
collectionsType: {
|
||||||
|
type: "my-collections" | "team-collections"
|
||||||
|
selectedTeam: Team | undefined
|
||||||
|
}
|
||||||
|
collectionID: string
|
||||||
|
picked?: {
|
||||||
|
pickedType: string
|
||||||
|
requestID: string
|
||||||
|
}
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(
|
||||||
|
e: "select",
|
||||||
|
data:
|
||||||
|
| {
|
||||||
|
picked: {
|
||||||
|
pickedType: string
|
||||||
|
requestID: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
| undefined
|
||||||
|
): void
|
||||||
|
|
||||||
|
(
|
||||||
|
e: "remove-request",
|
||||||
|
data: {
|
||||||
|
folderPath: string | undefined
|
||||||
|
requestIndex: string
|
||||||
|
}
|
||||||
|
): void
|
||||||
|
|
||||||
|
(
|
||||||
|
e: "edit-request",
|
||||||
|
data: {
|
||||||
|
collectionIndex: number
|
||||||
|
folderIndex: number
|
||||||
|
folderName: string | undefined
|
||||||
|
requestIndex: string
|
||||||
|
request: HoppRESTRequest
|
||||||
|
}
|
||||||
|
): void
|
||||||
|
|
||||||
|
(
|
||||||
|
e: "duplicate-request",
|
||||||
|
data: {
|
||||||
|
collectionID: number | string
|
||||||
|
requestIndex: string
|
||||||
|
request: HoppRESTRequest
|
||||||
|
}
|
||||||
|
): void
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const t = useI18n()
|
||||||
|
const toast = useToast()
|
||||||
|
|
||||||
|
const dragging = ref(false)
|
||||||
|
const requestMethodLabels = {
|
||||||
|
get: "text-green-500",
|
||||||
|
post: "text-yellow-500",
|
||||||
|
put: "text-blue-500",
|
||||||
|
delete: "text-red-500",
|
||||||
|
default: "text-gray-500",
|
||||||
|
}
|
||||||
|
const confirmChange = ref(false)
|
||||||
|
const showSaveRequestModal = ref(false)
|
||||||
|
|
||||||
|
// Template refs
|
||||||
|
const tippyActions = ref<any | null>(null)
|
||||||
|
const options = ref<any | null>(null)
|
||||||
|
const edit = ref<any | null>(null)
|
||||||
|
const duplicate = ref<any | null>(null)
|
||||||
|
const deleteAction = ref<any | null>(null)
|
||||||
|
|
||||||
|
const active = useReadonlyStream(restSaveContext$, null)
|
||||||
|
|
||||||
|
const isSelected = computed(
|
||||||
|
() =>
|
||||||
|
props.picked &&
|
||||||
|
props.picked.pickedType === "teams-request" &&
|
||||||
|
props.picked.requestID === props.requestIndex
|
||||||
|
)
|
||||||
|
|
||||||
|
const isActive = computed(
|
||||||
|
() =>
|
||||||
|
active.value &&
|
||||||
|
active.value.originLocation === "team-collection" &&
|
||||||
|
active.value.requestID === props.requestIndex
|
||||||
|
)
|
||||||
|
|
||||||
|
const dragStart = ({ dataTransfer }: DragEvent) => {
|
||||||
|
if (dataTransfer) {
|
||||||
|
dragging.value = !dragging.value
|
||||||
|
dataTransfer.setData("requestIndex", props.requestIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const removeRequest = () => {
|
||||||
|
emit("remove-request", {
|
||||||
|
folderPath: props.folderName,
|
||||||
|
requestIndex: props.requestIndex,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const getRequestLabelColor = (method: string): string => {
|
||||||
|
return (
|
||||||
|
(requestMethodLabels as any)[method.toLowerCase()] ||
|
||||||
|
requestMethodLabels.default
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const setRestReq = (request: HoppRESTRequest) => {
|
||||||
|
setRESTRequest(
|
||||||
|
safelyExtractRESTRequest(
|
||||||
|
translateToNewRequest(request),
|
||||||
|
getDefaultRESTRequest()
|
||||||
|
),
|
||||||
|
{
|
||||||
|
originLocation: "team-collection",
|
||||||
|
requestID: props.requestIndex,
|
||||||
|
req: request,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectRequest = () => {
|
||||||
|
// Check if this is a save as request popup, if so we don't need to prompt the confirm change popup.
|
||||||
|
if (props.saveRequest) {
|
||||||
|
emit("select", {
|
||||||
|
picked: {
|
||||||
|
pickedType: "teams-request",
|
||||||
|
requestID: props.requestIndex,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
} else if (isEqualHoppRESTRequest(props.request, getDefaultRESTRequest())) {
|
||||||
|
confirmChange.value = false
|
||||||
|
setRestReq(props.request)
|
||||||
|
} else if (!active.value) {
|
||||||
|
confirmChange.value = true
|
||||||
|
} else {
|
||||||
|
const currentReqWithNoChange = active.value.req
|
||||||
|
const currentFullReq = getRESTRequest()
|
||||||
|
|
||||||
|
// Check if whether user clicked the same request or not
|
||||||
|
if (!isActive.value && currentReqWithNoChange) {
|
||||||
|
// Check if there is any changes done on the current request
|
||||||
|
if (isEqualHoppRESTRequest(currentReqWithNoChange, currentFullReq)) {
|
||||||
|
setRestReq(props.request)
|
||||||
|
} else {
|
||||||
|
confirmChange.value = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setRESTSaveContext(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Save current request to the collection */
|
||||||
|
const saveRequestChange = () => {
|
||||||
|
const saveCtx = getRESTSaveContext()
|
||||||
|
saveCurrentRequest(saveCtx)
|
||||||
|
confirmChange.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Discard changes and change the current request and context */
|
||||||
|
const discardRequestChange = () => {
|
||||||
|
setRestReq(props.request)
|
||||||
|
if (!isActive.value) {
|
||||||
|
setRESTSaveContext({
|
||||||
|
originLocation: "team-collection",
|
||||||
|
requestID: props.requestIndex,
|
||||||
|
req: props.request,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
confirmChange.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const saveCurrentRequest = (saveCtx: HoppRequestSaveContext | null) => {
|
||||||
|
if (!saveCtx) {
|
||||||
|
showSaveRequestModal.value = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (saveCtx.originLocation === "team-collection") {
|
||||||
|
const req = getRESTRequest()
|
||||||
|
try {
|
||||||
|
runMutation(UpdateRequestDocument, {
|
||||||
|
requestID: saveCtx.requestID,
|
||||||
|
data: {
|
||||||
|
title: req.name,
|
||||||
|
request: JSON.stringify(req),
|
||||||
|
},
|
||||||
|
})().then((result) => {
|
||||||
|
if (E.isLeft(result)) {
|
||||||
|
toast.error(`${t("profile.no_permission")}`)
|
||||||
|
} else {
|
||||||
|
toast.success(`${t("request.saved")}`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
setRestReq(props.request)
|
||||||
|
} catch (error) {
|
||||||
|
showSaveRequestModal.value = true
|
||||||
|
toast.error(`${t("error.something_went_wrong")}`)
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
} else if (saveCtx.originLocation === "user-collection") {
|
||||||
|
try {
|
||||||
|
editRESTRequest(
|
||||||
|
saveCtx.folderPath,
|
||||||
|
saveCtx.requestIndex,
|
||||||
|
getRESTRequest()
|
||||||
|
)
|
||||||
|
setRestReq(props.request)
|
||||||
|
toast.success(`${t("request.saved")}`)
|
||||||
|
} catch (e) {
|
||||||
|
setRESTSaveContext(null)
|
||||||
|
saveCurrentRequest(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -12,6 +12,7 @@
|
|||||||
<SmartTab
|
<SmartTab
|
||||||
:id="'team-environments'"
|
:id="'team-environments'"
|
||||||
:label="`${t('environment.team_environments')}`"
|
:label="`${t('environment.team_environments')}`"
|
||||||
|
:disabled="!currentUser"
|
||||||
>
|
>
|
||||||
<SmartIntersection @intersecting="onTeamSelectIntersect">
|
<SmartIntersection @intersecting="onTeamSelectIntersect">
|
||||||
<tippy
|
<tippy
|
||||||
@@ -75,17 +76,16 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { nextTick, ref, watch } from "vue"
|
import { ref, watch } from "vue"
|
||||||
import { GetMyTeamsQuery } from "~/helpers/backend/graphql"
|
import { GetMyTeamsQuery } from "~/helpers/backend/graphql"
|
||||||
import { onLoggedIn } from "@composables/auth"
|
import { onLoggedIn } from "@composables/auth"
|
||||||
import { platform } from "~/platform"
|
import { currentUserInfo$ } from "~/helpers/teams/BackendUserInfo"
|
||||||
import TeamListAdapter from "~/helpers/teams/TeamListAdapter"
|
import TeamListAdapter from "~/helpers/teams/TeamListAdapter"
|
||||||
import { useReadonlyStream } from "@composables/stream"
|
import { useReadonlyStream } from "@composables/stream"
|
||||||
import { useLocalState } from "~/newstore/localstate"
|
import { useLocalState } from "~/newstore/localstate"
|
||||||
import { useI18n } from "@composables/i18n"
|
import { useI18n } from "@composables/i18n"
|
||||||
import IconDone from "~icons/lucide/check"
|
import IconDone from "~icons/lucide/check"
|
||||||
import IconUsers from "~icons/lucide/users"
|
import IconUsers from "~icons/lucide/users"
|
||||||
import { invokeAction } from "~/helpers/actions"
|
|
||||||
|
|
||||||
const t = useI18n()
|
const t = useI18n()
|
||||||
|
|
||||||
@@ -111,10 +111,7 @@ const emit = defineEmits<{
|
|||||||
(e: "update-selected-team", team: SelectedTeam): void
|
(e: "update-selected-team", team: SelectedTeam): void
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const currentUser = useReadonlyStream(
|
const currentUser = useReadonlyStream(currentUserInfo$, null)
|
||||||
platform.auth.getCurrentUserStream(),
|
|
||||||
platform.auth.getCurrentUser()
|
|
||||||
)
|
|
||||||
|
|
||||||
const adapter = new TeamListAdapter(true)
|
const adapter = new TeamListAdapter(true)
|
||||||
const myTeams = useReadonlyStream(adapter.teamList$, null)
|
const myTeams = useReadonlyStream(adapter.teamList$, null)
|
||||||
@@ -141,9 +138,7 @@ watch(
|
|||||||
)
|
)
|
||||||
|
|
||||||
onLoggedIn(() => {
|
onLoggedIn(() => {
|
||||||
try {
|
adapter.initialize()
|
||||||
adapter.initialize()
|
|
||||||
} catch (e) {}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const onTeamSelectIntersect = () => {
|
const onTeamSelectIntersect = () => {
|
||||||
@@ -161,9 +156,6 @@ const updateSelectedTeam = (team: SelectedTeam) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
watch(selectedEnvironmentTab, (newValue: EnvironmentTabs) => {
|
watch(selectedEnvironmentTab, (newValue: EnvironmentTabs) => {
|
||||||
if (newValue === "team-environments" && !currentUser.value) {
|
updateEnvironmentType(newValue)
|
||||||
invokeAction("modals.login.toggle")
|
|
||||||
nextTick(() => (selectedEnvironmentTab.value = "my-environments"))
|
|
||||||
} else updateEnvironmentType(newValue)
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ import IconDownload from "~icons/lucide/download"
|
|||||||
import IconGithub from "~icons/lucide/github"
|
import IconGithub from "~icons/lucide/github"
|
||||||
import { computed, ref } from "vue"
|
import { computed, ref } from "vue"
|
||||||
import { Environment } from "@hoppscotch/data"
|
import { Environment } from "@hoppscotch/data"
|
||||||
import { platform } from "~/platform"
|
import { currentUser$ } from "~/helpers/fb/auth"
|
||||||
import axios from "axios"
|
import axios from "axios"
|
||||||
import { useI18n } from "@composables/i18n"
|
import { useI18n } from "@composables/i18n"
|
||||||
import { useReadonlyStream } from "@composables/stream"
|
import { useReadonlyStream } from "@composables/stream"
|
||||||
@@ -141,10 +141,7 @@ const t = useI18n()
|
|||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
|
|
||||||
const myEnvironments = useReadonlyStream(environments$, [])
|
const myEnvironments = useReadonlyStream(environments$, [])
|
||||||
const currentUser = useReadonlyStream(
|
const currentUser = useReadonlyStream(currentUser$, null)
|
||||||
platform.auth.getCurrentUserStream(),
|
|
||||||
platform.auth.getCurrentUser()
|
|
||||||
)
|
|
||||||
|
|
||||||
// Template refs
|
// Template refs
|
||||||
const tippyActions = ref<TippyComponent | null>(null)
|
const tippyActions = ref<TippyComponent | null>(null)
|
||||||
@@ -190,7 +187,7 @@ const createEnvironmentGist = async () => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
toast.success(t("export.gist_created").toString())
|
toast.success(t("export.gist_created").toString())
|
||||||
window.open(res.data.html_url)
|
window.open(res.html_url)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
toast.error(t("error.something_went_wrong").toString())
|
toast.error(t("error.something_went_wrong").toString())
|
||||||
console.error(e)
|
console.error(e)
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
interactive
|
interactive
|
||||||
trigger="click"
|
trigger="click"
|
||||||
theme="popover"
|
theme="popover"
|
||||||
|
arrow
|
||||||
:on-shown="() => tippyActions!.focus()"
|
:on-shown="() => tippyActions!.focus()"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
@@ -68,7 +69,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</tippy>
|
</tippy>
|
||||||
<tippy v-else interactive trigger="click" theme="popover">
|
<tippy v-else interactive trigger="click" theme="popover" arrow>
|
||||||
<span
|
<span
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
:title="`${t('environment.select')}`"
|
:title="`${t('environment.select')}`"
|
||||||
@@ -183,7 +184,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref, watch } from "vue"
|
import { computed, ref, watch } from "vue"
|
||||||
import { isEqual } from "lodash-es"
|
import { isEqual } from "lodash-es"
|
||||||
import { platform } from "~/platform"
|
import { currentUser$ } from "~/helpers/fb/auth"
|
||||||
import { Team } from "~/helpers/backend/graphql"
|
import { Team } from "~/helpers/backend/graphql"
|
||||||
import { useReadonlyStream, useStream } from "@composables/stream"
|
import { useReadonlyStream, useStream } from "@composables/stream"
|
||||||
import { useI18n } from "~/composables/i18n"
|
import { useI18n } from "~/composables/i18n"
|
||||||
@@ -222,10 +223,7 @@ const globalEnvironment = computed(() => ({
|
|||||||
variables: globalEnv.value,
|
variables: globalEnv.value,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
const currentUser = useReadonlyStream(
|
const currentUser = useReadonlyStream(currentUser$, null)
|
||||||
platform.auth.getCurrentUserStream(),
|
|
||||||
platform.auth.getCurrentUser()
|
|
||||||
)
|
|
||||||
|
|
||||||
const updateSelectedTeam = (newSelectedTeam: SelectedTeam) => {
|
const updateSelectedTeam = (newSelectedTeam: SelectedTeam) => {
|
||||||
environmentType.value.selectedTeam = newSelectedTeam
|
environmentType.value.selectedTeam = newSelectedTeam
|
||||||
|
|||||||
@@ -46,6 +46,7 @@
|
|||||||
role="menu"
|
role="menu"
|
||||||
@keyup.e="edit!.$el.click()"
|
@keyup.e="edit!.$el.click()"
|
||||||
@keyup.d="duplicate!.$el.click()"
|
@keyup.d="duplicate!.$el.click()"
|
||||||
|
@keyup.x="exportAction!.$el.click()"
|
||||||
@keyup.delete="
|
@keyup.delete="
|
||||||
!(environmentIndex === 'Global')
|
!(environmentIndex === 'Global')
|
||||||
? deleteAction!.$el.click()
|
? deleteAction!.$el.click()
|
||||||
@@ -77,6 +78,18 @@
|
|||||||
}
|
}
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
|
<SmartItem
|
||||||
|
ref="exportAction"
|
||||||
|
:icon="IconDownload"
|
||||||
|
:label="t('export.title')"
|
||||||
|
:shortcut="['X']"
|
||||||
|
@click="
|
||||||
|
() => {
|
||||||
|
exportEnvironment()
|
||||||
|
hide()
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
<SmartItem
|
<SmartItem
|
||||||
v-if="environmentIndex !== 'Global'"
|
v-if="environmentIndex !== 'Global'"
|
||||||
ref="deleteAction"
|
ref="deleteAction"
|
||||||
@@ -108,6 +121,7 @@ import IconMoreVertical from "~icons/lucide/more-vertical"
|
|||||||
import IconEdit from "~icons/lucide/edit"
|
import IconEdit from "~icons/lucide/edit"
|
||||||
import IconCopy from "~icons/lucide/copy"
|
import IconCopy from "~icons/lucide/copy"
|
||||||
import IconTrash2 from "~icons/lucide/trash-2"
|
import IconTrash2 from "~icons/lucide/trash-2"
|
||||||
|
import IconDownload from "~icons/lucide/download"
|
||||||
import { ref } from "vue"
|
import { ref } from "vue"
|
||||||
import { Environment } from "@hoppscotch/data"
|
import { Environment } from "@hoppscotch/data"
|
||||||
import { cloneDeep } from "lodash-es"
|
import { cloneDeep } from "lodash-es"
|
||||||
@@ -122,7 +136,7 @@ import {
|
|||||||
import { useI18n } from "@composables/i18n"
|
import { useI18n } from "@composables/i18n"
|
||||||
import { useToast } from "@composables/toast"
|
import { useToast } from "@composables/toast"
|
||||||
import { TippyComponent } from "vue-tippy"
|
import { TippyComponent } from "vue-tippy"
|
||||||
import SmartItem from "@hoppscotch/ui/src/components/smart/Item.vue"
|
import SmartItem from "@components/smart/Item.vue"
|
||||||
|
|
||||||
const t = useI18n()
|
const t = useI18n()
|
||||||
const toast = useToast()
|
const toast = useToast()
|
||||||
@@ -140,9 +154,10 @@ const confirmRemove = ref(false)
|
|||||||
|
|
||||||
const tippyActions = ref<TippyComponent | null>(null)
|
const tippyActions = ref<TippyComponent | null>(null)
|
||||||
const options = ref<TippyComponent | null>(null)
|
const options = ref<TippyComponent | null>(null)
|
||||||
const edit = ref<typeof SmartItem>()
|
const edit = ref<typeof SmartItem | null>(null)
|
||||||
const duplicate = ref<typeof SmartItem>()
|
const duplicate = ref<typeof SmartItem | null>(null)
|
||||||
const deleteAction = ref<typeof SmartItem>()
|
const deleteAction = ref<typeof SmartItem | null>(null)
|
||||||
|
const exportAction = ref<typeof SmartItem | null>(null)
|
||||||
|
|
||||||
const removeEnvironment = () => {
|
const removeEnvironment = () => {
|
||||||
if (props.environmentIndex === null) return
|
if (props.environmentIndex === null) return
|
||||||
@@ -161,4 +176,22 @@ const duplicateEnvironments = () => {
|
|||||||
)
|
)
|
||||||
} else duplicateEnvironment(props.environmentIndex)
|
} else duplicateEnvironment(props.environmentIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const exportEnvironment = () => {
|
||||||
|
const environmentJSON = JSON.stringify(props.environment)
|
||||||
|
|
||||||
|
const file = new Blob([environmentJSON], { type: "application/json" })
|
||||||
|
const a = document.createElement("a")
|
||||||
|
const url = URL.createObjectURL(file)
|
||||||
|
a.href = url
|
||||||
|
|
||||||
|
a.download = `${props.environment.name}.json`
|
||||||
|
document.body.appendChild(a)
|
||||||
|
a.click()
|
||||||
|
toast.success(t("state.download_started").toString())
|
||||||
|
setTimeout(() => {
|
||||||
|
document.body.removeChild(a)
|
||||||
|
URL.revokeObjectURL(url)
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
interactive
|
interactive
|
||||||
trigger="click"
|
trigger="click"
|
||||||
theme="popover"
|
theme="popover"
|
||||||
|
arrow
|
||||||
:on-shown="() => tippyActions!.focus()"
|
:on-shown="() => tippyActions!.focus()"
|
||||||
>
|
>
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
@@ -39,6 +40,7 @@
|
|||||||
role="menu"
|
role="menu"
|
||||||
@keyup.e="edit!.$el.click()"
|
@keyup.e="edit!.$el.click()"
|
||||||
@keyup.d="duplicate!.$el.click()"
|
@keyup.d="duplicate!.$el.click()"
|
||||||
|
@keyup.x="exportAction!.$el.click()"
|
||||||
@keyup.delete="deleteAction!.$el.click()"
|
@keyup.delete="deleteAction!.$el.click()"
|
||||||
@keyup.escape="options!.tippy().hide()"
|
@keyup.escape="options!.tippy().hide()"
|
||||||
>
|
>
|
||||||
@@ -66,6 +68,18 @@
|
|||||||
}
|
}
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
|
<SmartItem
|
||||||
|
ref="exportAction"
|
||||||
|
:icon="IconDownload"
|
||||||
|
:label="t('export.title')"
|
||||||
|
:shortcut="['X']"
|
||||||
|
@click="
|
||||||
|
() => {
|
||||||
|
exportEnvironment()
|
||||||
|
hide()
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
<SmartItem
|
<SmartItem
|
||||||
ref="deleteAction"
|
ref="deleteAction"
|
||||||
:icon="IconTrash2"
|
:icon="IconTrash2"
|
||||||
@@ -107,8 +121,9 @@ import IconEdit from "~icons/lucide/edit"
|
|||||||
import IconCopy from "~icons/lucide/copy"
|
import IconCopy from "~icons/lucide/copy"
|
||||||
import IconTrash2 from "~icons/lucide/trash-2"
|
import IconTrash2 from "~icons/lucide/trash-2"
|
||||||
import IconMoreVertical from "~icons/lucide/more-vertical"
|
import IconMoreVertical from "~icons/lucide/more-vertical"
|
||||||
|
import IconDownload from "~icons/lucide/download"
|
||||||
import { TippyComponent } from "vue-tippy"
|
import { TippyComponent } from "vue-tippy"
|
||||||
import SmartItem from "@hoppscotch/ui/src/components/smart/Item.vue"
|
import SmartItem from "@components/smart/Item.vue"
|
||||||
|
|
||||||
const t = useI18n()
|
const t = useI18n()
|
||||||
const toast = useToast()
|
const toast = useToast()
|
||||||
@@ -126,9 +141,10 @@ const confirmRemove = ref(false)
|
|||||||
|
|
||||||
const tippyActions = ref<TippyComponent | null>(null)
|
const tippyActions = ref<TippyComponent | null>(null)
|
||||||
const options = ref<TippyComponent | null>(null)
|
const options = ref<TippyComponent | null>(null)
|
||||||
const edit = ref<typeof SmartItem>()
|
const edit = ref<typeof SmartItem | null>(null)
|
||||||
const duplicate = ref<typeof SmartItem>()
|
const duplicate = ref<typeof SmartItem | null>(null)
|
||||||
const deleteAction = ref<typeof SmartItem>()
|
const deleteAction = ref<typeof SmartItem | null>(null)
|
||||||
|
const exportAction = ref<typeof SmartItem | null>(null)
|
||||||
|
|
||||||
const removeEnvironment = () => {
|
const removeEnvironment = () => {
|
||||||
pipe(
|
pipe(
|
||||||
@@ -172,4 +188,22 @@ const getErrorMessage = (err: GQLError<string>) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const exportEnvironment = () => {
|
||||||
|
const environmentJSON = JSON.stringify(props.environment.environment)
|
||||||
|
|
||||||
|
const file = new Blob([environmentJSON], { type: "application/json" })
|
||||||
|
const a = document.createElement("a")
|
||||||
|
const url = URL.createObjectURL(file)
|
||||||
|
a.href = url
|
||||||
|
|
||||||
|
a.download = `${props.environment.environment.name}.json`
|
||||||
|
document.body.appendChild(a)
|
||||||
|
a.click()
|
||||||
|
toast.success(t("state.download_started").toString())
|
||||||
|
setTimeout(() => {
|
||||||
|
document.body.removeChild(a)
|
||||||
|
URL.revokeObjectURL(url)
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -122,7 +122,16 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from "vue"
|
import { defineComponent } from "vue"
|
||||||
import { platform } from "~/platform"
|
import {
|
||||||
|
signInUserWithGoogle,
|
||||||
|
signInUserWithGithub,
|
||||||
|
signInUserWithMicrosoft,
|
||||||
|
setProviderInfo,
|
||||||
|
currentUser$,
|
||||||
|
signInWithEmail,
|
||||||
|
linkWithFBCredentialFromAuthError,
|
||||||
|
getGithubCredentialFromResult,
|
||||||
|
} from "~/helpers/fb/auth"
|
||||||
import IconGithub from "~icons/auth/github"
|
import IconGithub from "~icons/auth/github"
|
||||||
import IconGoogle from "~icons/auth/google"
|
import IconGoogle from "~icons/auth/google"
|
||||||
import IconEmail from "~icons/auth/email"
|
import IconEmail from "~icons/auth/email"
|
||||||
@@ -165,8 +174,6 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
const currentUser$ = platform.auth.getCurrentUserStream()
|
|
||||||
|
|
||||||
this.subscribeToStream(currentUser$, (user) => {
|
this.subscribeToStream(currentUser$, (user) => {
|
||||||
if (user) this.hideModal()
|
if (user) this.hideModal()
|
||||||
})
|
})
|
||||||
@@ -179,7 +186,8 @@ export default defineComponent({
|
|||||||
this.signingInWithGoogle = true
|
this.signingInWithGoogle = true
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await platform.auth.signInUserWithGoogle()
|
await signInUserWithGoogle()
|
||||||
|
this.showLoginSuccess()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
/*
|
/*
|
||||||
@@ -194,32 +202,35 @@ export default defineComponent({
|
|||||||
async signInWithGithub() {
|
async signInWithGithub() {
|
||||||
this.signingInWithGitHub = true
|
this.signingInWithGitHub = true
|
||||||
|
|
||||||
const result = await platform.auth.signInUserWithGithub()
|
try {
|
||||||
|
const result = await signInUserWithGithub()
|
||||||
|
const credential = getGithubCredentialFromResult(result)!
|
||||||
|
const token = credential.accessToken
|
||||||
|
setProviderInfo(result.providerId!, token!)
|
||||||
|
|
||||||
if (!result) {
|
this.showLoginSuccess()
|
||||||
this.signingInWithGitHub = false
|
} catch (e) {
|
||||||
return
|
console.error(e)
|
||||||
}
|
// This user's email is already present in Firebase but with other providers, namely Google or Microsoft
|
||||||
|
if (
|
||||||
|
(e as any).code === "auth/account-exists-with-different-credential"
|
||||||
|
) {
|
||||||
|
this.toast.info(`${this.t("auth.account_exists")}`, {
|
||||||
|
duration: 0,
|
||||||
|
closeOnSwipe: false,
|
||||||
|
action: {
|
||||||
|
text: `${this.t("action.yes")}`,
|
||||||
|
onClick: async (_, toastObject) => {
|
||||||
|
await linkWithFBCredentialFromAuthError(e)
|
||||||
|
this.showLoginSuccess()
|
||||||
|
|
||||||
if (result.type === "success") {
|
toastObject.goAway(0)
|
||||||
// this.showLoginSuccess()
|
},
|
||||||
} else if (result.type === "account-exists-with-different-cred") {
|
|
||||||
this.toast.info(`${this.t("auth.account_exists")}`, {
|
|
||||||
duration: 0,
|
|
||||||
closeOnSwipe: false,
|
|
||||||
action: {
|
|
||||||
text: `${this.t("action.yes")}`,
|
|
||||||
onClick: async (_, toastObject) => {
|
|
||||||
await result.link()
|
|
||||||
this.showLoginSuccess()
|
|
||||||
|
|
||||||
toastObject.goAway(0)
|
|
||||||
},
|
},
|
||||||
},
|
})
|
||||||
})
|
} else {
|
||||||
} else {
|
this.toast.error(`${this.t("error.something_went_wrong")}`)
|
||||||
console.log("error logging into github", result.err)
|
}
|
||||||
this.toast.error(`${this.t("error.something_went_wrong")}`)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.signingInWithGitHub = false
|
this.signingInWithGitHub = false
|
||||||
@@ -228,8 +239,8 @@ export default defineComponent({
|
|||||||
this.signingInWithMicrosoft = true
|
this.signingInWithMicrosoft = true
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await platform.auth.signInUserWithMicrosoft()
|
await signInUserWithMicrosoft()
|
||||||
// this.showLoginSuccess()
|
this.showLoginSuccess()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
/*
|
/*
|
||||||
@@ -248,8 +259,11 @@ export default defineComponent({
|
|||||||
async signInWithEmail() {
|
async signInWithEmail() {
|
||||||
this.signingInWithEmail = true
|
this.signingInWithEmail = true
|
||||||
|
|
||||||
await platform.auth
|
const actionCodeSettings = {
|
||||||
.signInWithEmail(this.form.email)
|
url: `${import.meta.env.VITE_BASE_URL}/enter`,
|
||||||
|
handleCodeInApp: true,
|
||||||
|
}
|
||||||
|
await signInWithEmail(this.form.email, actionCodeSettings)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.mode = "email-sent"
|
this.mode = "email-sent"
|
||||||
setLocalConfig("emailForSignIn", this.form.email)
|
setLocalConfig("emailForSignIn", this.form.email)
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import { ref } from "vue"
|
|||||||
import IconLogOut from "~icons/lucide/log-out"
|
import IconLogOut from "~icons/lucide/log-out"
|
||||||
import { useToast } from "@composables/toast"
|
import { useToast } from "@composables/toast"
|
||||||
import { useI18n } from "@composables/i18n"
|
import { useI18n } from "@composables/i18n"
|
||||||
import { platform } from "~/platform"
|
import { signOutUser } from "~/helpers/fb/auth"
|
||||||
|
|
||||||
defineProps({
|
defineProps({
|
||||||
outline: {
|
outline: {
|
||||||
@@ -47,7 +47,7 @@ const t = useI18n()
|
|||||||
|
|
||||||
const logout = async () => {
|
const logout = async () => {
|
||||||
try {
|
try {
|
||||||
await platform.auth.signOutUser()
|
await signOutUser()
|
||||||
toast.success(`${t("auth.logged_out")}`)
|
toast.success(`${t("auth.logged_out")}`)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
|
|||||||
@@ -99,7 +99,7 @@
|
|||||||
:show-more="showMore"
|
:show-more="showMore"
|
||||||
@toggle-star="toggleStar(entry.entry)"
|
@toggle-star="toggleStar(entry.entry)"
|
||||||
@delete-entry="deleteHistory(entry.entry)"
|
@delete-entry="deleteHistory(entry.entry)"
|
||||||
@use-entry="useHistory(toRaw(entry.entry))"
|
@use-entry="useHistory(entry.entry)"
|
||||||
/>
|
/>
|
||||||
</details>
|
</details>
|
||||||
</div>
|
</div>
|
||||||
@@ -164,7 +164,7 @@ import IconHelpCircle from "~icons/lucide/help-circle"
|
|||||||
import IconTrash2 from "~icons/lucide/trash-2"
|
import IconTrash2 from "~icons/lucide/trash-2"
|
||||||
import IconTrash from "~icons/lucide/trash"
|
import IconTrash from "~icons/lucide/trash"
|
||||||
import IconFilter from "~icons/lucide/filter"
|
import IconFilter from "~icons/lucide/filter"
|
||||||
import { computed, ref, Ref, toRaw } from "vue"
|
import { computed, ref, Ref } from "vue"
|
||||||
import { useColorMode } from "@composables/theming"
|
import { useColorMode } from "@composables/theming"
|
||||||
import {
|
import {
|
||||||
HoppGQLRequest,
|
HoppGQLRequest,
|
||||||
@@ -331,25 +331,14 @@ const setRestReq = (request: HoppRESTRequest | null | undefined) => {
|
|||||||
// (That is not a really good behaviour tho ¯\_(ツ)_/¯)
|
// (That is not a really good behaviour tho ¯\_(ツ)_/¯)
|
||||||
const useHistory = (entry: RESTHistoryEntry) => {
|
const useHistory = (entry: RESTHistoryEntry) => {
|
||||||
const currentFullReq = getRESTRequest()
|
const currentFullReq = getRESTRequest()
|
||||||
|
|
||||||
const currentReqWithNoChange = getRESTSaveContext()?.req
|
|
||||||
|
|
||||||
// checks if the current request is the same as the save context request if present
|
|
||||||
if (
|
|
||||||
currentReqWithNoChange &&
|
|
||||||
isEqualHoppRESTRequest(currentReqWithNoChange, currentFullReq)
|
|
||||||
) {
|
|
||||||
props.page === "rest" && setRestReq(entry.request)
|
|
||||||
clickedHistory.value = entry
|
|
||||||
}
|
|
||||||
// Initial state trigers a popup
|
// Initial state trigers a popup
|
||||||
else if (!clickedHistory.value) {
|
if (!clickedHistory.value) {
|
||||||
clickedHistory.value = entry
|
clickedHistory.value = entry
|
||||||
confirmChange.value = true
|
confirmChange.value = true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Checks if there are any change done in current request and the history request
|
// Checks if there are any change done in current request and the history request
|
||||||
else if (
|
if (
|
||||||
!isEqualHoppRESTRequest(
|
!isEqualHoppRESTRequest(
|
||||||
currentFullReq,
|
currentFullReq,
|
||||||
clickedHistory.value.request as HoppRESTRequest
|
clickedHistory.value.request as HoppRESTRequest
|
||||||
@@ -358,7 +347,7 @@ const useHistory = (entry: RESTHistoryEntry) => {
|
|||||||
clickedHistory.value = entry
|
clickedHistory.value = entry
|
||||||
confirmChange.value = true
|
confirmChange.value = true
|
||||||
} else {
|
} else {
|
||||||
props.page === "rest" && setRestReq(entry.request)
|
props.page === "rest" && setRestReq(entry.request as HoppRESTRequest)
|
||||||
clickedHistory.value = entry
|
clickedHistory.value = entry
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,15 +28,7 @@
|
|||||||
@click.prevent="linewrapEnabled = !linewrapEnabled"
|
@click.prevent="linewrapEnabled = !linewrapEnabled"
|
||||||
/>
|
/>
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
v-if="
|
v-if="contentType && contentType.endsWith('json')"
|
||||||
[
|
|
||||||
'application/json',
|
|
||||||
'application/ld+json',
|
|
||||||
'application/hal+json',
|
|
||||||
'application/vnd.api+json',
|
|
||||||
'application/xml',
|
|
||||||
].includes(contentType)
|
|
||||||
"
|
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
:title="t('action.prettify')"
|
:title="t('action.prettify')"
|
||||||
:icon="prettifyIcon"
|
:icon="prettifyIcon"
|
||||||
@@ -47,7 +39,7 @@
|
|||||||
v-tippy="{ theme: 'tooltip' }"
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
:title="t('import.title')"
|
:title="t('import.title')"
|
||||||
:icon="IconFilePlus"
|
:icon="IconFilePlus"
|
||||||
@click="payload?.click()"
|
@click="$refs.payload.click()"
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@@ -55,7 +47,7 @@
|
|||||||
class="input"
|
class="input"
|
||||||
name="payload"
|
name="payload"
|
||||||
type="file"
|
type="file"
|
||||||
@change="uploadPayload($event)"
|
@change="uploadPayload"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -94,8 +86,6 @@ type PossibleContentTypes = Exclude<
|
|||||||
|
|
||||||
const t = useI18n()
|
const t = useI18n()
|
||||||
|
|
||||||
const payload = ref<HTMLInputElement | null>(null)
|
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
contentType: PossibleContentTypes
|
contentType: PossibleContentTypes
|
||||||
}>()
|
}>()
|
||||||
@@ -155,7 +145,7 @@ const clearContent = () => {
|
|||||||
rawParamsBody.value = ""
|
rawParamsBody.value = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
const uploadPayload = async (e: Event) => {
|
const uploadPayload = async (e: InputEvent) => {
|
||||||
await pipe(
|
await pipe(
|
||||||
(e.target as HTMLInputElement).files?.[0],
|
(e.target as HTMLInputElement).files?.[0],
|
||||||
TO.of,
|
TO.of,
|
||||||
@@ -171,17 +161,10 @@ const uploadPayload = async (e: Event) => {
|
|||||||
)
|
)
|
||||||
)()
|
)()
|
||||||
}
|
}
|
||||||
|
|
||||||
const prettifyRequestBody = () => {
|
const prettifyRequestBody = () => {
|
||||||
let prettifyBody = ""
|
|
||||||
try {
|
try {
|
||||||
if (props.contentType.endsWith("json")) {
|
const jsonObj = JSON.parse(rawParamsBody.value)
|
||||||
const jsonObj = JSON.parse(rawParamsBody.value as string)
|
rawParamsBody.value = JSON.stringify(jsonObj, null, 2)
|
||||||
prettifyBody = JSON.stringify(jsonObj, null, 2)
|
|
||||||
} else if (props.contentType == "application/xml") {
|
|
||||||
prettifyBody = prettifyXML(rawParamsBody.value as string)
|
|
||||||
}
|
|
||||||
rawParamsBody.value = prettifyBody
|
|
||||||
prettifyIcon.value = IconCheck
|
prettifyIcon.value = IconCheck
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
@@ -189,28 +172,4 @@ const prettifyRequestBody = () => {
|
|||||||
toast.error(`${t("error.json_prettify_invalid_body")}`)
|
toast.error(`${t("error.json_prettify_invalid_body")}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const prettifyXML = (xml: string) => {
|
|
||||||
const PADDING = " ".repeat(2) // set desired indent size here
|
|
||||||
const reg = /(>)(<)(\/*)/g
|
|
||||||
let pad = 0
|
|
||||||
xml = xml.replace(reg, "$1\r\n$2$3")
|
|
||||||
return xml
|
|
||||||
.split("\r\n")
|
|
||||||
.map((node) => {
|
|
||||||
let indent = 0
|
|
||||||
if (node.match(/.+<\/\w[^>]*>$/)) {
|
|
||||||
indent = 0
|
|
||||||
} else if (node.match(/^<\/\w/) && pad > 0) {
|
|
||||||
pad -= 1
|
|
||||||
} else if (node.match(/^<\w[^>]*[^\/]>.*$/)) {
|
|
||||||
indent = 1
|
|
||||||
} else {
|
|
||||||
indent = 0
|
|
||||||
}
|
|
||||||
pad += indent
|
|
||||||
return PADDING.repeat(pad - indent) + node
|
|
||||||
})
|
|
||||||
.join("\r\n")
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -16,15 +16,14 @@
|
|||||||
<ButtonPrimary
|
<ButtonPrimary
|
||||||
v-focus
|
v-focus
|
||||||
:label="t('action.save')"
|
:label="t('action.save')"
|
||||||
:loading="loading"
|
|
||||||
outline
|
outline
|
||||||
@click="saveChange"
|
@click="saveApiChange"
|
||||||
/>
|
/>
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
:label="t('action.dont_save')"
|
:label="t('action.dont_save')"
|
||||||
outline
|
outline
|
||||||
filled
|
filled
|
||||||
@click="discardChange"
|
@click="discardApiChange"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
@@ -44,7 +43,6 @@ const t = useI18n()
|
|||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
show: boolean
|
show: boolean
|
||||||
loading?: boolean
|
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
@@ -53,11 +51,11 @@ const emit = defineEmits<{
|
|||||||
(e: "hide-modal"): void
|
(e: "hide-modal"): void
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const saveChange = () => {
|
const saveApiChange = () => {
|
||||||
emit("save-change")
|
emit("save-change")
|
||||||
}
|
}
|
||||||
|
|
||||||
const discardChange = () => {
|
const discardApiChange = () => {
|
||||||
emit("discard-change")
|
emit("discard-change")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,175 +0,0 @@
|
|||||||
<template>
|
|
||||||
<section class="p-4">
|
|
||||||
<h4 class="font-semibold text-secondaryDark">
|
|
||||||
{{ t("settings.short_codes") }}
|
|
||||||
</h4>
|
|
||||||
<div class="my-1 text-secondaryLight">
|
|
||||||
{{ t("settings.short_codes_description") }}
|
|
||||||
</div>
|
|
||||||
<div class="relative py-4 overflow-x-auto">
|
|
||||||
<div v-if="loading" class="flex flex-col items-center justify-center">
|
|
||||||
<SmartSpinner class="mb-4" />
|
|
||||||
<span class="text-secondaryLight">{{ t("state.loading") }}</span>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-if="!loading && myShortcodes.length === 0"
|
|
||||||
class="flex flex-col items-center justify-center p-4 text-secondaryLight"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
:src="`/images/states/${colorMode.value}/add_files.svg`"
|
|
||||||
loading="lazy"
|
|
||||||
class="inline-flex flex-col object-contain object-center w-16 h-16 mb-8"
|
|
||||||
:alt="`${t('empty.shortcodes')}`"
|
|
||||||
/>
|
|
||||||
<span class="mb-4 text-center">
|
|
||||||
{{ t("empty.shortcodes") }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div v-else-if="!loading">
|
|
||||||
<div
|
|
||||||
class="hidden w-full border-t rounded-t bg-primaryLight lg:flex border-x border-dividerLight"
|
|
||||||
>
|
|
||||||
<div class="flex w-full overflow-y-scroll">
|
|
||||||
<div class="table-box">
|
|
||||||
{{ t("shortcodes.short_code") }}
|
|
||||||
</div>
|
|
||||||
<div class="table-box">
|
|
||||||
{{ t("shortcodes.method") }}
|
|
||||||
</div>
|
|
||||||
<div class="table-box">
|
|
||||||
{{ t("shortcodes.url") }}
|
|
||||||
</div>
|
|
||||||
<div class="table-box">
|
|
||||||
{{ t("shortcodes.created_on") }}
|
|
||||||
</div>
|
|
||||||
<div class="justify-center table-box">
|
|
||||||
{{ t("shortcodes.actions") }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="flex flex-col items-center justify-between w-full overflow-y-scroll border rounded max-h-sm lg:rounded-t-none lg:divide-y border-dividerLight divide-dividerLight"
|
|
||||||
>
|
|
||||||
<ProfileShortcode
|
|
||||||
v-for="(shortcode, shortcodeIndex) in myShortcodes"
|
|
||||||
:key="`shortcode-${shortcodeIndex}`"
|
|
||||||
:shortcode="shortcode"
|
|
||||||
@delete-shortcode="deleteShortcode"
|
|
||||||
/>
|
|
||||||
<SmartIntersection
|
|
||||||
v-if="hasMoreShortcodes && myShortcodes.length > 0"
|
|
||||||
@intersecting="loadMoreShortcodes()"
|
|
||||||
>
|
|
||||||
<div v-if="adapterLoading" class="flex flex-col items-center py-3">
|
|
||||||
<SmartSpinner />
|
|
||||||
</div>
|
|
||||||
</SmartIntersection>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-if="!loading && adapterError"
|
|
||||||
class="flex flex-col items-center py-4"
|
|
||||||
>
|
|
||||||
<icon-lucide-help-circle class="mb-4 svg-icons" />
|
|
||||||
{{ getErrorMessage(adapterError) }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { ref, watchEffect, computed } from "vue"
|
|
||||||
import { pipe } from "fp-ts/function"
|
|
||||||
import * as TE from "fp-ts/TaskEither"
|
|
||||||
import { GQLError } from "~/helpers/backend/GQLClient"
|
|
||||||
import { platform } from "~/platform"
|
|
||||||
|
|
||||||
import { onAuthEvent, onLoggedIn } from "@composables/auth"
|
|
||||||
import { useReadonlyStream } from "@composables/stream"
|
|
||||||
import { useI18n } from "@composables/i18n"
|
|
||||||
import { useToast } from "@composables/toast"
|
|
||||||
import { useColorMode } from "@composables/theming"
|
|
||||||
import { usePageHead } from "@composables/head"
|
|
||||||
|
|
||||||
import ShortcodeListAdapter from "~/helpers/shortcodes/ShortcodeListAdapter"
|
|
||||||
import { deleteShortcode as backendDeleteShortcode } from "~/helpers/backend/mutations/Shortcode"
|
|
||||||
|
|
||||||
const t = useI18n()
|
|
||||||
const toast = useToast()
|
|
||||||
const colorMode = useColorMode()
|
|
||||||
|
|
||||||
usePageHead({
|
|
||||||
title: computed(() => t("navigation.profile")),
|
|
||||||
})
|
|
||||||
|
|
||||||
const currentUser = useReadonlyStream(
|
|
||||||
platform.auth.getCurrentUserStream(),
|
|
||||||
platform.auth.getCurrentUser()
|
|
||||||
)
|
|
||||||
|
|
||||||
const displayName = ref(currentUser.value?.displayName)
|
|
||||||
watchEffect(() => (displayName.value = currentUser.value?.displayName))
|
|
||||||
|
|
||||||
const emailAddress = ref(currentUser.value?.email)
|
|
||||||
watchEffect(() => (emailAddress.value = currentUser.value?.email))
|
|
||||||
|
|
||||||
const adapter = new ShortcodeListAdapter(true)
|
|
||||||
const adapterLoading = useReadonlyStream(adapter.loading$, false)
|
|
||||||
const adapterError = useReadonlyStream(adapter.error$, null)
|
|
||||||
const myShortcodes = useReadonlyStream(adapter.shortcodes$, [])
|
|
||||||
const hasMoreShortcodes = useReadonlyStream(adapter.hasMoreShortcodes$, true)
|
|
||||||
|
|
||||||
const loading = computed(
|
|
||||||
() => adapterLoading.value && myShortcodes.value.length === 0
|
|
||||||
)
|
|
||||||
|
|
||||||
onLoggedIn(() => {
|
|
||||||
try {
|
|
||||||
adapter.initialize()
|
|
||||||
} catch (e) {}
|
|
||||||
})
|
|
||||||
|
|
||||||
onAuthEvent((ev) => {
|
|
||||||
if (ev.event === "logout" && adapter.isInitialized()) {
|
|
||||||
adapter.dispose()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const deleteShortcode = (codeID: string) => {
|
|
||||||
pipe(
|
|
||||||
backendDeleteShortcode(codeID),
|
|
||||||
TE.match(
|
|
||||||
(err: GQLError<string>) => {
|
|
||||||
toast.error(`${getErrorMessage(err)}`)
|
|
||||||
},
|
|
||||||
() => {
|
|
||||||
toast.success(`${t("shortcodes.deleted")}`)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)()
|
|
||||||
}
|
|
||||||
|
|
||||||
const loadMoreShortcodes = () => {
|
|
||||||
adapter.loadMore()
|
|
||||||
}
|
|
||||||
|
|
||||||
const getErrorMessage = (err: GQLError<string>) => {
|
|
||||||
if (err.type === "network_error") {
|
|
||||||
return t("error.network_error")
|
|
||||||
} else {
|
|
||||||
switch (err.error) {
|
|
||||||
case "shortcode/not_found":
|
|
||||||
return t("shortcodes.not_found")
|
|
||||||
default:
|
|
||||||
return t("error.something_went_wrong")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.table-box {
|
|
||||||
@apply flex flex-1 items-center px-4 py-2 truncate;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,184 +0,0 @@
|
|||||||
<template>
|
|
||||||
<section class="p-4">
|
|
||||||
<h4 class="font-semibold text-secondaryDark">
|
|
||||||
{{ t("settings.delete_account") }}
|
|
||||||
</h4>
|
|
||||||
<div class="my-1 mb-4 text-secondaryLight">
|
|
||||||
{{ t("settings.delete_account_description") }}
|
|
||||||
</div>
|
|
||||||
<ButtonSecondary
|
|
||||||
filled
|
|
||||||
outline
|
|
||||||
:label="t('settings.delete_account')"
|
|
||||||
type="submit"
|
|
||||||
@click="showDeleteAccountModal = true"
|
|
||||||
/>
|
|
||||||
<SmartModal
|
|
||||||
v-if="showDeleteAccountModal"
|
|
||||||
dialog
|
|
||||||
:title="t('settings.delete_account')"
|
|
||||||
@close="showDeleteAccountModal = false"
|
|
||||||
>
|
|
||||||
<template #body>
|
|
||||||
<div v-if="loading" class="flex flex-col items-center justify-center">
|
|
||||||
<SmartSpinner class="mb-4" />
|
|
||||||
<span class="text-secondaryLight">{{ t("state.loading") }}</span>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-else-if="myTeams.length"
|
|
||||||
class="flex flex-col p-4 space-y-2 border border-red-500 rounded-lg text-secondaryDark bg-error"
|
|
||||||
>
|
|
||||||
<h2 class="font-bold text-red-500">
|
|
||||||
{{ t("error.danger_zone") }}
|
|
||||||
</h2>
|
|
||||||
<div>
|
|
||||||
{{ t("error.delete_account") }}
|
|
||||||
<ul class="my-4 ml-8 space-y-2 list-disc">
|
|
||||||
<li v-for="team in myTeams" :key="team.id">
|
|
||||||
{{ team.name }}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<span class="font-semibold">
|
|
||||||
{{ t("error.delete_account_description") }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-else>
|
|
||||||
<div
|
|
||||||
class="flex flex-col p-4 mb-4 space-y-2 border border-red-500 rounded-lg text-secondaryDark bg-error"
|
|
||||||
>
|
|
||||||
<h2 class="font-bold text-red-500">
|
|
||||||
{{ t("error.danger_zone") }}
|
|
||||||
</h2>
|
|
||||||
<div class="font-medium text-secondaryDark">
|
|
||||||
{{ t("settings.delete_account_description") }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="flex flex-col">
|
|
||||||
<input
|
|
||||||
id="deleteUserAccount"
|
|
||||||
v-model="userVerificationInput"
|
|
||||||
class="input floating-input"
|
|
||||||
placeholder=" "
|
|
||||||
type="text"
|
|
||||||
autocomplete="off"
|
|
||||||
/>
|
|
||||||
<label for="deleteUserAccount">
|
|
||||||
Type
|
|
||||||
<span class="font-bold"> delete my account </span>
|
|
||||||
to confirm
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template #footer>
|
|
||||||
<span class="flex space-x-2">
|
|
||||||
<ButtonPrimary
|
|
||||||
:label="t('settings.delete_account')"
|
|
||||||
:loading="deletingUser"
|
|
||||||
filled
|
|
||||||
outline
|
|
||||||
:disabled="
|
|
||||||
loading ||
|
|
||||||
myTeams.length > 0 ||
|
|
||||||
userVerificationInput !== 'delete my account'
|
|
||||||
"
|
|
||||||
class="!bg-red-500 !hover:bg-red-600 !border-red-500 !hover:border-red-600"
|
|
||||||
@click="deleteUserAccount"
|
|
||||||
/>
|
|
||||||
<ButtonSecondary
|
|
||||||
:label="t('action.cancel')"
|
|
||||||
outline
|
|
||||||
filled
|
|
||||||
@click="showDeleteAccountModal = false"
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
</SmartModal>
|
|
||||||
</section>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { pipe } from "fp-ts/function"
|
|
||||||
import * as TE from "fp-ts/TaskEither"
|
|
||||||
import { ref, watch } from "vue"
|
|
||||||
import { GQLError, runGQLQuery } from "~/helpers/backend/GQLClient"
|
|
||||||
import * as E from "fp-ts/Either"
|
|
||||||
import { useRouter } from "vue-router"
|
|
||||||
import { useI18n } from "~/composables/i18n"
|
|
||||||
import { useToast } from "~/composables/toast"
|
|
||||||
import { GetMyTeamsDocument, GetMyTeamsQuery } from "~/helpers/backend/graphql"
|
|
||||||
import { deleteUser } from "~/helpers/backend/mutations/Profile"
|
|
||||||
import { platform } from "~/platform"
|
|
||||||
|
|
||||||
const t = useI18n()
|
|
||||||
const toast = useToast()
|
|
||||||
const router = useRouter()
|
|
||||||
|
|
||||||
const showDeleteAccountModal = ref(false)
|
|
||||||
const userVerificationInput = ref("")
|
|
||||||
|
|
||||||
const loading = ref(true)
|
|
||||||
const myTeams = ref<GetMyTeamsQuery["myTeams"]>([])
|
|
||||||
|
|
||||||
watch(showDeleteAccountModal, (isModalOpen) => {
|
|
||||||
if (isModalOpen) {
|
|
||||||
fetchMyTeams()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const fetchMyTeams = async () => {
|
|
||||||
loading.value = true
|
|
||||||
const result = await runGQLQuery({
|
|
||||||
query: GetMyTeamsDocument,
|
|
||||||
})
|
|
||||||
loading.value = false
|
|
||||||
|
|
||||||
if (E.isLeft(result)) {
|
|
||||||
throw new Error(
|
|
||||||
`Failed fetching teams list: ${JSON.stringify(result.left)}`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
myTeams.value = result.right.myTeams.filter((team) => {
|
|
||||||
return team.ownersCount === 1 && team.myRole === "OWNER"
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const deletingUser = ref(false)
|
|
||||||
|
|
||||||
const deleteUserAccount = async () => {
|
|
||||||
if (deletingUser.value) return
|
|
||||||
deletingUser.value = true
|
|
||||||
|
|
||||||
pipe(
|
|
||||||
deleteUser(),
|
|
||||||
TE.match(
|
|
||||||
(err: GQLError<string>) => {
|
|
||||||
deletingUser.value = false
|
|
||||||
toast.error(getErrorMessage(err))
|
|
||||||
},
|
|
||||||
() => {
|
|
||||||
deletingUser.value = false
|
|
||||||
showDeleteAccountModal.value = false
|
|
||||||
toast.success(t("settings.account_deleted"))
|
|
||||||
platform.auth.signOutUser()
|
|
||||||
router.push(`/`)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)()
|
|
||||||
}
|
|
||||||
|
|
||||||
const getErrorMessage = (err: GQLError<string>) => {
|
|
||||||
if (err.type === "network_error") {
|
|
||||||
return t("error.network_error")
|
|
||||||
} else {
|
|
||||||
switch (err.error) {
|
|
||||||
case "shortcode/not_found":
|
|
||||||
return t("shortcodes.not_found")
|
|
||||||
default:
|
|
||||||
return t("error.something_went_wrong")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
<SmartModal
|
<SmartModal
|
||||||
v-if="show"
|
v-if="show"
|
||||||
dialog
|
dialog
|
||||||
:title="confirm ?? t?.('modal.confirm') ?? 'Confirm'"
|
:title="t('modal.confirm')"
|
||||||
role="dialog"
|
role="dialog"
|
||||||
aria-modal="true"
|
aria-modal="true"
|
||||||
@close="hideModal"
|
@close="hideModal"
|
||||||
@@ -16,13 +16,13 @@
|
|||||||
<span class="flex space-x-2">
|
<span class="flex space-x-2">
|
||||||
<ButtonPrimary
|
<ButtonPrimary
|
||||||
v-focus
|
v-focus
|
||||||
:label="yes ?? t?.('action.yes') ?? 'Yes'"
|
:label="yes ?? t('action.yes')"
|
||||||
:loading="!!loadingState"
|
:loading="!!loadingState"
|
||||||
outline
|
outline
|
||||||
@click="resolve"
|
@click="resolve"
|
||||||
/>
|
/>
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
:label="no ?? t?.('action.no') ?? 'No'"
|
:label="no ?? t('action.no')"
|
||||||
filled
|
filled
|
||||||
outline
|
outline
|
||||||
@click="hideModal"
|
@click="hideModal"
|
||||||
@@ -33,23 +33,20 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { inject } from "vue"
|
import { useI18n } from "@composables/i18n"
|
||||||
import { HoppUIPluginOptions, HOPP_UI_OPTIONS } from "./../../index"
|
|
||||||
|
|
||||||
const { t } = inject<HoppUIPluginOptions>(HOPP_UI_OPTIONS) ?? {}
|
const t = useI18n()
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
show: boolean
|
show: boolean
|
||||||
title?: string | null
|
title?: string | null
|
||||||
confirm?: string | null
|
|
||||||
yes?: string | null
|
yes?: string | null
|
||||||
no?: string | null
|
no?: string | null
|
||||||
loadingState?: boolean | null
|
loadingState?: boolean | null
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
title: null,
|
title: null,
|
||||||
confirm: null,
|
|
||||||
yes: null,
|
yes: null,
|
||||||
no: null,
|
no: null,
|
||||||
loadingState: null,
|
loadingState: null,
|
||||||
@@ -9,11 +9,7 @@
|
|||||||
>
|
>
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
:icon="expand ? IconChevronUp : IconChevronDown"
|
:icon="expand ? IconChevronUp : IconChevronDown"
|
||||||
:label="
|
:label="expand ? t('action.less') : t('action.more')"
|
||||||
expand
|
|
||||||
? less ?? t?.('action.less') ?? 'Less'
|
|
||||||
: more ?? t?.('action.more') ?? 'More'
|
|
||||||
"
|
|
||||||
filled
|
filled
|
||||||
rounded
|
rounded
|
||||||
@click="expand = !expand"
|
@click="expand = !expand"
|
||||||
@@ -25,21 +21,10 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import IconChevronUp from "~icons/lucide/chevron-up"
|
import IconChevronUp from "~icons/lucide/chevron-up"
|
||||||
import IconChevronDown from "~icons/lucide/chevron-down"
|
import IconChevronDown from "~icons/lucide/chevron-down"
|
||||||
import { inject, ref } from "vue"
|
import { ref } from "vue"
|
||||||
import { HoppUIPluginOptions, HOPP_UI_OPTIONS } from "./../../index"
|
import { useI18n } from "@composables/i18n"
|
||||||
|
|
||||||
const { t } = inject<HoppUIPluginOptions>(HOPP_UI_OPTIONS) ?? {}
|
const t = useI18n()
|
||||||
|
|
||||||
const expand = ref(false)
|
const expand = ref(false)
|
||||||
|
|
||||||
withDefaults(
|
|
||||||
defineProps<{
|
|
||||||
less?: string
|
|
||||||
more?: string
|
|
||||||
}>(),
|
|
||||||
{
|
|
||||||
less: "Less",
|
|
||||||
more: "More",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
</script>
|
</script>
|
||||||
@@ -63,82 +63,78 @@
|
|||||||
</SmartLink>
|
</SmartLink>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineComponent } from "vue"
|
defineProps({
|
||||||
|
to: {
|
||||||
|
type: String,
|
||||||
|
default: "",
|
||||||
|
},
|
||||||
|
exact: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
blank: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
type: String,
|
||||||
|
default: "",
|
||||||
|
},
|
||||||
|
description: {
|
||||||
|
type: String,
|
||||||
|
default: "",
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* This will be a component!
|
||||||
|
*/
|
||||||
|
icon: {
|
||||||
|
type: Object,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* This will be a component!
|
||||||
|
*/
|
||||||
|
svg: {
|
||||||
|
type: Object,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
loading: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
reverse: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
outline: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
shortcut: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
active: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
|
||||||
export default defineComponent({
|
activeInfoIcon: {
|
||||||
props: {
|
type: Boolean,
|
||||||
to: {
|
default: false,
|
||||||
type: String,
|
},
|
||||||
default: "",
|
|
||||||
},
|
|
||||||
exact: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
blank: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
label: {
|
|
||||||
type: String,
|
|
||||||
default: "",
|
|
||||||
},
|
|
||||||
description: {
|
|
||||||
type: String,
|
|
||||||
default: "",
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* This will be a component!
|
|
||||||
*/
|
|
||||||
icon: {
|
|
||||||
type: Object,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* This will be a component!
|
|
||||||
*/
|
|
||||||
svg: {
|
|
||||||
type: Object,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
disabled: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
loading: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
reverse: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
outline: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
shortcut: {
|
|
||||||
type: Array,
|
|
||||||
default: () => [],
|
|
||||||
},
|
|
||||||
active: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
|
|
||||||
activeInfoIcon: {
|
/**
|
||||||
type: Boolean,
|
* This will be a component!
|
||||||
default: false,
|
*/
|
||||||
},
|
infoIcon: {
|
||||||
|
type: Object,
|
||||||
/**
|
default: null,
|
||||||
* This will be a component!
|
|
||||||
*/
|
|
||||||
infoIcon: {
|
|
||||||
type: Object,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user