Compare commits
108 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a795fc4310 | ||
|
|
790b743e42 | ||
|
|
21aeded2ea | ||
|
|
baf6d6bd29 | ||
|
|
2e213a8692 | ||
|
|
455cccebb5 | ||
|
|
9e602394cf | ||
|
|
46ebd49936 | ||
|
|
fcac750ad7 | ||
|
|
8cd3acd205 | ||
|
|
bc95a028ce | ||
|
|
5fb457d331 | ||
|
|
454c11a12c | ||
|
|
3ac9a418e6 | ||
|
|
c98de5988e | ||
|
|
ca5df588b7 | ||
|
|
08a41691af | ||
|
|
665f272a0e | ||
|
|
2591e3ab67 | ||
|
|
44df9b3be8 | ||
|
|
40ddfa8def | ||
|
|
cc27c552af | ||
|
|
7f248da0b3 | ||
|
|
79a0002594 | ||
|
|
a6c015bcc5 | ||
|
|
692d98cb55 | ||
|
|
d9ddc184cb | ||
|
|
e424d06026 | ||
|
|
f9821e5f80 | ||
|
|
5bd53dc093 | ||
|
|
204834872e | ||
|
|
a24049aa17 | ||
|
|
9cb9e9e7b6 | ||
|
|
3e7a766d12 | ||
|
|
02debdc28b | ||
|
|
e104fe3021 | ||
|
|
dda40537cc | ||
|
|
217269467c | ||
|
|
5f193680c9 | ||
|
|
c0ef713c0b | ||
|
|
3533aa391a | ||
|
|
ec90365427 | ||
|
|
78f7a9ba06 | ||
|
|
7793dd8b3e | ||
|
|
76866f78f0 | ||
|
|
8e3ecb4c25 | ||
|
|
1b9b2ac4c9 | ||
|
|
489f14b88f | ||
|
|
8f09c82763 | ||
|
|
2b8cda40a2 | ||
|
|
4656d67fcf | ||
|
|
e7dd67deaa | ||
|
|
b9c3219094 | ||
|
|
f55a995c0a | ||
|
|
ee378a128c | ||
|
|
c1f083d19f | ||
|
|
2ff0f97295 | ||
|
|
dd3b51d0b7 | ||
|
|
853acfda2c | ||
|
|
c756be54a1 | ||
|
|
3bbf334f99 | ||
|
|
c6c8b7da6a | ||
|
|
8a30d3ed42 | ||
|
|
a5a6b864a5 | ||
|
|
97b7320939 | ||
|
|
5c11bcb2c6 | ||
|
|
3888584b58 | ||
|
|
531a9c0bc8 | ||
|
|
84f8048e0a | ||
|
|
fdf96b0b63 | ||
|
|
86ddfe2c9f | ||
|
|
115ae93073 | ||
|
|
aa65cada6f | ||
|
|
ad9cdf3fa8 | ||
|
|
5c0035f4c3 | ||
|
|
37b4c67ead | ||
|
|
63998f4a23 | ||
|
|
efeec13794 | ||
|
|
6c49e5c86a | ||
|
|
ec57392424 | ||
|
|
8713aa7fd3 | ||
|
|
627811f28d | ||
|
|
3f743b4f61 | ||
|
|
d676b5a68d | ||
|
|
a6dfab5fbf | ||
|
|
b8bc110d33 | ||
|
|
597fd2c6d1 | ||
|
|
ab0bc3e927 | ||
|
|
e28373dae1 | ||
|
|
1bc57f159c | ||
|
|
6a8019c7a0 | ||
|
|
9b0dc5a849 | ||
|
|
283e1b0790 | ||
|
|
fc05a4bb78 | ||
|
|
88b32e317a | ||
|
|
6c61a70d58 | ||
|
|
5df3ebae22 | ||
|
|
ad252476ce | ||
|
|
8e5f6a3a96 | ||
|
|
9ff209bd32 | ||
|
|
505e61475d | ||
|
|
2c137a0eec | ||
|
|
b65ae89be6 | ||
|
|
432884a2af | ||
|
|
6878498b2e | ||
|
|
4d5332fef7 | ||
|
|
50a149b662 | ||
|
|
930838d676 |
@@ -30,7 +30,7 @@ coverage
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# editorconfig.org
|
||||
# EditorConfig is awesome: https://EditorConfig.org
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
@@ -8,6 +9,3 @@ end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
29
.env.example
29
.env.example
@@ -1,15 +1,18 @@
|
||||
# Google Analytics
|
||||
GA_ID=UA-XXXXXXXX-X
|
||||
# Google Analytics ID
|
||||
GA_ID=UA-61422507-4
|
||||
|
||||
# Google Tag Manager
|
||||
GTM_ID=GTM-XXXXXXX
|
||||
# Google Tag Manager ID
|
||||
GTM_ID=GTM-NMKVBMV
|
||||
|
||||
# Firebase
|
||||
API_KEY=api-key
|
||||
AUTH_DOMAIN=project-id.firebaseapp.com
|
||||
DATABASE_URL=https://project-id.firebaseio.com
|
||||
PROJECT_ID=project-id
|
||||
STORAGE_BUCKET=project-id.appspot.com
|
||||
MESSAGING_SENDER_ID=sender-id
|
||||
APP_ID=app-id
|
||||
MEASUREMENT_ID=G-measurement-id
|
||||
# Firebase config
|
||||
API_KEY=AIzaSyCMsFreESs58-hRxTtiqQrIcimh4i1wbsM
|
||||
AUTH_DOMAIN=postwoman-api.firebaseapp.com
|
||||
DATABASE_URL=https://postwoman-api.firebaseio.com
|
||||
PROJECT_ID=postwoman-api
|
||||
STORAGE_BUCKET=postwoman-api.appspot.com
|
||||
MESSAGING_SENDER_ID=421993993223
|
||||
APP_ID=1:421993993223:web:ec0baa8ee8c02ffa1fc6a2
|
||||
MEASUREMENT_ID=G-ERJ6025CEB
|
||||
|
||||
# Base URL
|
||||
BASE_URL=https://hoppscotch.io
|
||||
|
||||
49
.eslintrc.js
Normal file
49
.eslintrc.js
Normal file
@@ -0,0 +1,49 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
env: {
|
||||
browser: true,
|
||||
node: true,
|
||||
jest: true,
|
||||
},
|
||||
parserOptions: {
|
||||
sourceType: "module",
|
||||
requireConfigFile: false,
|
||||
},
|
||||
extends: [
|
||||
"@nuxtjs",
|
||||
"@nuxtjs/eslint-config-typescript",
|
||||
"prettier/prettier",
|
||||
"eslint:recommended",
|
||||
"plugin:vue/recommended",
|
||||
"plugin:prettier/recommended",
|
||||
"plugin:nuxt/recommended",
|
||||
],
|
||||
plugins: ["vue", "prettier"],
|
||||
// add your custom rules here
|
||||
rules: {
|
||||
semi: [2, "never"],
|
||||
"no-console": process.env.NODE_ENV === "production" ? "error" : "off",
|
||||
"no-debugger": process.env.NODE_ENV === "production" ? "error" : "off",
|
||||
"vue/max-attributes-per-line": "off",
|
||||
"vue/component-name-in-template-casing": ["error", "PascalCase"],
|
||||
"vue/html-self-closing": [
|
||||
"error",
|
||||
{
|
||||
html: {
|
||||
normal: "never",
|
||||
void: "always",
|
||||
},
|
||||
},
|
||||
],
|
||||
"vue/singleline-html-element-content-newline": "off",
|
||||
"vue/multiline-html-element-content-newline": "off",
|
||||
"vue/require-default-prop": "warn",
|
||||
"vue/require-prop-types": "warn",
|
||||
"prettier/prettier": ["warn", { semi: false }],
|
||||
"import/no-named-as-default": "off",
|
||||
"no-undef": "off",
|
||||
},
|
||||
globals: {
|
||||
$nuxt: true,
|
||||
},
|
||||
}
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -26,7 +26,7 @@ coverage
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/bin/sh
|
||||
. "$(dirname $0)/_/husky.sh"
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
npm run pretty-quick
|
||||
npm run pre-commit
|
||||
|
||||
@@ -4,4 +4,5 @@
|
||||
.hoppscotch
|
||||
.vscode
|
||||
package-lock.json
|
||||
node_modules
|
||||
node_modules
|
||||
dist
|
||||
3
.prettierrc.js
Normal file
3
.prettierrc.js
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
semi: false
|
||||
}
|
||||
24
.stylelintrc.js
Normal file
24
.stylelintrc.js
Normal file
@@ -0,0 +1,24 @@
|
||||
module.exports = {
|
||||
extends: ["stylelint-config-standard", "stylelint-config-prettier"],
|
||||
// add your custom config here
|
||||
// https://stylelint.io/user-guide/configuration
|
||||
rules: {
|
||||
"at-rule-no-unknown": [
|
||||
true,
|
||||
{
|
||||
ignoreAtRules: [
|
||||
"extends",
|
||||
"tailwind",
|
||||
"apply",
|
||||
"variants",
|
||||
"responsive",
|
||||
"screen",
|
||||
"mixin",
|
||||
"include",
|
||||
],
|
||||
},
|
||||
],
|
||||
"declaration-block-trailing-semicolon": null,
|
||||
"no-descending-specificity": null,
|
||||
},
|
||||
}
|
||||
@@ -1,5 +1,13 @@
|
||||
# Changelog
|
||||
|
||||
## [v1.12.0](https://github.com/hoppscotch/hoppscotch/tree/v1.12.0) (2020-05-27)
|
||||
|
||||
[Full Changelog](https://github.com/hoppscotch/hoppscotch/compare/v1.10.0...v1.12.0)
|
||||
|
||||
## [v1.10.0](https://github.com/hoppscotch/hoppscotch/tree/v1.10.0) (2020-04-10)
|
||||
|
||||
[Full Changelog](https://github.com/hoppscotch/hoppscotch/compare/v1.9.9...v1.10.0)
|
||||
|
||||
## [v1.9.9](https://github.com/liyasthomas/postwoman/tree/v1.9.9) (2020-07-30)
|
||||
|
||||
[Full Changelog](https://github.com/liyasthomas/postwoman/compare/v1.9.7...v1.9.9)
|
||||
|
||||
@@ -2,75 +2,131 @@
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
||||
level of experience, education, socio-economic status, nationality, personal
|
||||
appearance, race, religion, or sexual identity and orientation.
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, caste, color, religion, or sexual identity
|
||||
and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
- Demonstrating empathy and kindness toward other people
|
||||
- Being respectful of differing opinions, viewpoints, and experiences
|
||||
- Giving and gracefully accepting constructive feedback
|
||||
- Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
- Focusing on what is best not just for us as individuals, but for the
|
||||
overall community
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
- The use of sexualized language or imagery, and sexual attention or
|
||||
advances of any kind
|
||||
- Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
- Public or private harassment
|
||||
- Publishing others' private information, such as a physical or email
|
||||
address, without their explicit permission
|
||||
- Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior. Maintainers are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned with our Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. Examples of
|
||||
representing a project or community include using an official project e-mail
|
||||
address, posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event. Representation of a project may be
|
||||
further defined and clarified by project maintainers.
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at liyascthomas@gmail.com. All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
reported to the community leaders responsible for enforcement at
|
||||
[INSERT CONTACT METHOD].
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series
|
||||
of actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or
|
||||
permanent ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within
|
||||
the community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.0, available at
|
||||
[https://www.contributor-covenant.org/version/2/0/code_of_conduct.html][v2.0].
|
||||
|
||||
Community Impact Guidelines were inspired by
|
||||
[Mozilla's code of conduct enforcement ladder][mozilla coc].
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
[https://www.contributor-covenant.org/faq][faq]. Translations are available
|
||||
at [https://www.contributor-covenant.org/translations][translations].
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see
|
||||
https://www.contributor-covenant.org/faq
|
||||
[v2.0]: https://www.contributor-covenant.org/version/2/0/code_of_conduct.html
|
||||
[mozilla coc]: https://github.com/mozilla/diversity
|
||||
[faq]: https://www.contributor-covenant.org/faq
|
||||
[translations]: https://www.contributor-covenant.org/translations
|
||||
|
||||
@@ -12,81 +12,6 @@ Please note we have a code of conduct, please follow it in all your interactions
|
||||
2. Update the README.md with details of changes to the interface, this includes new environment
|
||||
variables, exposed ports, useful file locations and container parameters.
|
||||
3. Increase the version numbers in any examples files and the README.md to the new version that this
|
||||
Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/).
|
||||
Pull Request would represent. The versioning scheme we use is [SemVer](https://semver.org).
|
||||
4. You may merge the Pull Request once you have the sign-off of two other developers, or if you
|
||||
do not have permission to do that, you may request the second reviewer merge it for you.
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
### Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, gender identity and expression, level of experience,
|
||||
nationality, personal appearance, race, religion, or sexual identity and
|
||||
orientation.
|
||||
|
||||
### Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
### Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior. Maintainers are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned with our Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
### Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. Examples of
|
||||
representing a project or community include using an official project e-mail
|
||||
address, posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event. Representation of a project may be
|
||||
further defined and clarified by project maintainers.
|
||||
|
||||
### Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at liyascthomas@gmail.com. All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
### Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at [http://contributor-covenant.org/version/1/4][version]
|
||||
|
||||
[homepage]: http://contributor-covenant.org
|
||||
[version]: http://contributor-covenant.org/version/1/4/
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
FROM node:12.10.0-alpine
|
||||
|
||||
LABEL maintainer="Liyas Thomas (liyascthomas@gmail.com)"
|
||||
LABEL maintainer="Hoppscotch (support@hoppscotch.io)"
|
||||
|
||||
# Add git as the prebuild target requires it to parse version information
|
||||
RUN apk add --update --no-cache \
|
||||
@@ -17,9 +17,9 @@ ADD . /app/
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN npm run generate
|
||||
|
||||
ENV HOST 0.0.0.0
|
||||
EXPOSE 3000
|
||||
|
||||
RUN mv .env.example .env
|
||||
|
||||
CMD ["npm", "run", "dev"]
|
||||
|
||||
109
GITHUB_EXTERNSHIP.md
Normal file
109
GITHUB_EXTERNSHIP.md
Normal file
@@ -0,0 +1,109 @@
|
||||
# **GitHub Externship @ Hoppscotch**
|
||||
|
||||
**We ❤️ open source**
|
||||
|
||||
We are super excited to be a ‘Partner Organization’ of GitHub Externship 2021 and look forward to supporting all interested students to participate and contribute to our [open source projects](https://github.com/hoppscotch).
|
||||
|
||||
This document covers the process, expectations and guidelines for any interested student so that we can guide, mentor in the best possible manner and most importantly review your community engagement which is a critical factor along with your project idea to be selected from our organization.
|
||||
|
||||
**This post will be updated from time to time. So please keep an eye on it.**
|
||||
|
||||
Before, any of you jump in and start contributing, here are a few documents from the GitHub Externship Team which we advise every student to read through carefully and thoroughly:
|
||||
|
||||
- [GitHub Externship: Website](https://github-externships.github.io/externship/index.html)
|
||||
- [GitHub Externship: Program Structure](https://github-externships.github.io/externship/structure.html)
|
||||
|
||||
Our awesome community members frequently request for features on our [Discord server](https://hoppscotch.io/discord), [Telegram group](https://hoppscotch.io/externship_telegram), [GitHub issues tab](https://github.com/hoppscotch/hoppscotch/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc) and we have a never ending list of awesome ideas. However, keeping in mind the 3 months time span, we have filtered through our stockpile and come up with the following ideas for [GitHub Externship](https://github-externships.github.io/externship/index.html) program.
|
||||
|
||||
## **Ideas**
|
||||
|
||||
If you’re a student interested in working with Hoppscotch during GitHub Externship, we’ve assembled for you a list of 20 ideas under the following three categories:
|
||||
|
||||
### **1. Implement existing feature requests:**
|
||||
|
||||
- [gRPC-Web support](https://github.com/hoppscotch/hoppscotch/issues/402)
|
||||
|
||||
Like swagger, let users view message formats from `.proto` / `URL` and test the server methods by sending any messages from the Hoppscotch.
|
||||
|
||||
[**`Learn more →`**](https://github.com/hoppscotch/hoppscotch/issues/402)
|
||||
|
||||
- [Swagger / OpenAPI support](https://github.com/hoppscotch/hoppscotch/issues/470)
|
||||
|
||||
Like [Swagger Inspector](https://inspector.swagger.io/builder) / [Swagger Editor](https://editor.swagger.io). Let users import `swagger.json` and test the server methods by sending any messages from the Hoppscotch.
|
||||
|
||||
[**`Learn more →`**](https://github.com/hoppscotch/hoppscotch/issues/470)
|
||||
|
||||
- [Mock Server](https://github.com/hoppscotch/hoppscotch/issues/1598)
|
||||
|
||||
Mock server allows users to create mock endpoints that accepts request from the system-under-test or from a `json` file.
|
||||
|
||||
[**`Learn more →`**](https://github.com/hoppscotch/hoppscotch/issues/1598)
|
||||
|
||||
### **2. Implement new features:**
|
||||
|
||||
If you'd like to implement a totally new feature to help developers make API testing easy, feel free to come up with your own feature request or a port of feature from other API testing tools.
|
||||
|
||||
[**Create new feature request →**](https://github.com/hoppscotch/hoppscotch/issues/new?assignees=&labels=&template=feature_request.md&title=Feature%20[GitHub%20Externship]:)
|
||||
|
||||
### **3. Refactor existing features:**
|
||||
|
||||
If you'd like to refactor an already implemented feature / workflow - feel free to open a PR to do that. We appreciate using industry best pratices and latest technologies on all of our open source projects.
|
||||
|
||||
## **Built with**
|
||||
|
||||
- [HTML](https://developer.mozilla.org/en-US/docs/Web/HTML)
|
||||
- [CSS](https://developer.mozilla.org/en-US/docs/Web/CSS), [SCSS](https://sass-lang.com), [Tailwind CSS](https://tailwindcss.com)
|
||||
- [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript)
|
||||
- [TypeScript](https://www.typescriptlang.org)
|
||||
- [Vue](https://vuejs.org)
|
||||
- [Nuxt](https://nuxtjs.org)
|
||||
|
||||
## **Contributing**
|
||||
|
||||
Please contribute using [GitHub Flow](https://guides.github.com/introduction/flow). Create a branch, add commits, and [open a pull request](https://github.com/hoppscotch/hoppscotch/compare).
|
||||
|
||||
Please read [`CONTRIBUTING`](CONTRIBUTING.md) for details on our [`CODE OF CONDUCT`](CODE_OF_CONDUCT.md), and the process for submitting pull requests to us.
|
||||
|
||||
## **Quick links**
|
||||
|
||||
[**`Repository →`**](https://github.com/hoppscotch/hoppscotch)
|
||||
|
||||
[**`Web app →`**](https://hoppscotch.io)
|
||||
|
||||
[**`Discord server →`**](https://hoppscotch.io/discord)
|
||||
|
||||
[**`Telegram group →`**](https://hoppscotch.io/externship_telegram)
|
||||
|
||||
## **Discussions channel**
|
||||
|
||||
We use [GitHub Discussions](https://github.com/hoppscotch/hoppscotch/discussions) as the primary channel for anything and everything related to GitHub Externship, ranging from
|
||||
|
||||
- Queries on issues you’re working or thinking about to take up.
|
||||
- Mentor advice on your project ideas [Google Drive document once there is enough clarity for easier collaboration and feedback – check project proposal ideas above]
|
||||
- Helping out your friends to onboard and contribute
|
||||
- Finally even selection updates etc.
|
||||
|
||||
Before asking a question please check this [How do I ask a good question?](https://stackoverflow.com/help/how-to-ask)
|
||||
|
||||
## **Need help?**
|
||||
|
||||
Start a discussion [here](https://github.com/hoppscotch/hoppscotch/discussions) if you’re unable to get a response from the [Telegram](https://hoppscotch.io/externship_telegram), [Discord](https://hoppscotch.io/discord) communities.
|
||||
|
||||
Start a new idea under [discussions](https://github.com/hoppscotch/hoppscotch/discussions) only once you’ve finalized your project proposal.
|
||||
|
||||
Send us an email at support@hoppscotch.io for any administrative or operational concerns regarding the program.
|
||||
|
||||
## **Submit your proposal on time**
|
||||
|
||||
You can find the complete GitHub Externship program timeline [here](https://github-externships.github.io/externship/structure.html). Students can begin registering and submitting project proposals to mentor organizations—including Hoppscotch—on April 22, 2021.
|
||||
|
||||
All proposals must be submitted by May 21, 2021.
|
||||
|
||||
We look forward to seeing your proposals for a happy summer of coding.
|
||||
|
||||
## **Mentors**
|
||||
|
||||
- [Liyas Thomas](https://github.com/liyasthomas)
|
||||
- [Andrew Bastin](https://github.com/andrewbastin)
|
||||
|
||||
If you have any questions or queries please contact us via [Telegram](https://hoppscotch.io/externship_telegram), [Discord](https://hoppscotch.io/discord) or send an email to us at support@hoppscotch.io.
|
||||
65
README.md
65
README.md
@@ -6,11 +6,11 @@
|
||||
<b>Hoppscotch - Open source API development ecosystem</b>
|
||||
</p>
|
||||
<p>
|
||||
<i>Helps you create requests faster, saving precious time on development - <a href="http://eepurl.com/g6n_P5">Subscribe</a></i>
|
||||
<i>Helps you create requests faster, saving precious time on development - <a href="https://eepurl.com/g6n_P5">Subscribe</a></i>
|
||||
</p>
|
||||
<p>
|
||||
|
||||
[](CODE_OF_CONDUCT.md) [](https://hoppscotch.io) [](https://travis-ci.com/hoppscotch/hoppscotch) [](https://twitter.com/intent/tweet?url=https%3A%2F%2Fhoppscotch.io&text=%F0%9F%91%BD%20Hoppscotch%20%E2%80%A2%20API%20request%20builder%20-%20Helps%20you%20create%20requests%20faster%2C%20saving%20precious%20time%20on%20development&original_referer=https%3A%2F%2Ftwitter.com%2Fshare%3Ftext%3D%25F0%259F%2591%25BD%2520Hoppscotch%2520%25E2%2580%25A2%2520API%2520request%2520builder%2520-%2520Helps%2520you%2520create%2520requests%2520faster%2C%2520saving%2520precious%2520time%2520on%2520development%26url%3Dhttps%3A%2F%2Fhoppscotch.io%26hashtags%3Dhoppscotch%26via%3Dliyasthomas&via=liyasthomas&hashtags=hoppscotch)
|
||||
[](CODE_OF_CONDUCT.md) [](https://hoppscotch.io) [](https://travis-ci.com/hoppscotch/hoppscotch) [](https://twitter.com/share?text=%F0%9F%91%BD%20Hoppscotch%20%E2%80%A2%20Open%20source%20API%20development%20ecosystem%20-%20Helps%20you%20create%20requests%20faster,%20saving%20precious%20time%20on%20development.&url=https://hoppscotch.io&hashtags=hoppscotch&via=hoppscotch_io)
|
||||
|
||||
</p>
|
||||
<p>
|
||||
@@ -45,7 +45,7 @@
|
||||
|
||||
#### **Contact**
|
||||
|
||||
[](https://t.me/hoppscotch) [](https://discord.gg/GAMWxmR)
|
||||
[](https://hoppscotch.io/telegram) [](https://hoppscotch.io/discord)
|
||||
|
||||
#### **Support**
|
||||
|
||||
@@ -410,6 +410,21 @@ _Notes are only available for signed-in users_
|
||||
|
||||
</details>
|
||||
|
||||
👨👩👧👦 **Teams β:** Helps you collaborate across your team to design, develop, and test APIs faster.
|
||||
|
||||
<details>
|
||||
<summary><i>Features</i></summary>
|
||||
|
||||
---
|
||||
|
||||
- Unlimited team collections and shared requests
|
||||
- Unlimited team members
|
||||
- User roles
|
||||
|
||||
---
|
||||
|
||||
</details>
|
||||
|
||||
**To find out more, please check out [Hoppscotch Wiki](https://github.com/hoppscotch/hoppscotch/wiki).**
|
||||
|
||||
## **Demo**
|
||||
@@ -428,6 +443,7 @@ _Notes are only available for signed-in users_
|
||||
- [HTML](https://developer.mozilla.org/en-US/docs/Web/HTML)
|
||||
- [CSS](https://developer.mozilla.org/en-US/docs/Web/CSS), [SCSS](https://sass-lang.com), [Tailwind CSS](https://tailwindcss.com)
|
||||
- [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript)
|
||||
- [TypeScript](https://www.typescriptlang.org)
|
||||
- [Vue](https://vuejs.org)
|
||||
- [Nuxt](https://nuxtjs.org)
|
||||
|
||||
@@ -548,6 +564,30 @@ Become a financial contributor and help us sustain our community [[Support](#sup
|
||||
#### GitHub Sponsors
|
||||
|
||||
<p align="center">
|
||||
<a href="https://simplescraper.io/?utm_source=hs" target="_blank" rel="noopener">
|
||||
<img
|
||||
width="100"
|
||||
src="https://raw.githubusercontent.com/hoppscotch/hoppscotch/main/assets/images/Simplescraper.png"
|
||||
alt="Simplescraper"
|
||||
/>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/anmolm96" target="_blank" rel="noopener">
|
||||
<img
|
||||
width="64"
|
||||
src="https://github.com/anmolm96.png?size=64"
|
||||
alt="Anmol Maini"
|
||||
/>
|
||||
</a>
|
||||
<a href="https://github.com/juzhiyuan" target="_blank" rel="noopener">
|
||||
<img
|
||||
width="64"
|
||||
src="https://github.com/juzhiyuan.png?size=64"
|
||||
alt="琚致远"
|
||||
/>
|
||||
</a>
|
||||
<a href="https://github.com/eldadfux" target="_blank" rel="noopener">
|
||||
<img
|
||||
width="64"
|
||||
@@ -597,13 +637,6 @@ Become a financial contributor and help us sustain our community [[Support](#sup
|
||||
alt="Erica Brescia"
|
||||
/>
|
||||
</a>
|
||||
<a href="http://tom.preston-werner.com" target="_blank" rel="noopener">
|
||||
<img
|
||||
width="64"
|
||||
src="https://github.com/mojombo.png?size=64"
|
||||
alt="Tom Preston-Werner"
|
||||
/>
|
||||
</a>
|
||||
<a href="https://github.com/mlynch" target="_blank" rel="noopener">
|
||||
<img
|
||||
width="64"
|
||||
@@ -637,6 +670,13 @@ Become a financial contributor and help us sustain our community [[Support](#sup
|
||||
#### Open Collective
|
||||
|
||||
<p align="center">
|
||||
<a href="https://pipedream.com/?ref=hoppscotch" target="_blank" rel="noopener">
|
||||
<img
|
||||
width="100"
|
||||
src="https://raw.githubusercontent.com/hoppscotch/hoppscotch/main/assets/images/pipedream.png"
|
||||
alt="pipedream"
|
||||
/>
|
||||
</a>
|
||||
<a href="https://paw.cloud/?utm_source=hoppscotch&utm_medium=github&utm_campaign=hoppscotch-sponsorship" target="_blank" rel="noopener">
|
||||
<img
|
||||
width="100"
|
||||
@@ -646,11 +686,6 @@ Become a financial contributor and help us sustain our community [[Support](#sup
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://opencollective.com/hoppscotch/organization/0/website"><img src="https://opencollective.com/hoppscotch/organization/0/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/hoppscotch/organization/1/website"><img src="https://opencollective.com/hoppscotch/organization/1/avatar.svg"></a>
|
||||
</p>
|
||||
|
||||
### Code Contributors
|
||||
|
||||
This project exists thanks to all the people who contribute [[Contribute](CONTRIBUTING.md)].
|
||||
|
||||
@@ -1,203 +1,249 @@
|
||||
/* fallback */
|
||||
@font-face {
|
||||
font-family: 'Material Icons';
|
||||
font-family: "Material Icons";
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url('~assets/fonts/Material_Icons-400-fallback1.woff2') format('woff2');
|
||||
src: url("~assets/fonts/Material_Icons-400-fallback1.woff2") format("woff2");
|
||||
}
|
||||
|
||||
/* devanagari */
|
||||
@font-face {
|
||||
font-family: 'Poppins';
|
||||
font-family: "Poppins";
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url('~assets/fonts/Poppins-400-devanagari2.woff2') format('woff2');
|
||||
unicode-range: U+0900-097F, U+1CD0-1CF6, U+1CF8-1CF9, U+200C-200D, U+20A8, U+20B9, U+25CC, U+A830-A839, U+A8E0-A8FB;
|
||||
src: url("~assets/fonts/Poppins-400-devanagari2.woff2") format("woff2");
|
||||
unicode-range: U+0900-097F, U+1CD0-1CF6, U+1CF8-1CF9, U+200C-200D, U+20A8,
|
||||
U+20B9, U+25CC, U+A830-A839, U+A8E0-A8FB;
|
||||
}
|
||||
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Poppins';
|
||||
font-family: "Poppins";
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url('~assets/fonts/Poppins-400-latin-ext3.woff2') format('woff2');
|
||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
src: url("~assets/fonts/Poppins-400-latin-ext3.woff2") format("woff2");
|
||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB,
|
||||
U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Poppins';
|
||||
font-family: "Poppins";
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url('~assets/fonts/Poppins-400-latin4.woff2') format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
src: url("~assets/fonts/Poppins-400-latin4.woff2") format("woff2");
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
|
||||
U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
|
||||
U+FEFF, U+FFFD;
|
||||
}
|
||||
|
||||
/* devanagari */
|
||||
@font-face {
|
||||
font-family: 'Poppins';
|
||||
font-family: "Poppins";
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src: url('~assets/fonts/Poppins-500-devanagari5.woff2') format('woff2');
|
||||
unicode-range: U+0900-097F, U+1CD0-1CF6, U+1CF8-1CF9, U+200C-200D, U+20A8, U+20B9, U+25CC, U+A830-A839, U+A8E0-A8FB;
|
||||
src: url("~assets/fonts/Poppins-500-devanagari5.woff2") format("woff2");
|
||||
unicode-range: U+0900-097F, U+1CD0-1CF6, U+1CF8-1CF9, U+200C-200D, U+20A8,
|
||||
U+20B9, U+25CC, U+A830-A839, U+A8E0-A8FB;
|
||||
}
|
||||
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Poppins';
|
||||
font-family: "Poppins";
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src: url('~assets/fonts/Poppins-500-latin-ext6.woff2') format('woff2');
|
||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
src: url("~assets/fonts/Poppins-500-latin-ext6.woff2") format("woff2");
|
||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB,
|
||||
U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Poppins';
|
||||
font-family: "Poppins";
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src: url('~assets/fonts/Poppins-500-latin7.woff2') format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
src: url("~assets/fonts/Poppins-500-latin7.woff2") format("woff2");
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
|
||||
U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
|
||||
U+FEFF, U+FFFD;
|
||||
}
|
||||
|
||||
/* devanagari */
|
||||
@font-face {
|
||||
font-family: 'Poppins';
|
||||
font-family: "Poppins";
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-display: swap;
|
||||
src: url('~assets/fonts/Poppins-600-devanagari8.woff2') format('woff2');
|
||||
unicode-range: U+0900-097F, U+1CD0-1CF6, U+1CF8-1CF9, U+200C-200D, U+20A8, U+20B9, U+25CC, U+A830-A839, U+A8E0-A8FB;
|
||||
src: url("~assets/fonts/Poppins-600-devanagari8.woff2") format("woff2");
|
||||
unicode-range: U+0900-097F, U+1CD0-1CF6, U+1CF8-1CF9, U+200C-200D, U+20A8,
|
||||
U+20B9, U+25CC, U+A830-A839, U+A8E0-A8FB;
|
||||
}
|
||||
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Poppins';
|
||||
font-family: "Poppins";
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-display: swap;
|
||||
src: url('~assets/fonts/Poppins-600-latin-ext9.woff2') format('woff2');
|
||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
src: url("~assets/fonts/Poppins-600-latin-ext9.woff2") format("woff2");
|
||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB,
|
||||
U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Poppins';
|
||||
font-family: "Poppins";
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-display: swap;
|
||||
src: url('~assets/fonts/Poppins-600-latin10.woff2') format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
src: url("~assets/fonts/Poppins-600-latin10.woff2") format("woff2");
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
|
||||
U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
|
||||
U+FEFF, U+FFFD;
|
||||
}
|
||||
|
||||
/* devanagari */
|
||||
@font-face {
|
||||
font-family: 'Poppins';
|
||||
font-family: "Poppins";
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: url('~assets/fonts/Poppins-700-devanagari11.woff2') format('woff2');
|
||||
unicode-range: U+0900-097F, U+1CD0-1CF6, U+1CF8-1CF9, U+200C-200D, U+20A8, U+20B9, U+25CC, U+A830-A839, U+A8E0-A8FB;
|
||||
src: url("~assets/fonts/Poppins-700-devanagari11.woff2") format("woff2");
|
||||
unicode-range: U+0900-097F, U+1CD0-1CF6, U+1CF8-1CF9, U+200C-200D, U+20A8,
|
||||
U+20B9, U+25CC, U+A830-A839, U+A8E0-A8FB;
|
||||
}
|
||||
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Poppins';
|
||||
font-family: "Poppins";
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: url('~assets/fonts/Poppins-700-latin-ext12.woff2') format('woff2');
|
||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
src: url("~assets/fonts/Poppins-700-latin-ext12.woff2") format("woff2");
|
||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB,
|
||||
U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Poppins';
|
||||
font-family: "Poppins";
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: url('~assets/fonts/Poppins-700-latin13.woff2') format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
src: url("~assets/fonts/Poppins-700-latin13.woff2") format("woff2");
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
|
||||
U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
|
||||
U+FEFF, U+FFFD;
|
||||
}
|
||||
|
||||
/* devanagari */
|
||||
@font-face {
|
||||
font-family: 'Poppins';
|
||||
font-family: "Poppins";
|
||||
font-style: normal;
|
||||
font-weight: 800;
|
||||
font-display: swap;
|
||||
src: url('~assets/fonts/Poppins-800-devanagari14.woff2') format('woff2');
|
||||
unicode-range: U+0900-097F, U+1CD0-1CF6, U+1CF8-1CF9, U+200C-200D, U+20A8, U+20B9, U+25CC, U+A830-A839, U+A8E0-A8FB;
|
||||
src: url("~assets/fonts/Poppins-800-devanagari14.woff2") format("woff2");
|
||||
unicode-range: U+0900-097F, U+1CD0-1CF6, U+1CF8-1CF9, U+200C-200D, U+20A8,
|
||||
U+20B9, U+25CC, U+A830-A839, U+A8E0-A8FB;
|
||||
}
|
||||
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Poppins';
|
||||
font-family: "Poppins";
|
||||
font-style: normal;
|
||||
font-weight: 800;
|
||||
font-display: swap;
|
||||
src: url('~assets/fonts/Poppins-800-latin-ext15.woff2') format('woff2');
|
||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
src: url("~assets/fonts/Poppins-800-latin-ext15.woff2") format("woff2");
|
||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB,
|
||||
U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Poppins';
|
||||
font-family: "Poppins";
|
||||
font-style: normal;
|
||||
font-weight: 800;
|
||||
font-display: swap;
|
||||
src: url('~assets/fonts/Poppins-800-latin16.woff2') format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
src: url("~assets/fonts/Poppins-800-latin16.woff2") format("woff2");
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
|
||||
U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
|
||||
U+FEFF, U+FFFD;
|
||||
}
|
||||
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto Mono';
|
||||
font-family: "Roboto Mono";
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url('~assets/fonts/Roboto_Mono-400-cyrillic-ext17.woff2') format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
src: url("~assets/fonts/Roboto_Mono-400-cyrillic-ext17.woff2") format("woff2");
|
||||
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F,
|
||||
U+FE2E-FE2F;
|
||||
}
|
||||
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Roboto Mono';
|
||||
font-family: "Roboto Mono";
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url('~assets/fonts/Roboto_Mono-400-cyrillic18.woff2') format('woff2');
|
||||
src: url("~assets/fonts/Roboto_Mono-400-cyrillic18.woff2") format("woff2");
|
||||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: 'Roboto Mono';
|
||||
font-family: "Roboto Mono";
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url('~assets/fonts/Roboto_Mono-400-greek19.woff2') format('woff2');
|
||||
src: url("~assets/fonts/Roboto_Mono-400-greek19.woff2") format("woff2");
|
||||
unicode-range: U+0370-03FF;
|
||||
}
|
||||
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Roboto Mono';
|
||||
font-family: "Roboto Mono";
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url('~assets/fonts/Roboto_Mono-400-vietnamese20.woff2') format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
|
||||
src: url("~assets/fonts/Roboto_Mono-400-vietnamese20.woff2") format("woff2");
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1,
|
||||
U+01AF-01B0, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto Mono';
|
||||
font-family: "Roboto Mono";
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url('~assets/fonts/Roboto_Mono-400-latin-ext21.woff2') format('woff2');
|
||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
src: url("~assets/fonts/Roboto_Mono-400-latin-ext21.woff2") format("woff2");
|
||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB,
|
||||
U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Roboto Mono';
|
||||
font-family: "Roboto Mono";
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url('~assets/fonts/Roboto_Mono-400-latin22.woff2') format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
src: url("~assets/fonts/Roboto_Mono-400-latin22.woff2") format("woff2");
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
|
||||
U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
|
||||
U+FEFF, U+FFFD;
|
||||
}
|
||||
|
||||
.material-icons {
|
||||
font-family: 'Material Icons';
|
||||
font-family: "Material Icons"; /* stylelint-disable-line font-family-no-missing-generic-family-keyword */
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-size: 24px;
|
||||
@@ -208,6 +254,6 @@
|
||||
white-space: nowrap;
|
||||
word-wrap: normal;
|
||||
direction: ltr;
|
||||
-webkit-font-feature-settings: 'liga';
|
||||
-webkit-font-feature-settings: "liga";
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/* purgecss start ignore */
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
|
||||
/* purgecss end ignore */
|
||||
@tailwind utilities;
|
||||
|
||||
BIN
assets/images/Simplescraper.png
Normal file
BIN
assets/images/Simplescraper.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.9 KiB |
BIN
assets/images/Simplescraper_dark.png
Normal file
BIN
assets/images/Simplescraper_dark.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.9 KiB |
BIN
assets/images/pipedream.png
Normal file
BIN
assets/images/pipedream.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.3 KiB |
@@ -17,7 +17,10 @@ const regex = { ws, sse, socketio }
|
||||
// type = ws/sse/socketio
|
||||
async function validator(type, url) {
|
||||
console.time(`validator ${url}`)
|
||||
const [res1, res2] = await Promise.all([regex[type][0].test(url), regex[type][1].test(url)])
|
||||
const [res1, res2] = await Promise.all([
|
||||
regex[type][0].test(url),
|
||||
regex[type][1].test(url),
|
||||
])
|
||||
console.timeEnd(`validator ${url}`)
|
||||
return res1 || res2
|
||||
}
|
||||
|
||||
@@ -1,218 +1,12 @@
|
||||
**Table of Contents**
|
||||
|
||||
{{#collections}}
|
||||
{{>folderContents}}
|
||||
{{/collections}}
|
||||
|
||||
{{#collections}}
|
||||
|
||||
# {{name}}
|
||||
|
||||
## {{#folders}}
|
||||
|
||||
## Folder: {{name}}
|
||||
|
||||
{{#requests}}
|
||||
|
||||
### {{name}}
|
||||
|
||||
**Method**: {{method}}
|
||||
|
||||
**Request URL**: `{{{url}}}{{{path}}}`
|
||||
|
||||
{{#isHeaders}}
|
||||
**Headers**:
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Key</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
{{#headers}}
|
||||
<tr>
|
||||
<td>{{{key}}}</td>
|
||||
<td>`{{{value}}}`</td>
|
||||
</tr>
|
||||
{{/headers}}
|
||||
</table>
|
||||
{{/isHeaders}}
|
||||
|
||||
{{#isParams}}
|
||||
**Parameters**:
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>type</th>
|
||||
<th>Key</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
{{#params}}
|
||||
<tr>
|
||||
<td>{{type}}</td>
|
||||
<td>{{{key}}}</td>
|
||||
<td>{{{value}}}</td>
|
||||
</tr>
|
||||
{{/params}}
|
||||
</table>
|
||||
{{/isParams}}
|
||||
|
||||
{{#isAuth}}
|
||||
**Authentication Type**: {{{auth}}}
|
||||
{{/isAuth}}
|
||||
|
||||
{{#bearerToken}}
|
||||
**Bearer Token**: `{{{.}}}`
|
||||
{{/bearerToken}}
|
||||
|
||||
{{#isAuthBasic}}
|
||||
Username: `{{{httpUser}}}`
|
||||
Password: `{{{httpPassword}}}`
|
||||
{{/isAuthBasic}}
|
||||
|
||||
{{#isRawParams}}
|
||||
**RawParams**:
|
||||
|
||||
```json
|
||||
{{{rawParams}}}
|
||||
```
|
||||
|
||||
{{/isRawParams}}
|
||||
|
||||
{{#contentType}}
|
||||
**ContentType**: `{{{contentType}}}`
|
||||
{{/contentType}}
|
||||
|
||||
{{#preRequestScript}}
|
||||
**Pre Request Script**:
|
||||
|
||||
```js
|
||||
{
|
||||
{
|
||||
{
|
||||
preRequestScript
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
{{/preRequestScript}}
|
||||
|
||||
{{#testScript}}
|
||||
**Test Script**:
|
||||
|
||||
```js
|
||||
{
|
||||
{
|
||||
{
|
||||
testScript
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
{{/testScript}}
|
||||
|
||||
{{/requests}}
|
||||
|
||||
---
|
||||
|
||||
{{/folders}}
|
||||
|
||||
{{#requests}}
|
||||
|
||||
## {{name}}
|
||||
|
||||
**Method**: {{method}}
|
||||
|
||||
**Request URL**: `{{{url}}}{{{path}}}`
|
||||
|
||||
{{#isHeaders}}
|
||||
**Headers**:
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Key</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
{{#headers}}
|
||||
<tr>
|
||||
<td>{{{key}}}</td>
|
||||
<td>`{{{value}}}`</td>
|
||||
</tr>
|
||||
{{/headers}}
|
||||
</table>
|
||||
{{/isHeaders}}
|
||||
|
||||
{{#isParams}}
|
||||
**Parameters**:
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>type</th>
|
||||
<th>Key</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
{{#params}}
|
||||
<tr>
|
||||
<td>{{type}}</td>
|
||||
<td>{{{key}}}</td>
|
||||
<td>{{{value}}}</td>
|
||||
</tr>
|
||||
{{/params}}
|
||||
</table>
|
||||
{{/isParams}}
|
||||
|
||||
{{#isAuth}}
|
||||
**Authentication Type**: {{{auth}}}
|
||||
{{/isAuth}}
|
||||
|
||||
{{#bearerToken}}
|
||||
**Bearer Token**: `{{{.}}}`
|
||||
{{/bearerToken}}
|
||||
|
||||
{{#isAuthBasic}}
|
||||
Username: `{{{httpUser}}}`
|
||||
Password: `{{{httpPassword}}}`
|
||||
{{/isAuthBasic}}
|
||||
|
||||
{{#isRawParams}}
|
||||
**Raw Parameters**:
|
||||
|
||||
```json
|
||||
{{{rawParams}}}
|
||||
```
|
||||
|
||||
{{/isRawParams}}
|
||||
|
||||
{{#contentType}}
|
||||
**Content Type**: `{{{contentType}}}`
|
||||
{{/contentType}}
|
||||
|
||||
{{#preRequestScript}}
|
||||
**Pre Request Script**:
|
||||
|
||||
```js
|
||||
{
|
||||
{
|
||||
{
|
||||
preRequestScript
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
{{/preRequestScript}}
|
||||
|
||||
{{#testScript}}
|
||||
**Test Script**:
|
||||
|
||||
```js
|
||||
{
|
||||
{
|
||||
{
|
||||
testScript
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
{{/testScript}}
|
||||
|
||||
{{/requests}}
|
||||
{{>folderBody}}
|
||||
|
||||
{{/collections}}
|
||||
|
||||
|
||||
113
assets/md/folderBody.md
Normal file
113
assets/md/folderBody.md
Normal file
@@ -0,0 +1,113 @@
|
||||
{{nestingLevel}} {{name}}
|
||||
|
||||
{{#requests}}
|
||||
|
||||
{{nestingLevel}} Request: {{name}}
|
||||
|
||||
**Method**: {{method}}
|
||||
|
||||
**Request URL**:
|
||||
|
||||
```
|
||||
{{{url}}}{{{path}}}
|
||||
```
|
||||
|
||||
{{#isHeaders}}
|
||||
**Headers**:
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Key</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
{{#headers}}
|
||||
<tr>
|
||||
<td>{{{key}}}</td>
|
||||
<td>`{{{value}}}`</td>
|
||||
</tr>
|
||||
{{/headers}}
|
||||
</table>
|
||||
{{/isHeaders}}
|
||||
|
||||
{{#isParams}}
|
||||
**Parameters**:
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>type</th>
|
||||
<th>Key</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
{{#params}}
|
||||
<tr>
|
||||
<td>{{type}}</td>
|
||||
<td>{{{key}}}</td>
|
||||
<td>{{{value}}}</td>
|
||||
</tr>
|
||||
{{/params}}
|
||||
</table>
|
||||
{{/isParams}}
|
||||
|
||||
{{#isAuth}}
|
||||
**Authentication Type**: {{{auth}}}
|
||||
{{/isAuth}}
|
||||
|
||||
{{#bearerToken}}
|
||||
**Bearer Token**: `{{{.}}}`
|
||||
{{/bearerToken}}
|
||||
|
||||
{{#isAuthBasic}}
|
||||
Username: `{{{httpUser}}}`
|
||||
Password: `{{{httpPassword}}}`
|
||||
{{/isAuthBasic}}
|
||||
|
||||
{{#isRawParams}}
|
||||
**Raw Parameters**:
|
||||
|
||||
```json
|
||||
{{{rawParams}}}
|
||||
```
|
||||
|
||||
{{/isRawParams}}
|
||||
|
||||
{{#contentType}}
|
||||
**Content Type**: `{{{contentType}}}`
|
||||
{{/contentType}}
|
||||
|
||||
{{#isPreRequestScript}}
|
||||
**Pre Request Script**:
|
||||
|
||||
```js
|
||||
{
|
||||
{
|
||||
{
|
||||
preRequestScript
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
{{/isPreRequestScript}}
|
||||
|
||||
{{#isTestScript}}
|
||||
**Test Script**:
|
||||
|
||||
```js
|
||||
{
|
||||
{
|
||||
{
|
||||
testScript
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
{{/isTestScript}}
|
||||
|
||||
{{/requests}}
|
||||
|
||||
{{#folders}}
|
||||
|
||||
{{> folderBody }}
|
||||
|
||||
{{/folders}}
|
||||
4
assets/md/folderContents.md
Normal file
4
assets/md/folderContents.md
Normal file
@@ -0,0 +1,4 @@
|
||||
{{{id}}} <a href="#{{ref}}"> {{name}} </a> <br>
|
||||
{{#folders}}
|
||||
{{> folderContents}}
|
||||
{{/folders}}
|
||||
@@ -5,7 +5,11 @@ function isBabelLoader(caller) {
|
||||
module.exports = function (api) {
|
||||
if (api.env("test") && !api.caller(isBabelLoader)) {
|
||||
return {
|
||||
plugins: ["@babel/plugin-proposal-class-properties"],
|
||||
plugins: [
|
||||
"@babel/plugin-proposal-class-properties",
|
||||
"@babel/plugin-proposal-nullish-coalescing-operator",
|
||||
"@babel/plugin-proposal-optional-chaining",
|
||||
],
|
||||
presets: [
|
||||
[
|
||||
"@babel/preset-env",
|
||||
|
||||
@@ -9,10 +9,10 @@
|
||||
<div class="px-2 row-wrapper">
|
||||
<span>
|
||||
<a
|
||||
v-tooltip.right="$t('recurring')"
|
||||
href="https://github.com/sponsors/hoppscotch"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
v-tooltip.right="$t('recurring')"
|
||||
>
|
||||
<button class="icon">
|
||||
<i class="material-icons">
|
||||
@@ -36,10 +36,10 @@
|
||||
<div class="px-2 row-wrapper">
|
||||
<span>
|
||||
<a
|
||||
v-tooltip.right="$t('one_time_recurring')"
|
||||
href="https://opencollective.com/hoppscotch"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
v-tooltip.right="$t('one_time_recurring')"
|
||||
>
|
||||
<button class="icon">
|
||||
<i class="material-icons">donut_large</i>
|
||||
@@ -51,10 +51,10 @@
|
||||
<div class="px-2 row-wrapper">
|
||||
<span>
|
||||
<a
|
||||
v-tooltip.right="$t('recurring')"
|
||||
href="https://www.patreon.com/liyasthomas"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
v-tooltip.right="$t('recurring')"
|
||||
>
|
||||
<button class="icon">
|
||||
<i class="material-icons">local_parking</i>
|
||||
@@ -66,10 +66,10 @@
|
||||
<div class="px-2 row-wrapper">
|
||||
<span>
|
||||
<a
|
||||
v-tooltip.right="$t('one_time')"
|
||||
href="https://www.paypal.me/liyascthomas"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
v-tooltip.right="$t('one_time')"
|
||||
>
|
||||
<button class="icon">
|
||||
<i class="material-icons">payment</i>
|
||||
@@ -82,15 +82,17 @@
|
||||
<div class="p-2">
|
||||
<h3 class="title">Financial Contributors</h3>
|
||||
<div class="contributors">
|
||||
<a href="https://oss.capital/?ref=hoppscotch" target="_blank" rel="noopener">
|
||||
<a
|
||||
href="https://oss.capital/?ref=hoppscotch"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
<img
|
||||
style="height: 100%; width: 300px"
|
||||
src="~assets/images/ossc-banner.svg"
|
||||
alt="OSS Capital"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
<div class="contributors">
|
||||
<a
|
||||
href="https://appwrite.io/?utm_source=hoppscotch&utm_medium=banner&utm_campaign=hello"
|
||||
target="_blank"
|
||||
@@ -102,8 +104,17 @@
|
||||
alt="Appwrite"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
<div class="contributors">
|
||||
<a
|
||||
href="https://pipedream.com/?ref=hoppscotch"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
<img
|
||||
style="max-width: 100px"
|
||||
src="~assets/images/pipedream.png"
|
||||
alt="pipedream"
|
||||
/>
|
||||
</a>
|
||||
<a
|
||||
href="https://paw.cloud/?utm_source=hoppscotch&utm_medium=website&utm_campaign=hoppscotch-sponsorship"
|
||||
target="_blank"
|
||||
@@ -115,8 +126,17 @@
|
||||
alt="Paw"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
<div class="contributors">
|
||||
<a
|
||||
href="https://simplescraper.io/?utm_source=hs"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
<img
|
||||
style="max-height: 50px"
|
||||
src="~assets/images/Simplescraper_dark.png"
|
||||
alt="Simplescraper"
|
||||
/>
|
||||
</a>
|
||||
<a href="https://tyk.io?ref=hoppscotch" target="_blank" rel="noopener">
|
||||
<img
|
||||
style="max-width: 320px"
|
||||
@@ -125,22 +145,6 @@
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
<div class="contributors">
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
href="https://opencollective.com/hoppscotch/organization/0/website"
|
||||
>
|
||||
<img src="https://opencollective.com/hoppscotch/organization/0/avatar.svg" />
|
||||
</a>
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
href="https://opencollective.com/hoppscotch/organization/1/website"
|
||||
>
|
||||
<img src="https://opencollective.com/hoppscotch/organization/1/avatar.svg" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
<p class="info">
|
||||
@@ -162,8 +166,10 @@
|
||||
.contributors {
|
||||
@apply flex;
|
||||
@apply items-center;
|
||||
@apply flex-nowrap;
|
||||
@apply flex-wrap;
|
||||
@apply overflow-auto;
|
||||
@apply m-2;
|
||||
@apply space-x-2;
|
||||
@apply space-y-2;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -21,13 +21,22 @@
|
||||
rel="noopener"
|
||||
>
|
||||
<button class="icon">
|
||||
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24">
|
||||
<svg
|
||||
class="material-icons"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
>
|
||||
<path
|
||||
d="M12 0C5.373 0 0 5.373 0 12s5.373 12 12 12 12-5.373 12-12S18.627 0 12 0zm8.003 8.657c-1.276-3.321-4.46-4.605-5.534-4.537 3.529 1.376 4.373 6.059 4.06 7.441-.307-1.621-1.286-3.017-1.872-3.385 3.417 8.005-4.835 10.465-7.353 7.687.649.168 1.931.085 2.891-.557.898-.602.983-.638 1.56-.683.686-.053-.041-1.406-1.539-1.177-.616.094-1.632.819-2.88.341-1.508-.576-1.46-2.634.096-2.015.337-.437.088-1.263.088-1.263.452-.414 1.022-.706 1.37-.911.228-.135.829-.507.795-1.23-.123-.096-.32-.219-.766-.193-1.736.11-1.852-.518-1.967-.808.078-.668.524-1.534 1.361-1.931-1.257-.193-2.28.397-2.789 1.154-.809-.174-1.305-.183-2.118-.031-.316-.24-.666-.67-.878-1.181C6.36 3.312 9.027 2 12 2c5.912 0 8.263 4.283 8.003 6.657z"
|
||||
/>
|
||||
</svg>
|
||||
<span>Firefox</span>
|
||||
<span class="icon" v-if="hasFirefoxExtInstalled" v-tooltip="$t('installed')">
|
||||
<span
|
||||
v-if="hasFirefoxExtInstalled"
|
||||
v-tooltip="$t('installed')"
|
||||
class="icon"
|
||||
>
|
||||
<i class="material-icons">done</i>
|
||||
</span>
|
||||
</button>
|
||||
@@ -40,13 +49,22 @@
|
||||
rel="noopener"
|
||||
>
|
||||
<button class="icon">
|
||||
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24">
|
||||
<svg
|
||||
class="material-icons"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
>
|
||||
<path
|
||||
d="M2.897 4.181A11.87 11.87 0 0111.969 0c4.288 0 8.535 2.273 10.717 6.554h-9.293c-1.674.001-2.755-.037-3.926.579-1.376.724-2.415 2.067-2.777 3.644L2.897 4.181zM8.007 12c0 2.2 1.789 3.99 3.988 3.99s3.988-1.79 3.988-3.99-1.789-3.991-3.988-3.991S8.007 9.8 8.007 12zm5.536 5.223c-2.238.666-4.858-.073-6.293-2.549-1.095-1.891-3.989-6.933-5.305-9.225A11.856 11.856 0 000 11.956c0 5.448 3.726 10.65 9.673 11.818l3.87-6.551zm2.158-9.214a5.463 5.463 0 011.007 6.719 1815.43 1815.43 0 01-5.46 9.248C18.437 24.419 24 18.616 24 12.004c0-1.313-.22-2.66-.69-3.995h-7.609z"
|
||||
/>
|
||||
</svg>
|
||||
<span>Chrome</span>
|
||||
<span class="icon" v-if="hasChromeExtInstalled" v-tooltip="$t('installed')">
|
||||
<span
|
||||
v-if="hasChromeExtInstalled"
|
||||
v-tooltip="$t('installed')"
|
||||
class="icon"
|
||||
>
|
||||
<i class="material-icons">done</i>
|
||||
</span>
|
||||
</button>
|
||||
@@ -67,18 +85,18 @@ export default {
|
||||
props: {
|
||||
show: Boolean,
|
||||
},
|
||||
watch: {
|
||||
show() {
|
||||
this.hasChromeExtInstalled = hasChromeExtensionInstalled()
|
||||
this.hasFirefoxExtInstalled = hasFirefoxExtensionInstalled()
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
hasChromeExtInstalled: hasChromeExtensionInstalled(),
|
||||
hasFirefoxExtInstalled: hasFirefoxExtensionInstalled(),
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
show() {
|
||||
this.hasChromeExtInstalled = hasChromeExtensionInstalled()
|
||||
this.hasFirefoxExtInstalled = hasFirefoxExtensionInstalled()
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
hideModal() {
|
||||
this.$emit("hide-modal")
|
||||
|
||||
@@ -1,11 +1,18 @@
|
||||
<template>
|
||||
<footer class="footer">
|
||||
<div class="row-wrapper">
|
||||
<span class="flex flex-col font-mono md:flex-row" style="align-items: start">
|
||||
<a class="footer-link" href="https://www.netlify.com" target="_blank" rel="noopener">
|
||||
<span
|
||||
class="flex flex-col font-mono md:flex-row"
|
||||
style="align-items: start"
|
||||
>
|
||||
<a
|
||||
class="footer-link"
|
||||
href="https://www.netlify.com"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
Powered by Netlify
|
||||
</a>
|
||||
<span class="footer-link"> Sponsored by </span>
|
||||
<span>
|
||||
<a
|
||||
class="footer-link"
|
||||
@@ -15,9 +22,6 @@
|
||||
>
|
||||
OSS Capital
|
||||
</a>
|
||||
</span>
|
||||
<span class="footer-link"> & </span>
|
||||
<span>
|
||||
<a
|
||||
class="footer-link"
|
||||
href="https://paw.cloud/?utm_source=hoppscotch&utm_medium=website&utm_campaign=hoppscotch-sponsorship"
|
||||
@@ -26,6 +30,14 @@
|
||||
>
|
||||
Paw
|
||||
</a>
|
||||
<a
|
||||
class="footer-link"
|
||||
href="https://simplescraper.io/?utm_source=hs"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
Simplescraper
|
||||
</a>
|
||||
</span>
|
||||
<iframe
|
||||
src="https://ghbtns.com/github-btn.html?user=hoppscotch&type=sponsor"
|
||||
@@ -38,20 +50,23 @@
|
||||
loading="lazy"
|
||||
></iframe>
|
||||
</span>
|
||||
<span class="flex flex-col font-mono md:flex-row" style="align-items: start">
|
||||
<a href="mailto:liyascthomas@gmail.com" target="_blank" rel="noopener">
|
||||
<button class="icon" v-tooltip="$t('contact_us')">
|
||||
<span
|
||||
class="flex flex-col font-mono md:flex-row"
|
||||
style="align-items: start"
|
||||
>
|
||||
<a href="mailto:support@hoppscotch.io" target="_blank" rel="noopener">
|
||||
<button v-tooltip="$t('contact_us')" class="icon">
|
||||
<i class="material-icons">email</i>
|
||||
</button>
|
||||
</a>
|
||||
<v-popover>
|
||||
<button class="icon" v-tooltip="$t('choose_language')">
|
||||
<button v-tooltip="$t('choose_language')" class="icon">
|
||||
<i class="material-icons">translate</i>
|
||||
</button>
|
||||
<template slot="popover">
|
||||
<div v-for="locale in availableLocales" :key="locale.code">
|
||||
<nuxt-link :to="switchLocalePath(locale.code)">
|
||||
<button class="icon" v-close-popover>
|
||||
<button v-close-popover class="icon">
|
||||
{{ locale.name }}
|
||||
</button>
|
||||
</nuxt-link>
|
||||
@@ -63,20 +78,6 @@
|
||||
</footer>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.footer-link {
|
||||
@apply inline-flex;
|
||||
@apply flex-shrink-0;
|
||||
@apply my-2;
|
||||
@apply mx-4;
|
||||
@apply text-fgLightColor;
|
||||
|
||||
&:hover {
|
||||
@apply text-fgColor;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
computed: {
|
||||
@@ -86,3 +87,18 @@ export default {
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.footer-link {
|
||||
@apply inline-flex;
|
||||
@apply flex-shrink-0;
|
||||
@apply my-2;
|
||||
@apply mx-4;
|
||||
@apply text-fgLightColor;
|
||||
@apply text-sm;
|
||||
|
||||
&:hover {
|
||||
@apply text-fgColor;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -22,16 +22,31 @@
|
||||
href="https://appwrite.io/?utm_source=hoppscotch&utm_medium=banner&utm_campaign=hello"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
class="inline-flex items-center px-4 py-2 mx-4 font-mono text-sm rounded-md bg-bgDarkColor hide-on-small-screen"
|
||||
class="
|
||||
inline-flex
|
||||
items-center
|
||||
px-4
|
||||
py-2
|
||||
mx-4
|
||||
font-mono
|
||||
text-sm
|
||||
rounded-md
|
||||
bg-bgDarkColor
|
||||
hide-on-small-screen
|
||||
"
|
||||
>
|
||||
Appwrite - Open-Source Backend as a Service
|
||||
<img class="w-8 ml-2" src="~assets/images/appwrite-icon.svg" alt="Appwrite" />
|
||||
<img
|
||||
class="w-8 ml-2"
|
||||
src="~assets/images/appwrite-icon.svg"
|
||||
alt="Appwrite"
|
||||
/>
|
||||
</a>
|
||||
<button
|
||||
class="icon"
|
||||
id="installPWA"
|
||||
@click.prevent="showInstallPrompt()"
|
||||
v-tooltip="$t('install_pwa')"
|
||||
class="icon"
|
||||
@click.prevent="showInstallPrompt()"
|
||||
>
|
||||
<i class="material-icons">offline_bolt</i>
|
||||
</button>
|
||||
@@ -41,8 +56,13 @@
|
||||
aria-label="GitHub"
|
||||
rel="noopener"
|
||||
>
|
||||
<button class="icon" aria-label="GitHub" v-tooltip="'GitHub'">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" class="material-icons">
|
||||
<button v-tooltip="'GitHub'" class="icon" aria-label="GitHub">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
class="material-icons"
|
||||
>
|
||||
<path
|
||||
d="M12 0C5.374 0 0 5.373 0 12c0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23A11.509 11.509 0 0112 5.803c1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576C20.566 21.797 24 17.3 24 12c0-6.627-5.373-12-12-12z"
|
||||
/>
|
||||
@@ -50,7 +70,7 @@
|
||||
</button>
|
||||
</a>
|
||||
<v-popover v-if="fb.currentUser === null">
|
||||
<button class="icon" v-tooltip="$t('login_with')">
|
||||
<button v-tooltip="$t('login_with')" class="icon">
|
||||
<i class="material-icons">login</i>
|
||||
</button>
|
||||
<template slot="popover">
|
||||
@@ -59,12 +79,13 @@
|
||||
</v-popover>
|
||||
<v-popover v-else>
|
||||
<button
|
||||
class="icon"
|
||||
v-tooltip="
|
||||
(fb.currentUser.displayName || '<label><i>Name not found</i></label>') +
|
||||
(fb.currentUser.displayName ||
|
||||
'<label><i>Name not found</i></label>') +
|
||||
'<br>' +
|
||||
(fb.currentUser.email || '<label><i>Email not found</i></label>')
|
||||
"
|
||||
class="icon"
|
||||
aria-label="Account"
|
||||
>
|
||||
<img
|
||||
@@ -77,7 +98,7 @@
|
||||
</button>
|
||||
<template slot="popover">
|
||||
<div>
|
||||
<nuxt-link :to="localePath('settings')" v-close-popover>
|
||||
<nuxt-link v-close-popover :to="localePath('settings')">
|
||||
<button class="icon">
|
||||
<i class="material-icons">settings</i>
|
||||
<span>
|
||||
@@ -92,26 +113,26 @@
|
||||
</template>
|
||||
</v-popover>
|
||||
<v-popover>
|
||||
<button class="icon" v-tooltip="$t('more')">
|
||||
<button v-tooltip="$t('more')" class="icon">
|
||||
<i class="material-icons">drag_indicator</i>
|
||||
</button>
|
||||
<template slot="popover">
|
||||
<button class="icon" @click="showExtensions = true" v-close-popover>
|
||||
<button v-close-popover class="icon" @click="showExtensions = true">
|
||||
<i class="material-icons">extension</i>
|
||||
<span>{{ $t("extensions") }}</span>
|
||||
</button>
|
||||
<button class="icon" @click="showShortcuts = true" v-close-popover>
|
||||
<button v-close-popover class="icon" @click="showShortcuts = true">
|
||||
<i class="material-icons">keyboard</i>
|
||||
<span>{{ $t("shortcuts") }}</span>
|
||||
</button>
|
||||
<button class="icon" @click="showSupport = true" v-close-popover>
|
||||
<button v-close-popover class="icon" @click="showSupport = true">
|
||||
<i class="material-icons">favorite</i>
|
||||
<span>{{ $t("support_us") }}</span>
|
||||
</button>
|
||||
<button
|
||||
class="icon"
|
||||
onClick="window.open('https://twitter.com/share?text=👽 Hoppscotch • Open source API development ecosystem - Helps you create requests faster, saving precious time on development.&url=https://hoppscotch.io&hashtags=hoppscotch&via=liyasthomas');"
|
||||
v-close-popover
|
||||
class="icon"
|
||||
onClick="window.open('https://twitter.com/share?text=👽 Hoppscotch • Open source API development ecosystem - Helps you create requests faster, saving precious time on development.&url=https://hoppscotch.io&hashtags=hoppscotch&via=hoppscotch_io');"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24">
|
||||
<path
|
||||
@@ -122,10 +143,10 @@
|
||||
</button>
|
||||
<button
|
||||
v-if="navigatorShare"
|
||||
class="icon"
|
||||
@click="nativeShare"
|
||||
v-close-popover
|
||||
v-tooltip="$t('more')"
|
||||
class="icon"
|
||||
@click="nativeShare"
|
||||
>
|
||||
<i class="material-icons">share</i>
|
||||
<span>Share</span>
|
||||
@@ -134,12 +155,149 @@
|
||||
</v-popover>
|
||||
</span>
|
||||
</div>
|
||||
<AppExtensions :show="showExtensions" @hide-modal="showExtensions = false" />
|
||||
<AppExtensions
|
||||
:show="showExtensions"
|
||||
@hide-modal="showExtensions = false"
|
||||
/>
|
||||
<AppShortcuts :show="showShortcuts" @hide-modal="showShortcuts = false" />
|
||||
<AppSupport :show="showSupport" @hide-modal="showSupport = false" />
|
||||
</header>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import intializePwa from "~/helpers/pwa"
|
||||
import { fb } from "~/helpers/fb"
|
||||
// import { hasExtensionInstalled } from "~/helpers/strategies/ExtensionStrategy"
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
// Once the PWA code is initialized, this holds a method
|
||||
// that can be called to show the user the installation
|
||||
// prompt.
|
||||
showInstallPrompt: null,
|
||||
showExtensions: false,
|
||||
showShortcuts: false,
|
||||
showSupport: false,
|
||||
navigatorShare: navigator.share,
|
||||
fb,
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
this._keyListener = function (e) {
|
||||
if (e.key === "Escape") {
|
||||
e.preventDefault()
|
||||
this.showExtensions = this.showShortcuts = this.showSupport = false
|
||||
}
|
||||
}
|
||||
document.addEventListener("keydown", this._keyListener.bind(this))
|
||||
|
||||
// Initializes the PWA code - checks if the app is installed,
|
||||
// etc.
|
||||
this.showInstallPrompt = await intializePwa()
|
||||
const cookiesAllowed = localStorage.getItem("cookiesAllowed") === "yes"
|
||||
if (!cookiesAllowed) {
|
||||
this.$toast.show(this.$t("we_use_cookies"), {
|
||||
icon: "info",
|
||||
duration: 5000,
|
||||
theme: "toasted-primary",
|
||||
action: [
|
||||
{
|
||||
text: this.$t("dismiss"),
|
||||
onClick: (_, toastObject) => {
|
||||
localStorage.setItem("cookiesAllowed", "yes")
|
||||
toastObject.goAway(0)
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
}
|
||||
|
||||
// let showAd = localStorage.getItem("showAd") === "no"
|
||||
// if (!showAd) {
|
||||
// setTimeout(() => {
|
||||
// this.$toast.clear()
|
||||
// this.$toast.show(
|
||||
// "<span><a href='https://github.com/sponsors/hoppscotch' target='_blank' rel='noopener'>Sponsor us to support Hoppscotch open source project 💖</a><br><sub>Whoosh this away to dismiss.</sub></span>",
|
||||
// {
|
||||
// icon: "",
|
||||
// duration: 0,
|
||||
// theme: "toasted-ad",
|
||||
// action: [
|
||||
// {
|
||||
// text: "Sponsor",
|
||||
// icon: "chevron_right",
|
||||
// onClick: (_, toastObject) => {
|
||||
// localStorage.setItem("showAd", "no")
|
||||
// toastObject.goAway(0)
|
||||
// window.open("https://github.com/sponsors/hoppscotch")
|
||||
// },
|
||||
// },
|
||||
// ],
|
||||
// onComplete() {
|
||||
// localStorage.setItem("showAd", "no")
|
||||
// },
|
||||
// }
|
||||
// )
|
||||
// }, 8000)
|
||||
// }
|
||||
|
||||
// let showExtensionsToast = localStorage.getItem("showExtensionsToast") === "yes"
|
||||
// if (!showExtensionsToast) {
|
||||
// setTimeout(() => {
|
||||
// if (!hasExtensionInstalled()) {
|
||||
// this.$toast.show(this.$t("extensions_info2"), {
|
||||
// icon: "extension",
|
||||
// duration: 5000,
|
||||
// theme: "toasted-primary",
|
||||
// action: [
|
||||
// {
|
||||
// text: this.$t("yes"),
|
||||
// onClick: (_, toastObject) => {
|
||||
// this.showExtensions = true
|
||||
// localStorage.setItem("showExtensionsToast", "yes")
|
||||
// toastObject.goAway(0)
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// text: this.$t("no"),
|
||||
// onClick: (_, toastObject) => {
|
||||
// this.$store.commit("setMiscState", {
|
||||
// value: false,
|
||||
// attribute: "showExtensionsToast",
|
||||
// })
|
||||
// localStorage.setItem("showExtensionsToast", "no")
|
||||
// toastObject.goAway(0)
|
||||
// },
|
||||
// },
|
||||
// ],
|
||||
// })
|
||||
// }
|
||||
// }, 5000)
|
||||
// }
|
||||
},
|
||||
beforeDestroy() {
|
||||
document.removeEventListener("keydown", this._keyListener)
|
||||
},
|
||||
methods: {
|
||||
nativeShare() {
|
||||
if (navigator.share) {
|
||||
navigator
|
||||
.share({
|
||||
title: "Hoppscotch",
|
||||
text: "Hoppscotch • Open source API development ecosystem - Helps you create requests faster, saving precious time on development.",
|
||||
url: "https://hoppscotch.io",
|
||||
})
|
||||
.then(() => {})
|
||||
.catch(console.error)
|
||||
} else {
|
||||
// fallback
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
$responsiveWidth: 768px;
|
||||
|
||||
@@ -182,138 +340,3 @@ $responsiveWidth: 768px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import intializePwa from "~/helpers/pwa"
|
||||
import { fb } from "~/helpers/fb"
|
||||
// import { hasExtensionInstalled } from "~/helpers/strategies/ExtensionStrategy"
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
// Once the PWA code is initialized, this holds a method
|
||||
// that can be called to show the user the installation
|
||||
// prompt.
|
||||
showInstallPrompt: null,
|
||||
showExtensions: false,
|
||||
showShortcuts: false,
|
||||
showSupport: false,
|
||||
navigatorShare: navigator.share,
|
||||
fb,
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
this._keyListener = function (e) {
|
||||
if (e.key === "Escape") {
|
||||
e.preventDefault()
|
||||
this.showExtensions = this.showShortcuts = this.showSupport = false
|
||||
}
|
||||
}
|
||||
document.addEventListener("keydown", this._keyListener.bind(this))
|
||||
|
||||
// Initializes the PWA code - checks if the app is installed,
|
||||
// etc.
|
||||
this.showInstallPrompt = await intializePwa()
|
||||
let cookiesAllowed = localStorage.getItem("cookiesAllowed") === "yes"
|
||||
if (!cookiesAllowed) {
|
||||
this.$toast.show(this.$t("we_use_cookies"), {
|
||||
icon: "info",
|
||||
duration: 5000,
|
||||
theme: "toasted-primary",
|
||||
action: [
|
||||
{
|
||||
text: this.$t("dismiss"),
|
||||
onClick: (e, toastObject) => {
|
||||
localStorage.setItem("cookiesAllowed", "yes")
|
||||
toastObject.goAway(0)
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
}
|
||||
|
||||
// let showAd = localStorage.getItem("showAd") === "no"
|
||||
// if (!showAd) {
|
||||
// setTimeout(() => {
|
||||
// this.$toast.clear()
|
||||
// this.$toast.show(
|
||||
// "<span><a href='https://github.com/sponsors/hoppscotch' target='_blank' rel='noopener'>Sponsor us to support Hoppscotch open source project 💖</a><br><sub>Whoosh this away to dismiss.</sub></span>",
|
||||
// {
|
||||
// icon: "",
|
||||
// duration: 0,
|
||||
// theme: "toasted-ad",
|
||||
// action: [
|
||||
// {
|
||||
// text: "Sponsor",
|
||||
// icon: "chevron_right",
|
||||
// onClick: (e, toastObject) => {
|
||||
// localStorage.setItem("showAd", "no")
|
||||
// toastObject.goAway(0)
|
||||
// window.open("https://github.com/sponsors/hoppscotch")
|
||||
// },
|
||||
// },
|
||||
// ],
|
||||
// onComplete() {
|
||||
// localStorage.setItem("showAd", "no")
|
||||
// },
|
||||
// }
|
||||
// )
|
||||
// }, 8000)
|
||||
// }
|
||||
|
||||
// let showExtensionsToast = localStorage.getItem("showExtensionsToast") === "yes"
|
||||
// if (!showExtensionsToast) {
|
||||
// setTimeout(() => {
|
||||
// if (!hasExtensionInstalled()) {
|
||||
// this.$toast.show(this.$t("extensions_info2"), {
|
||||
// icon: "extension",
|
||||
// duration: 5000,
|
||||
// theme: "toasted-primary",
|
||||
// action: [
|
||||
// {
|
||||
// text: this.$t("yes"),
|
||||
// onClick: (e, toastObject) => {
|
||||
// this.showExtensions = true
|
||||
// localStorage.setItem("showExtensionsToast", "yes")
|
||||
// toastObject.goAway(0)
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// text: this.$t("no"),
|
||||
// onClick: (e, toastObject) => {
|
||||
// this.$store.commit("setMiscState", {
|
||||
// value: false,
|
||||
// attribute: "showExtensionsToast",
|
||||
// })
|
||||
// localStorage.setItem("showExtensionsToast", "no")
|
||||
// toastObject.goAway(0)
|
||||
// },
|
||||
// },
|
||||
// ],
|
||||
// })
|
||||
// }
|
||||
// }, 5000)
|
||||
// }
|
||||
},
|
||||
methods: {
|
||||
nativeShare() {
|
||||
if (navigator.share) {
|
||||
navigator
|
||||
.share({
|
||||
title: "Hoppscotch",
|
||||
text:
|
||||
"Hoppscotch • Open source API development ecosystem - Helps you create requests faster, saving precious time on development.",
|
||||
url: "https://hoppscotch.io",
|
||||
})
|
||||
.then(() => {})
|
||||
.catch(console.error)
|
||||
} else {
|
||||
// fallback
|
||||
}
|
||||
},
|
||||
},
|
||||
beforeDestroy() {
|
||||
document.removeEventListener("keydown", this._keyListener)
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -15,6 +15,7 @@ export default {
|
||||
props: {
|
||||
color: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -6,12 +6,51 @@
|
||||
{{ isCollapsed(label) ? "expand_more" : "expand_less" }}
|
||||
</i>
|
||||
</legend>
|
||||
<div class="collapsible" :class="{ hidden: isCollapsed(label.toLowerCase()) }">
|
||||
<slot />
|
||||
<div
|
||||
class="collapsible"
|
||||
:class="{ hidden: isCollapsed(label.toLowerCase()) }"
|
||||
>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</fieldset>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from "vue"
|
||||
|
||||
export default Vue.extend({
|
||||
props: {
|
||||
label: {
|
||||
type: String,
|
||||
default: "Section",
|
||||
},
|
||||
noLegend: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
sectionString(): string {
|
||||
return `${this.$route.path.replace(/\/+$/, "")}/${this.label}`
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
collapse() {
|
||||
// Save collapsed section into the collapsedSections array
|
||||
this.$store.commit("setCollapsedSection", this.sectionString)
|
||||
},
|
||||
isCollapsed(_label: string) {
|
||||
return (
|
||||
this.$store.state.theme.collapsedSections.includes(
|
||||
this.sectionString
|
||||
) || false
|
||||
)
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
fieldset {
|
||||
@apply my-4;
|
||||
@@ -33,35 +72,3 @@ fieldset {
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from "vue"
|
||||
|
||||
export default Vue.extend({
|
||||
computed: {
|
||||
sectionString(): string {
|
||||
return `${this.$route.path.replace(/\/+$/, "")}/${this.label}`
|
||||
},
|
||||
},
|
||||
props: {
|
||||
label: {
|
||||
type: String,
|
||||
default: "Section",
|
||||
},
|
||||
noLegend: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
collapse() {
|
||||
// Save collapsed section into the collapsedSections array
|
||||
this.$store.commit("setCollapsedSection", this.sectionString)
|
||||
},
|
||||
isCollapsed(_label: string) {
|
||||
return this.$store.state.theme.collapsedSections.includes(this.sectionString) || false
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -73,18 +73,6 @@
|
||||
</SmartModal>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
kbd {
|
||||
@apply inline-flex;
|
||||
@apply resize-none;
|
||||
@apply m-2;
|
||||
@apply rounded-lg;
|
||||
@apply py-2;
|
||||
@apply px-4;
|
||||
@apply text-sm;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import { getPlatformSpecialKey } from "~/helpers/platformutils"
|
||||
|
||||
@@ -100,3 +88,15 @@ export default {
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
kbd {
|
||||
@apply inline-flex;
|
||||
@apply resize-none;
|
||||
@apply m-2;
|
||||
@apply rounded-lg;
|
||||
@apply py-2;
|
||||
@apply px-4;
|
||||
@apply text-sm;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -6,27 +6,32 @@
|
||||
seems to mess up the nuxt-link active class.
|
||||
-->
|
||||
<nuxt-link
|
||||
v-tooltip.right="$t('home')"
|
||||
:to="localePath('index')"
|
||||
:class="linkActive('/')"
|
||||
v-tooltip.right="$t('home')"
|
||||
:aria-label="$t('home')"
|
||||
>
|
||||
<AppLogo alt class="material-icons" style="height: 24px" />
|
||||
</nuxt-link>
|
||||
<nuxt-link
|
||||
v-tooltip.right="$t('realtime')"
|
||||
:to="localePath('realtime')"
|
||||
:class="linkActive('/realtime')"
|
||||
v-tooltip.right="$t('realtime')"
|
||||
>
|
||||
<i class="material-icons">language</i>
|
||||
</nuxt-link>
|
||||
<nuxt-link
|
||||
v-tooltip.right="$t('graphql')"
|
||||
:to="localePath('graphql')"
|
||||
:class="linkActive('/graphql')"
|
||||
v-tooltip.right="$t('graphql')"
|
||||
:aria-label="$t('graphql')"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24" width="24" viewBox="0 0 29.999 30">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="24"
|
||||
width="24"
|
||||
viewBox="0 0 29.999 30"
|
||||
>
|
||||
<path d="M4.08 22.864l-1.1-.636L15.248.98l1.1.636z" />
|
||||
<path d="M2.727 20.53h24.538v1.272H2.727z" />
|
||||
<path
|
||||
@@ -43,80 +48,111 @@
|
||||
</svg>
|
||||
</nuxt-link>
|
||||
<nuxt-link
|
||||
v-tooltip.right="$t('documentation')"
|
||||
:to="localePath('doc')"
|
||||
:class="linkActive('/doc')"
|
||||
v-tooltip.right="$t('documentation')"
|
||||
:aria-label="$t('documentation')"
|
||||
>
|
||||
<i class="material-icons">topic</i>
|
||||
</nuxt-link>
|
||||
<nuxt-link
|
||||
v-tooltip.right="$t('settings')"
|
||||
:to="localePath('settings')"
|
||||
:class="linkActive('/settings')"
|
||||
v-tooltip.right="$t('settings')"
|
||||
:aria-label="$t('settings')"
|
||||
>
|
||||
<i class="material-icons">settings</i>
|
||||
</nuxt-link>
|
||||
</nav>
|
||||
<nav v-if="$route.path == '/'" class="secondary-nav">
|
||||
<a href="#request" v-tooltip.right="$t('request')">
|
||||
<a v-tooltip.right="$t('request')" href="#request">
|
||||
<i class="material-icons">cloud_upload</i>
|
||||
</a>
|
||||
<a href="#options" v-tooltip.right="$t('options')">
|
||||
<a v-tooltip.right="$t('options')" href="#options">
|
||||
<i class="material-icons">toc</i>
|
||||
</a>
|
||||
<a href="#response" v-tooltip.right="$t('response')">
|
||||
<a v-tooltip.right="$t('response')" href="#response">
|
||||
<i class="material-icons">cloud_download</i>
|
||||
</a>
|
||||
</nav>
|
||||
<nav v-else-if="$route.path.includes('/realtime')" class="secondary-nav">
|
||||
<a href="#request" v-tooltip.right="$t('request')">
|
||||
<a v-tooltip.right="$t('request')" href="#request">
|
||||
<i class="material-icons">cloud_upload</i>
|
||||
</a>
|
||||
<a href="#response" v-tooltip.right="$t('communication')">
|
||||
<a v-tooltip.right="$t('communication')" href="#response">
|
||||
<i class="material-icons">cloud_download</i>
|
||||
</a>
|
||||
</nav>
|
||||
<nav v-else-if="$route.path.includes('/graphql')" class="secondary-nav">
|
||||
<a href="#endpoint" v-tooltip.right="$t('endpoint')">
|
||||
<a v-tooltip.right="$t('endpoint')" href="#endpoint">
|
||||
<i class="material-icons">cloud</i>
|
||||
</a>
|
||||
<a href="#schema" v-tooltip.right="$t('schema')">
|
||||
<a v-tooltip.right="$t('schema')" href="#schema">
|
||||
<i class="material-icons">assignment_returned</i>
|
||||
</a>
|
||||
<a href="#query" v-tooltip.right="$t('query')">
|
||||
<a v-tooltip.right="$t('query')" href="#query">
|
||||
<i class="material-icons">cloud_upload</i>
|
||||
</a>
|
||||
<a href="#response" v-tooltip.right="$t('response')">
|
||||
<a v-tooltip.right="$t('response')" href="#response">
|
||||
<i class="material-icons">cloud_download</i>
|
||||
</a>
|
||||
</nav>
|
||||
<nav v-else-if="$route.path.includes('/doc')" class="secondary-nav">
|
||||
<a href="#import" v-tooltip.right="$t('import')">
|
||||
<a v-tooltip.right="$t('import')" href="#import">
|
||||
<i class="material-icons">folder</i>
|
||||
</a>
|
||||
<a href="#documentation" v-tooltip.right="$t('documentation')">
|
||||
<a v-tooltip.right="$t('documentation')" href="#documentation">
|
||||
<i class="material-icons">insert_drive_file</i>
|
||||
</a>
|
||||
</nav>
|
||||
<nav v-else-if="$route.path.includes('/settings')" class="secondary-nav">
|
||||
<a href="#account" v-tooltip.right="$t('account')">
|
||||
<a v-tooltip.right="$t('account')" href="#account">
|
||||
<i class="material-icons">person</i>
|
||||
</a>
|
||||
<a href="#theme" v-tooltip.right="$t('theme')">
|
||||
<a v-tooltip.right="$t('theme')" href="#theme">
|
||||
<i class="material-icons">brush</i>
|
||||
</a>
|
||||
<a href="#extensions" v-tooltip.right="$t('extensions')">
|
||||
<a v-tooltip.right="$t('extensions')" href="#extensions">
|
||||
<i class="material-icons">extension</i>
|
||||
</a>
|
||||
<a href="#proxy" v-tooltip.right="$t('proxy')">
|
||||
<a v-tooltip.right="$t('proxy')" href="#proxy">
|
||||
<i class="material-icons">public</i>
|
||||
</a>
|
||||
</nav>
|
||||
</aside>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
mounted() {
|
||||
window.addEventListener("scroll", () => {
|
||||
const mainNavLinks = document.querySelectorAll("nav.secondary-nav a")
|
||||
const fromTop = window.scrollY
|
||||
mainNavLinks.forEach(({ hash, classList }) => {
|
||||
const section = document.querySelector(hash)
|
||||
if (
|
||||
section &&
|
||||
section.offsetTop <= fromTop &&
|
||||
section.offsetTop + section.offsetHeight > fromTop
|
||||
) {
|
||||
classList.add("current")
|
||||
} else {
|
||||
classList.remove("current")
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
linkActive(path) {
|
||||
return {
|
||||
"nuxt-link-exact-active": this.$route.path === path,
|
||||
"nuxt-link-active": this.$route.path === path,
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
$responsiveWidth: 768px;
|
||||
|
||||
@@ -269,39 +305,3 @@ nav.secondary-nav {
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
methods: {
|
||||
linkActive(path) {
|
||||
return {
|
||||
"nuxt-link-exact-active": this.$route.path === path,
|
||||
"nuxt-link-active": this.$route.path === path,
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
window.addEventListener("scroll", (event) => {
|
||||
let mainNavLinks = document.querySelectorAll("nav ul li a")
|
||||
let fromTop = window.scrollY
|
||||
mainNavLinks.forEach(({ hash, classList }) => {
|
||||
let section = document.querySelector(hash)
|
||||
if (
|
||||
section &&
|
||||
section.offsetTop <= fromTop &&
|
||||
section.offsetTop + section.offsetHeight > fromTop
|
||||
) {
|
||||
classList.add("current")
|
||||
} else {
|
||||
classList.remove("current")
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
// watch: {
|
||||
// $route() {
|
||||
// // this.$toast.clear();
|
||||
// },
|
||||
// },
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -13,9 +13,9 @@
|
||||
<div slot="body" class="flex flex-col">
|
||||
<label for="selectLabel">{{ $t("label") }}</label>
|
||||
<input
|
||||
type="text"
|
||||
id="selectLabel"
|
||||
v-model="name"
|
||||
type="text"
|
||||
:placeholder="$t('my_new_collection')"
|
||||
@keyup.enter="addNewCollection"
|
||||
/>
|
||||
@@ -37,47 +37,23 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { fb } from "~/helpers/fb"
|
||||
import { getSettingSubject } from "~/newstore/settings"
|
||||
|
||||
export default {
|
||||
props: {
|
||||
show: Boolean,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
name: undefined,
|
||||
}
|
||||
},
|
||||
subscriptions() {
|
||||
return {
|
||||
SYNC_COLLECTIONS: getSettingSubject("syncCollections"),
|
||||
name: null,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
syncCollections() {
|
||||
if (fb.currentUser !== null && this.SYNC_COLLECTIONS) {
|
||||
fb.writeCollections(
|
||||
JSON.parse(JSON.stringify(this.$store.state.postwoman.collections)),
|
||||
"collections"
|
||||
)
|
||||
}
|
||||
},
|
||||
addNewCollection() {
|
||||
if (!this.$data.name) {
|
||||
this.$toast.info(this.$t("invalid_collection_name"))
|
||||
return
|
||||
}
|
||||
this.$store.commit("postwoman/addNewCollection", {
|
||||
name: this.$data.name,
|
||||
flag: "rest",
|
||||
})
|
||||
this.$emit("hide-modal")
|
||||
this.syncCollections()
|
||||
this.$emit("submit", this.name)
|
||||
this.hideModal()
|
||||
},
|
||||
hideModal() {
|
||||
this.name = null
|
||||
this.$emit("hide-modal")
|
||||
this.$data.name = undefined
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<SmartModal v-if="show" @close="show = false">
|
||||
<SmartModal v-if="show" @close="$emit('hide-modal')">
|
||||
<div slot="header">
|
||||
<div class="row-wrapper">
|
||||
<h3 class="title">{{ $t("new_folder") }}</h3>
|
||||
@@ -13,9 +13,9 @@
|
||||
<div slot="body" class="flex flex-col">
|
||||
<label for="selectLabel">{{ $t("label") }}</label>
|
||||
<input
|
||||
type="text"
|
||||
id="selectLabel"
|
||||
v-model="name"
|
||||
type="text"
|
||||
:placeholder="$t('my_new_folder')"
|
||||
@keyup.enter="addFolder"
|
||||
/>
|
||||
@@ -40,13 +40,13 @@
|
||||
export default {
|
||||
props: {
|
||||
show: Boolean,
|
||||
folder: Object,
|
||||
folderPath: String,
|
||||
collectionIndex: Number,
|
||||
folder: { type: Object, default: () => {} },
|
||||
folderPath: { type: String, default: null },
|
||||
collectionIndex: { type: Number, default: null },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
name: undefined,
|
||||
name: null,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@@ -56,8 +56,10 @@ export default {
|
||||
folder: this.folder,
|
||||
path: this.folderPath || `${this.collectionIndex}`,
|
||||
})
|
||||
this.hideModal()
|
||||
},
|
||||
hideModal() {
|
||||
this.name = null
|
||||
this.$emit("hide-modal")
|
||||
},
|
||||
},
|
||||
|
||||
105
components/collections/ChooseType.vue
Normal file
105
components/collections/ChooseType.vue
Normal file
@@ -0,0 +1,105 @@
|
||||
<template>
|
||||
<div v-if="show">
|
||||
<SmartTabs
|
||||
:id="'collections_tab'"
|
||||
styles="m-4"
|
||||
@tab-changed="updateCollectionsType"
|
||||
>
|
||||
<SmartTab
|
||||
:id="'my-collections'"
|
||||
:label="'My Collections'"
|
||||
:selected="true"
|
||||
/>
|
||||
<SmartTab
|
||||
v-if="currentUser && currentUser.eaInvited && !doc"
|
||||
:id="'team-collections'"
|
||||
:label="'Team Collections'"
|
||||
>
|
||||
<ul>
|
||||
<li>
|
||||
<span class="select-wrapper">
|
||||
<SmartIntersection @intersecting="onTeamSelectIntersect">
|
||||
<select
|
||||
id="team"
|
||||
type="text"
|
||||
class="team"
|
||||
autofocus
|
||||
@change="updateSelectedTeam(myTeams[$event.target.value])"
|
||||
>
|
||||
<option
|
||||
:key="undefined"
|
||||
:value="undefined"
|
||||
hidden
|
||||
disabled
|
||||
selected
|
||||
>
|
||||
Select team
|
||||
</option>
|
||||
<option
|
||||
v-for="(team, index) in myTeams"
|
||||
:key="index"
|
||||
:value="index"
|
||||
>
|
||||
{{ team.name }}
|
||||
</option>
|
||||
</select>
|
||||
</SmartIntersection>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</SmartTab>
|
||||
</SmartTabs>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import gql from "graphql-tag"
|
||||
import { currentUserInfo$ } from "~/helpers/teams/BackendUserInfo"
|
||||
|
||||
export default {
|
||||
props: {
|
||||
doc: Boolean,
|
||||
show: Boolean,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
skipTeamsFetching: true,
|
||||
}
|
||||
},
|
||||
subscriptions() {
|
||||
return {
|
||||
currentUser: currentUserInfo$,
|
||||
}
|
||||
},
|
||||
apollo: {
|
||||
myTeams: {
|
||||
query: gql`
|
||||
query GetMyTeams {
|
||||
myTeams {
|
||||
id
|
||||
name
|
||||
myRole
|
||||
}
|
||||
}
|
||||
`,
|
||||
pollInterval: 10000,
|
||||
skip() {
|
||||
return this.skipTeamsFetching
|
||||
},
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onTeamSelectIntersect() {
|
||||
// Load team data as soon as intersection
|
||||
this.$apollo.queries.myTeams.refetch()
|
||||
this.skipTeamsFetching = false
|
||||
},
|
||||
updateCollectionsType(tabID) {
|
||||
this.$emit("update-collection-type", tabID)
|
||||
},
|
||||
updateSelectedTeam(team) {
|
||||
this.$emit("update-selected-team", team)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -13,10 +13,10 @@
|
||||
<div slot="body" class="flex flex-col">
|
||||
<label for="selectLabel">{{ $t("label") }}</label>
|
||||
<input
|
||||
type="text"
|
||||
id="selectLabel"
|
||||
v-model="name"
|
||||
:placeholder="editingCollection.name"
|
||||
type="text"
|
||||
:placeholder="placeholderCollName"
|
||||
@keyup.enter="saveCollection"
|
||||
/>
|
||||
</div>
|
||||
@@ -37,52 +37,23 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { fb } from "~/helpers/fb"
|
||||
import { getSettingSubject } from "~/newstore/settings"
|
||||
|
||||
export default {
|
||||
props: {
|
||||
show: Boolean,
|
||||
editingCollection: Object,
|
||||
editingCollectionIndex: Number,
|
||||
placeholderCollName: { type: String, default: null },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
name: undefined,
|
||||
}
|
||||
},
|
||||
subscriptions() {
|
||||
return {
|
||||
SYNC_COLLECTIONS: getSettingSubject("syncCollections"),
|
||||
name: null,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
syncCollections() {
|
||||
if (fb.currentUser !== null && this.SYNC_COLLECTIONS) {
|
||||
fb.writeCollections(
|
||||
JSON.parse(JSON.stringify(this.$store.state.postwoman.collections)),
|
||||
"collections"
|
||||
)
|
||||
}
|
||||
},
|
||||
saveCollection() {
|
||||
if (!this.$data.name) {
|
||||
this.$toast.info(this.$t("invalid_collection_name"))
|
||||
return
|
||||
}
|
||||
const collectionUpdated = {
|
||||
...this.$props.editingCollection,
|
||||
name: this.$data.name,
|
||||
}
|
||||
this.$store.commit("postwoman/editCollection", {
|
||||
collection: collectionUpdated,
|
||||
collectionIndex: this.$props.editingCollectionIndex,
|
||||
flag: "rest",
|
||||
})
|
||||
this.$emit("hide-modal")
|
||||
this.syncCollections()
|
||||
this.$emit("submit", this.name)
|
||||
this.hideModal()
|
||||
},
|
||||
hideModal() {
|
||||
this.name = null
|
||||
this.$emit("hide-modal")
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<SmartModal v-if="show" @close="show = false">
|
||||
<SmartModal v-if="show" @close="$emit('hide-modal')">
|
||||
<div slot="header">
|
||||
<div class="row-wrapper">
|
||||
<h3 class="title">{{ $t("edit_folder") }}</h3>
|
||||
@@ -13,10 +13,9 @@
|
||||
<div slot="body" class="flex flex-col">
|
||||
<label for="selectLabel">{{ $t("label") }}</label>
|
||||
<input
|
||||
type="text"
|
||||
id="selectLabel"
|
||||
v-model="name"
|
||||
:placeholder="folder.name"
|
||||
type="text"
|
||||
@keyup.enter="editFolder"
|
||||
/>
|
||||
</div>
|
||||
@@ -37,47 +36,22 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { fb } from "~/helpers/fb"
|
||||
import { getSettingSubject } from "~/newstore/settings"
|
||||
|
||||
export default {
|
||||
props: {
|
||||
show: Boolean,
|
||||
collectionIndex: Number,
|
||||
folder: Object,
|
||||
folderIndex: Number,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
name: undefined,
|
||||
}
|
||||
},
|
||||
subscriptions() {
|
||||
return {
|
||||
SYNC_COLLECTIONS: getSettingSubject("syncCollections"),
|
||||
name: null,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
syncCollections() {
|
||||
if (fb.currentUser !== null && this.SYNC_COLLECTIONS) {
|
||||
fb.writeCollections(
|
||||
JSON.parse(JSON.stringify(this.$store.state.postwoman.collections)),
|
||||
"collections"
|
||||
)
|
||||
}
|
||||
},
|
||||
editFolder() {
|
||||
this.$store.commit("postwoman/editFolder", {
|
||||
collectionIndex: this.$props.collectionIndex,
|
||||
folder: { ...this.$props.folder, name: this.$data.name },
|
||||
folderIndex: this.$props.folderIndex,
|
||||
folderName: this.$props.folder.name,
|
||||
flag: "rest",
|
||||
})
|
||||
this.$emit("submit", this.name)
|
||||
this.hideModal()
|
||||
this.syncCollections()
|
||||
},
|
||||
hideModal() {
|
||||
this.name = null
|
||||
this.$emit("hide-modal")
|
||||
},
|
||||
},
|
||||
|
||||
@@ -13,11 +13,11 @@
|
||||
<div slot="body" class="flex flex-col">
|
||||
<label for="selectLabel">{{ $t("label") }}</label>
|
||||
<input
|
||||
type="text"
|
||||
id="selectLabel"
|
||||
v-model="requestUpdateData.name"
|
||||
type="text"
|
||||
:placeholder="placeholderReqName"
|
||||
@keyup.enter="saveRequest"
|
||||
:placeholder="request.name"
|
||||
/>
|
||||
</div>
|
||||
<div slot="footer">
|
||||
@@ -37,58 +37,25 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { fb } from "~/helpers/fb"
|
||||
import { getSettingSubject } from "~/newstore/settings"
|
||||
|
||||
export default {
|
||||
props: {
|
||||
show: Boolean,
|
||||
collectionIndex: Number,
|
||||
folderIndex: Number,
|
||||
folderName: String,
|
||||
request: Object,
|
||||
requestIndex: Number,
|
||||
placeholderReqName: { type: String, default: null },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
requestUpdateData: {
|
||||
name: undefined,
|
||||
name: null,
|
||||
},
|
||||
}
|
||||
},
|
||||
subscriptions() {
|
||||
return {
|
||||
SYNC_COLLECTIONS: getSettingSubject("syncCollections"),
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
syncCollections() {
|
||||
if (fb.currentUser !== null && this.SYNC_COLLECTIONS) {
|
||||
fb.writeCollections(
|
||||
JSON.parse(JSON.stringify(this.$store.state.postwoman.collections)),
|
||||
"collections"
|
||||
)
|
||||
}
|
||||
},
|
||||
saveRequest() {
|
||||
const requestUpdated = {
|
||||
...this.$props.request,
|
||||
name: this.$data.requestUpdateData.name || this.$props.request.name,
|
||||
}
|
||||
|
||||
this.$store.commit("postwoman/editRequest", {
|
||||
requestCollectionIndex: this.$props.collectionIndex,
|
||||
requestFolderName: this.$props.folderName,
|
||||
requestFolderIndex: this.$props.folderIndex,
|
||||
requestNew: requestUpdated,
|
||||
requestIndex: this.$props.requestIndex,
|
||||
flag: "rest",
|
||||
})
|
||||
|
||||
this.$emit("submit", this.requestUpdateData)
|
||||
this.hideModal()
|
||||
this.syncCollections()
|
||||
},
|
||||
hideModal() {
|
||||
this.requestUpdateData = { name: null }
|
||||
this.$emit("hide-modal")
|
||||
},
|
||||
},
|
||||
|
||||
@@ -2,15 +2,32 @@
|
||||
<SmartModal v-if="show" @close="hideModal">
|
||||
<div slot="header">
|
||||
<div class="row-wrapper">
|
||||
<h3 class="title">{{ $t("import_export") }} {{ $t("collections") }}</h3>
|
||||
<h3 class="title">Export</h3>
|
||||
<div>
|
||||
<v-popover>
|
||||
<button class="tooltip-target icon" v-tooltip.left="$t('more')">
|
||||
<button
|
||||
v-if="mode != 'import_export'"
|
||||
v-tooltip.left="'Back'"
|
||||
class="tooltip-target icon"
|
||||
@click="mode = 'import_export'"
|
||||
>
|
||||
<i class="material-icons">arrow_back</i>
|
||||
</button>
|
||||
<v-popover
|
||||
v-if="
|
||||
mode == 'import_export' &&
|
||||
collectionsType.type == 'my-collections'
|
||||
"
|
||||
>
|
||||
<button v-tooltip.left="$t('more')" class="tooltip-target icon">
|
||||
<i class="material-icons">more_vert</i>
|
||||
</button>
|
||||
<template slot="popover">
|
||||
<div>
|
||||
<button class="icon" @click="readCollectionGist" v-close-popover>
|
||||
<button
|
||||
v-close-popover
|
||||
class="icon"
|
||||
@click="readCollectionGist"
|
||||
>
|
||||
<i class="material-icons">assignment_returned</i>
|
||||
<span>{{ $t("import_from_gist") }}</span>
|
||||
</button>
|
||||
@@ -25,12 +42,16 @@
|
||||
}"
|
||||
>
|
||||
<button
|
||||
v-close-popover
|
||||
:disabled="
|
||||
!fb.currentUser ? true : fb.currentUser.provider !== 'github.com' ? true : false
|
||||
!fb.currentUser
|
||||
? true
|
||||
: fb.currentUser.provider !== 'github.com'
|
||||
? true
|
||||
: false
|
||||
"
|
||||
class="icon"
|
||||
@click="createCollectionGist"
|
||||
v-close-popover
|
||||
>
|
||||
<i class="material-icons">assignment_turned_in</i>
|
||||
<span>{{ $t("create_secret_gist") }}</span>
|
||||
@@ -45,67 +66,127 @@
|
||||
</div>
|
||||
</div>
|
||||
<div slot="body" class="flex flex-col">
|
||||
<div class="flex flex-col items-start p-2">
|
||||
<div v-if="mode == 'import_export'" class="flex flex-col items-start p-2">
|
||||
<span
|
||||
v-tooltip="{
|
||||
content: !fb.currentUser ? $t('login_first') : $t('replace_current'),
|
||||
content: !fb.currentUser
|
||||
? $t('login_first')
|
||||
: $t('replace_current'),
|
||||
}"
|
||||
>
|
||||
<button :disabled="!fb.currentUser" class="icon" @click="syncCollections">
|
||||
<button
|
||||
v-if="collectionsType.type == 'my-collections'"
|
||||
:disabled="!fb.currentUser"
|
||||
class="icon"
|
||||
@click="syncCollections"
|
||||
>
|
||||
<i class="material-icons">folder_shared</i>
|
||||
<span>{{ $t("import_from_sync") }}</span>
|
||||
</button>
|
||||
</span>
|
||||
<button
|
||||
v-tooltip="$t('replace_current')"
|
||||
class="icon"
|
||||
@click="openDialogChooseFileToReplaceWith"
|
||||
v-tooltip="$t('replace_current')"
|
||||
>
|
||||
<i class="material-icons">create_new_folder</i>
|
||||
<span>{{ $t("replace_json") }}</span>
|
||||
<input
|
||||
type="file"
|
||||
@change="replaceWithJSON"
|
||||
style="display: none"
|
||||
ref="inputChooseFileToReplaceWith"
|
||||
type="file"
|
||||
style="display: none"
|
||||
accept="application/json"
|
||||
@change="replaceWithJSON"
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
v-tooltip="$t('preserve_current')"
|
||||
class="icon"
|
||||
@click="openDialogChooseFileToImportFrom"
|
||||
v-tooltip="$t('preserve_current')"
|
||||
>
|
||||
<i class="material-icons">folder_special</i>
|
||||
<span>{{ $t("import_json") }}</span>
|
||||
<input
|
||||
type="file"
|
||||
@change="importFromJSON"
|
||||
style="display: none"
|
||||
ref="inputChooseFileToImportFrom"
|
||||
type="file"
|
||||
style="display: none"
|
||||
accept="application/json"
|
||||
@change="importFromJSON"
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
v-if="collectionsType.type == 'team-collections'"
|
||||
v-tooltip="$t('replace_current')"
|
||||
class="icon"
|
||||
@click="mode = 'import_from_my_collections'"
|
||||
>
|
||||
<i class="material-icons">folder_special</i>
|
||||
<span>{{ "Import from My Collections" }}</span>
|
||||
</button>
|
||||
<button
|
||||
v-tooltip="$t('show_code')"
|
||||
class="icon"
|
||||
@click="
|
||||
() => {
|
||||
mode = 'export_as_json'
|
||||
getJSONCollection()
|
||||
}
|
||||
"
|
||||
>
|
||||
<i class="material-icons">folder_special</i>
|
||||
<span>{{ "Export As JSON" }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div v-if="showJsonCode" class="row-wrapper">
|
||||
<div v-if="mode == 'import_from_my_collections'">
|
||||
<span class="select-wrapper">
|
||||
<select
|
||||
type="text"
|
||||
autofocus
|
||||
@change="
|
||||
($event) => {
|
||||
mySelectedCollectionID = $event.target.value
|
||||
}
|
||||
"
|
||||
>
|
||||
<option
|
||||
:key="undefined"
|
||||
:value="undefined"
|
||||
hidden
|
||||
disabled
|
||||
selected
|
||||
>
|
||||
Select Collection
|
||||
</option>
|
||||
<option
|
||||
v-for="(collection, index) in myCollections"
|
||||
:key="index"
|
||||
:value="index"
|
||||
>
|
||||
{{ collection.name }}
|
||||
</option>
|
||||
</select>
|
||||
</span>
|
||||
<button
|
||||
class="m-2 icon primary"
|
||||
:disabled="mySelectedCollectionID == undefined"
|
||||
@click="importFromMyCollections"
|
||||
>
|
||||
{{ $t("import") }}
|
||||
</button>
|
||||
</div>
|
||||
<div v-if="mode == 'export_as_json'">
|
||||
<textarea v-model="collectionJson" rows="8" readonly></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div slot="footer">
|
||||
<div class="row-wrapper">
|
||||
<span>
|
||||
<SmartToggle :on="showJsonCode" @change="showJsonCode = $event">
|
||||
{{ $t("show_code") }}
|
||||
</SmartToggle>
|
||||
</span>
|
||||
<span>
|
||||
<button class="icon" @click="hideModal">
|
||||
{{ $t("cancel") }}
|
||||
</button>
|
||||
<button class="icon primary" @click="exportJSON" v-tooltip="$t('download_file')">
|
||||
{{ $t("export") }}
|
||||
</button>
|
||||
</span>
|
||||
<div class="row-wrapper">
|
||||
<span class="m-2">
|
||||
<button
|
||||
v-tooltip="$t('download_file')"
|
||||
class="icon primary"
|
||||
@click="exportJSON"
|
||||
>
|
||||
{{ $t("export") }}
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</SmartModal>
|
||||
@@ -114,23 +195,32 @@
|
||||
<script>
|
||||
import { fb } from "~/helpers/fb"
|
||||
import { getSettingSubject } from "~/newstore/settings"
|
||||
import * as teamUtils from "~/helpers/teams/utils"
|
||||
|
||||
export default {
|
||||
props: {
|
||||
show: Boolean,
|
||||
collectionsType: { type: Object, default: () => {} },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
fb,
|
||||
showJsonCode: false,
|
||||
mode: "import_export",
|
||||
mySelectedCollectionID: undefined,
|
||||
collectionJson: "",
|
||||
}
|
||||
},
|
||||
subscriptions() {
|
||||
SYNC_COLLECTIONS: getSettingSubject("syncCollections")
|
||||
},
|
||||
props: {
|
||||
show: Boolean,
|
||||
return {
|
||||
SYNC_COLLECTIONS: getSettingSubject("syncCollections"),
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
collectionJson() {
|
||||
return JSON.stringify(this.$store.state.postwoman.collections, null, 2)
|
||||
myCollections() {
|
||||
return fb.currentUser !== null
|
||||
? fb.currentCollections
|
||||
: this.$store.state.postwoman.collections
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
@@ -141,7 +231,7 @@ export default {
|
||||
{
|
||||
files: {
|
||||
"hoppscotch-collections.json": {
|
||||
content: this.collectionJson,
|
||||
content: this.getJSONCollection(),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -152,11 +242,11 @@ export default {
|
||||
},
|
||||
}
|
||||
)
|
||||
.then(({ html_url }) => {
|
||||
.then((res) => {
|
||||
this.$toast.success(this.$t("gist_created"), {
|
||||
icon: "done",
|
||||
})
|
||||
window.open(html_url)
|
||||
window.open(res.html_url)
|
||||
})
|
||||
.catch((error) => {
|
||||
this.$toast.error(this.$t("something_went_wrong"), {
|
||||
@@ -166,7 +256,7 @@ export default {
|
||||
})
|
||||
},
|
||||
async readCollectionGist() {
|
||||
let gist = prompt(this.$t("enter_gist_url"))
|
||||
const gist = prompt(this.$t("enter_gist_url"))
|
||||
if (!gist) return
|
||||
await this.$axios
|
||||
.$get(`https://api.github.com/gists/${gist.split("/").pop()}`, {
|
||||
@@ -175,8 +265,11 @@ export default {
|
||||
},
|
||||
})
|
||||
.then(({ files }) => {
|
||||
let collections = JSON.parse(Object.values(files)[0].content)
|
||||
this.$store.commit("postwoman/replaceCollections", { data: collections, flag: "rest" })
|
||||
const collections = JSON.parse(Object.values(files)[0].content)
|
||||
this.$store.commit("postwoman/replaceCollections", {
|
||||
data: collections,
|
||||
flag: "rest",
|
||||
})
|
||||
this.fileImported()
|
||||
this.syncToFBCollections()
|
||||
})
|
||||
@@ -186,6 +279,8 @@ export default {
|
||||
})
|
||||
},
|
||||
hideModal() {
|
||||
this.mode = "import_export"
|
||||
this.mySelectedCollectionID = undefined
|
||||
this.$emit("hide-modal")
|
||||
},
|
||||
openDialogChooseFileToReplaceWith() {
|
||||
@@ -195,60 +290,157 @@ export default {
|
||||
this.$refs.inputChooseFileToImportFrom.click()
|
||||
},
|
||||
replaceWithJSON() {
|
||||
let reader = new FileReader()
|
||||
const reader = new FileReader()
|
||||
reader.onload = ({ target }) => {
|
||||
let content = target.result
|
||||
const content = target.result
|
||||
let collections = JSON.parse(content)
|
||||
if (collections[0]) {
|
||||
let [name, folders, requests] = Object.keys(collections[0])
|
||||
if (name === "name" && folders === "folders" && requests === "requests") {
|
||||
const [name, folders, requests] = Object.keys(collections[0])
|
||||
if (
|
||||
name === "name" &&
|
||||
folders === "folders" &&
|
||||
requests === "requests"
|
||||
) {
|
||||
// Do nothing
|
||||
}
|
||||
} else if (collections.info && collections.info.schema.includes("v2.1.0")) {
|
||||
} else if (
|
||||
collections.info &&
|
||||
collections.info.schema.includes("v2.1.0")
|
||||
) {
|
||||
collections = [this.parsePostmanCollection(collections)]
|
||||
} else {
|
||||
this.failedImport()
|
||||
return
|
||||
}
|
||||
this.$store.commit("postwoman/replaceCollections", { data: collections, flag: "rest" })
|
||||
this.fileImported()
|
||||
this.syncToFBCollections()
|
||||
if (this.collectionsType.type === "team-collections") {
|
||||
teamUtils
|
||||
.replaceWithJSON(
|
||||
this.$apollo,
|
||||
collections,
|
||||
this.collectionsType.selectedTeam.id
|
||||
)
|
||||
.then((status) => {
|
||||
if (status) {
|
||||
this.fileImported()
|
||||
} else {
|
||||
this.failedImport()
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error)
|
||||
this.failedImport()
|
||||
})
|
||||
} else {
|
||||
this.$store.commit("postwoman/replaceCollections", {
|
||||
data: collections,
|
||||
flag: "rest",
|
||||
})
|
||||
this.fileImported()
|
||||
this.syncToFBCollections()
|
||||
}
|
||||
}
|
||||
reader.readAsText(this.$refs.inputChooseFileToReplaceWith.files[0])
|
||||
this.$refs.inputChooseFileToReplaceWith.value = ""
|
||||
},
|
||||
importFromJSON() {
|
||||
let reader = new FileReader()
|
||||
const reader = new FileReader()
|
||||
reader.onload = ({ target }) => {
|
||||
let content = target.result
|
||||
const content = target.result
|
||||
let collections = JSON.parse(content)
|
||||
if (collections[0]) {
|
||||
let [name, folders, requests] = Object.keys(collections[0])
|
||||
if (name === "name" && folders === "folders" && requests === "requests") {
|
||||
const [name, folders, requests] = Object.keys(collections[0])
|
||||
if (
|
||||
name === "name" &&
|
||||
folders === "folders" &&
|
||||
requests === "requests"
|
||||
) {
|
||||
// Do nothing
|
||||
}
|
||||
} else if (collections.info && collections.info.schema.includes("v2.1.0")) {
|
||||
//replace the variables, postman uses {{var}}, Hoppscotch uses <<var>>
|
||||
collections = JSON.parse(content.replaceAll(/{{([a-z]+)}}/gi, "<<$1>>"))
|
||||
} else if (
|
||||
collections.info &&
|
||||
collections.info.schema.includes("v2.1.0")
|
||||
) {
|
||||
// replace the variables, postman uses {{var}}, Hoppscotch uses <<var>>
|
||||
collections = JSON.parse(
|
||||
content.replaceAll(/{{([a-z]+)}}/gi, "<<$1>>")
|
||||
)
|
||||
collections = [this.parsePostmanCollection(collections)]
|
||||
} else {
|
||||
this.failedImport()
|
||||
return
|
||||
}
|
||||
this.$store.commit("postwoman/importCollections", { data: collections, flag: "rest" })
|
||||
this.fileImported()
|
||||
this.syncToFBCollections()
|
||||
if (this.collectionsType.type === "team-collections") {
|
||||
teamUtils
|
||||
.importFromJSON(
|
||||
this.$apollo,
|
||||
collections,
|
||||
this.collectionsType.selectedTeam.id
|
||||
)
|
||||
.then((status) => {
|
||||
if (status) {
|
||||
this.$emit("update-team-collections")
|
||||
this.fileImported()
|
||||
} else {
|
||||
this.failedImport()
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error)
|
||||
this.failedImport()
|
||||
})
|
||||
} else {
|
||||
this.$store.commit("postwoman/importCollections", {
|
||||
data: collections,
|
||||
flag: "rest",
|
||||
})
|
||||
this.syncToFBCollections()
|
||||
this.fileImported()
|
||||
}
|
||||
}
|
||||
reader.readAsText(this.$refs.inputChooseFileToImportFrom.files[0])
|
||||
this.$refs.inputChooseFileToImportFrom.value = ""
|
||||
},
|
||||
importFromMyCollections() {
|
||||
teamUtils
|
||||
.importFromMyCollections(
|
||||
this.$apollo,
|
||||
this.mySelectedCollectionID,
|
||||
this.collectionsType.selectedTeam.id
|
||||
)
|
||||
.then((success) => {
|
||||
if (success) {
|
||||
this.fileImported()
|
||||
this.$emit("update-team-collections")
|
||||
} else {
|
||||
this.failedImport()
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error)
|
||||
this.failedImport()
|
||||
})
|
||||
},
|
||||
async getJSONCollection() {
|
||||
if (this.collectionsType.type === "my-collections") {
|
||||
this.collectionJson = JSON.stringify(
|
||||
this.$store.state.postwoman.collections,
|
||||
null,
|
||||
2
|
||||
)
|
||||
} else {
|
||||
this.collectionJson = await teamUtils.exportAsJSON(
|
||||
this.$apollo,
|
||||
this.collectionsType.selectedTeam.id
|
||||
)
|
||||
}
|
||||
return this.collectionJson
|
||||
},
|
||||
exportJSON() {
|
||||
let text = this.collectionJson
|
||||
text = text.replace(/\n/g, "\r\n")
|
||||
let blob = new Blob([text], {
|
||||
const blob = new Blob([text], {
|
||||
type: "text/json",
|
||||
})
|
||||
let anchor = document.createElement("a")
|
||||
const anchor = document.createElement("a")
|
||||
anchor.download = "hoppscotch-collection.json"
|
||||
anchor.href = window.URL.createObjectURL(blob)
|
||||
anchor.target = "_blank"
|
||||
@@ -286,7 +478,7 @@ export default {
|
||||
})
|
||||
},
|
||||
parsePostmanCollection({ info, name, item }) {
|
||||
let postwomanCollection = {
|
||||
const postwomanCollection = {
|
||||
name: "",
|
||||
folders: [],
|
||||
requests: [],
|
||||
@@ -295,26 +487,39 @@ export default {
|
||||
postwomanCollection.name = info ? info.name : name
|
||||
|
||||
if (item && item.length > 0) {
|
||||
for (let collectionItem of item) {
|
||||
for (const collectionItem of item) {
|
||||
if (collectionItem.request) {
|
||||
if (postwomanCollection.hasOwnProperty("folders")) {
|
||||
if (
|
||||
Object.prototype.hasOwnProperty.call(
|
||||
postwomanCollection,
|
||||
"folders"
|
||||
)
|
||||
) {
|
||||
postwomanCollection.name = info ? info.name : name
|
||||
postwomanCollection.requests.push(this.parsePostmanRequest(collectionItem))
|
||||
postwomanCollection.requests.push(
|
||||
this.parsePostmanRequest(collectionItem)
|
||||
)
|
||||
} else {
|
||||
postwomanCollection.name = name ? name : ""
|
||||
postwomanCollection.requests.push(this.parsePostmanRequest(collectionItem))
|
||||
postwomanCollection.name = name || ""
|
||||
postwomanCollection.requests.push(
|
||||
this.parsePostmanRequest(collectionItem)
|
||||
)
|
||||
}
|
||||
} else if (this.hasFolder(collectionItem)) {
|
||||
postwomanCollection.folders.push(this.parsePostmanCollection(collectionItem))
|
||||
postwomanCollection.folders.push(
|
||||
this.parsePostmanCollection(collectionItem)
|
||||
)
|
||||
} else {
|
||||
postwomanCollection.requests.push(this.parsePostmanRequest(collectionItem))
|
||||
postwomanCollection.requests.push(
|
||||
this.parsePostmanRequest(collectionItem)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
return postwomanCollection
|
||||
},
|
||||
parsePostmanRequest({ name, request }) {
|
||||
let pwRequest = {
|
||||
const pwRequest = {
|
||||
url: "",
|
||||
path: "",
|
||||
method: "",
|
||||
@@ -334,20 +539,28 @@ export default {
|
||||
}
|
||||
|
||||
pwRequest.name = name
|
||||
let requestObjectUrl = request.url.raw.match(/^(.+:\/\/[^\/]+|{[^\/]+})(\/[^\?]+|).*$/)
|
||||
if (requestObjectUrl) {
|
||||
pwRequest.url = requestObjectUrl[1]
|
||||
pwRequest.path = requestObjectUrl[2] ? requestObjectUrl[2] : ""
|
||||
if (request.url) {
|
||||
const requestObjectUrl = request.url.raw.match(
|
||||
/^(.+:\/\/[^/]+|{[^/]+})(\/[^?]+|).*$/
|
||||
)
|
||||
if (requestObjectUrl) {
|
||||
pwRequest.url = requestObjectUrl[1]
|
||||
pwRequest.path = requestObjectUrl[2] ? requestObjectUrl[2] : ""
|
||||
}
|
||||
}
|
||||
pwRequest.method = request.method
|
||||
let itemAuth = request.auth ? request.auth : ""
|
||||
let authType = itemAuth ? itemAuth.type : ""
|
||||
const itemAuth = request.auth ? request.auth : ""
|
||||
const authType = itemAuth ? itemAuth.type : ""
|
||||
if (authType === "basic") {
|
||||
pwRequest.auth = "Basic Auth"
|
||||
pwRequest.httpUser =
|
||||
itemAuth.basic[0].key === "username" ? itemAuth.basic[0].value : itemAuth.basic[1].value
|
||||
itemAuth.basic[0].key === "username"
|
||||
? itemAuth.basic[0].value
|
||||
: itemAuth.basic[1].value
|
||||
pwRequest.httpPassword =
|
||||
itemAuth.basic[0].key === "password" ? itemAuth.basic[0].value : itemAuth.basic[1].value
|
||||
itemAuth.basic[0].key === "password"
|
||||
? itemAuth.basic[0].value
|
||||
: itemAuth.basic[1].value
|
||||
} else if (authType === "oauth2") {
|
||||
pwRequest.auth = "OAuth 2.0"
|
||||
pwRequest.bearerToken =
|
||||
@@ -358,26 +571,28 @@ export default {
|
||||
pwRequest.auth = "Bearer Token"
|
||||
pwRequest.bearerToken = itemAuth.bearer[0].value
|
||||
}
|
||||
let requestObjectHeaders = request.header
|
||||
const requestObjectHeaders = request.header
|
||||
if (requestObjectHeaders) {
|
||||
pwRequest.headers = requestObjectHeaders
|
||||
for (let header of pwRequest.headers) {
|
||||
for (const header of pwRequest.headers) {
|
||||
delete header.name
|
||||
delete header.type
|
||||
}
|
||||
}
|
||||
let requestObjectParams = request.url.query
|
||||
if (requestObjectParams) {
|
||||
pwRequest.params = requestObjectParams
|
||||
for (let param of pwRequest.params) {
|
||||
delete param.disabled
|
||||
if (request.url) {
|
||||
const requestObjectParams = request.url.query
|
||||
if (requestObjectParams) {
|
||||
pwRequest.params = requestObjectParams
|
||||
for (const param of pwRequest.params) {
|
||||
delete param.disabled
|
||||
}
|
||||
}
|
||||
}
|
||||
if (request.body) {
|
||||
if (request.body.mode === "urlencoded") {
|
||||
let params = request.body.urlencoded
|
||||
pwRequest.bodyParams = params ? params : []
|
||||
for (let param of pwRequest.bodyParams) {
|
||||
const params = request.body.urlencoded
|
||||
pwRequest.bodyParams = params || []
|
||||
for (const param of pwRequest.bodyParams) {
|
||||
delete param.type
|
||||
}
|
||||
} else if (request.body.mode === "raw") {
|
||||
@@ -388,7 +603,7 @@ export default {
|
||||
return pwRequest
|
||||
},
|
||||
hasFolder(item) {
|
||||
return item.hasOwnProperty("item")
|
||||
return Object.prototype.hasOwnProperty.call(item, "item")
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -12,46 +12,21 @@
|
||||
</div>
|
||||
<div slot="body" class="flex flex-col">
|
||||
<label for="selectLabel">{{ $t("token_req_name") }}</label>
|
||||
<input type="text" id="selectLabel" v-model="requestData.name" @keyup.enter="saveRequestAs" />
|
||||
<ul>
|
||||
<li>
|
||||
<label for="selectCollection">{{ $t("collection") }}</label>
|
||||
<span class="select-wrapper">
|
||||
<select type="text" id="selectCollection" v-model="requestData.collectionIndex">
|
||||
<option :key="undefined" :value="undefined" hidden disabled selected>
|
||||
{{ $t("select_collection") }}
|
||||
</option>
|
||||
<option
|
||||
v-for="(collection, index) in $store.state.postwoman.collections"
|
||||
:key="index"
|
||||
:value="index"
|
||||
>
|
||||
{{ collection.name }}
|
||||
</option>
|
||||
</select>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
<label>{{ $t("folder") }}</label>
|
||||
<SmartAutoComplete
|
||||
:placeholder="$t('search')"
|
||||
:source="folders"
|
||||
:spellcheck="false"
|
||||
v-model="requestData.folderName"
|
||||
<input
|
||||
id="selectLabel"
|
||||
v-model="requestData.name"
|
||||
type="text"
|
||||
@keyup.enter="saveRequestAs"
|
||||
/>
|
||||
<label for="selectLabel">Select location</label>
|
||||
<!-- <input readonly :value="path" /> -->
|
||||
<Collections
|
||||
:picked="picked"
|
||||
:save-request="true"
|
||||
@select="onSelect"
|
||||
@update-collection="collectionsType.type = $event"
|
||||
@update-coll-type="onUpdateCollType"
|
||||
/>
|
||||
<ul>
|
||||
<li>
|
||||
<label for="selectRequest">{{ $t("request") }}</label>
|
||||
<span class="select-wrapper">
|
||||
<select type="text" id="selectRequest" v-model="requestData.requestIndex">
|
||||
<option :key="undefined" :value="undefined">/</option>
|
||||
<option v-for="(folder, index) in requests" :key="index" :value="index">
|
||||
{{ folder.name }}
|
||||
</option>
|
||||
</select>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div slot="footer">
|
||||
<div class="row-wrapper">
|
||||
@@ -72,21 +47,28 @@
|
||||
<script>
|
||||
import { fb } from "~/helpers/fb"
|
||||
import { getSettingSubject } from "~/newstore/settings"
|
||||
import * as teamUtils from "~/helpers/teams/utils"
|
||||
|
||||
export default {
|
||||
props: {
|
||||
show: Boolean,
|
||||
editingRequest: Object,
|
||||
editingRequest: { type: Object, default: () => {} },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
defaultRequestName: "Untitled Request",
|
||||
path: "Path will appear here",
|
||||
requestData: {
|
||||
name: undefined,
|
||||
collectionIndex: undefined,
|
||||
folderName: undefined,
|
||||
requestIndex: undefined,
|
||||
},
|
||||
collectionsType: {
|
||||
type: "my-collections",
|
||||
selectedTeam: undefined,
|
||||
},
|
||||
picked: null,
|
||||
}
|
||||
},
|
||||
subscriptions() {
|
||||
@@ -94,20 +76,6 @@ export default {
|
||||
SYNC_COLLECTIONS: getSettingSubject("syncCollections"),
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
"requestData.collectionIndex": function resetFolderAndRequestIndex() {
|
||||
// if user has chosen some folder, than selected other collection, which doesn't have any folders
|
||||
// than `requestUpdateData.folderName` won't be reseted
|
||||
this.$data.requestData.folderName = undefined
|
||||
this.$data.requestData.requestIndex = undefined
|
||||
},
|
||||
"requestData.folderName": function resetRequestIndex() {
|
||||
this.$data.requestData.requestIndex = undefined
|
||||
},
|
||||
editingRequest({ name }) {
|
||||
this.$data.requestData.name = name || this.$data.defaultRequestName
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
folders() {
|
||||
const collections = this.$store.state.postwoman.collections
|
||||
@@ -130,7 +98,8 @@ export default {
|
||||
return []
|
||||
}
|
||||
|
||||
const userSelectedAnyFolder = folderName !== undefined && folderName !== ""
|
||||
const userSelectedAnyFolder =
|
||||
folderName !== undefined && folderName !== ""
|
||||
|
||||
if (userSelectedAnyFolder) {
|
||||
const collection = collections[collectionIndex]
|
||||
@@ -148,7 +117,27 @@ export default {
|
||||
}
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
"requestData.collectionIndex": function resetFolderAndRequestIndex() {
|
||||
// if user has chosen some folder, than selected other collection, which doesn't have any folders
|
||||
// than `requestUpdateData.folderName` won't be reseted
|
||||
this.$data.requestData.folderName = undefined
|
||||
this.$data.requestData.requestIndex = undefined
|
||||
},
|
||||
"requestData.folderName": function resetRequestIndex() {
|
||||
this.$data.requestData.requestIndex = undefined
|
||||
},
|
||||
editingRequest({ name }) {
|
||||
this.$data.requestData.name = name || this.$data.defaultRequestName
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onUpdateCollType(newCollType) {
|
||||
this.collectionsType = newCollType
|
||||
},
|
||||
onSelect({ picked }) {
|
||||
this.picked = picked
|
||||
},
|
||||
syncCollections() {
|
||||
if (fb.currentUser !== null && this.SYNC_COLLECTIONS) {
|
||||
fb.writeCollections(
|
||||
@@ -158,8 +147,7 @@ export default {
|
||||
}
|
||||
},
|
||||
saveRequestAs() {
|
||||
const userDidntSpecifyCollection = this.$data.requestData.collectionIndex === undefined
|
||||
if (userDidntSpecifyCollection) {
|
||||
if (this.picked == null) {
|
||||
this.$toast.error(this.$t("select_collection"), {
|
||||
icon: "error",
|
||||
})
|
||||
@@ -178,16 +166,61 @@ export default {
|
||||
collection: this.$data.requestData.collectionIndex,
|
||||
}
|
||||
|
||||
this.$store.commit("postwoman/saveRequestAs", {
|
||||
request: requestUpdated,
|
||||
collectionIndex: this.$data.requestData.collectionIndex,
|
||||
folderName: this.$data.requestData.folderName,
|
||||
requestIndex: this.$data.requestData.requestIndex,
|
||||
flag: "rest",
|
||||
})
|
||||
if (this.picked.pickedType === "my-request") {
|
||||
this.$store.commit("postwoman/saveRequestAs", {
|
||||
request: requestUpdated,
|
||||
collectionIndex: this.picked.collectionIndex,
|
||||
folderName: this.picked.folderName,
|
||||
requestIndex: this.picked.requestIndex,
|
||||
flag: "rest",
|
||||
})
|
||||
|
||||
this.syncCollections()
|
||||
} else if (this.picked.pickedType === "my-folder") {
|
||||
this.$store.commit("postwoman/saveRequestAs", {
|
||||
request: requestUpdated,
|
||||
collectionIndex: this.picked.collectionIndex,
|
||||
folderName: this.picked.folderName,
|
||||
flag: "rest",
|
||||
})
|
||||
|
||||
this.syncCollections()
|
||||
} else if (this.picked.pickedType === "my-collection") {
|
||||
this.$store.commit("postwoman/saveRequestAs", {
|
||||
request: requestUpdated,
|
||||
collectionIndex: this.picked.collectionIndex,
|
||||
flag: "rest",
|
||||
})
|
||||
|
||||
this.syncCollections()
|
||||
} else if (this.picked.pickedType === "teams-request") {
|
||||
teamUtils.overwriteRequestTeams(
|
||||
this.$apollo,
|
||||
JSON.stringify(requestUpdated),
|
||||
requestUpdated.name,
|
||||
this.picked.requestID
|
||||
)
|
||||
} else if (this.picked.pickedType === "teams-folder") {
|
||||
teamUtils.saveRequestAsTeams(
|
||||
this.$apollo,
|
||||
JSON.stringify(requestUpdated),
|
||||
requestUpdated.name,
|
||||
this.collectionsType.selectedTeam.id,
|
||||
this.picked.folderID
|
||||
)
|
||||
} else if (this.picked.pickedType === "teams-collection") {
|
||||
teamUtils.saveRequestAsTeams(
|
||||
this.$apollo,
|
||||
JSON.stringify(requestUpdated),
|
||||
requestUpdated.name,
|
||||
this.collectionsType.selectedTeam.id,
|
||||
this.picked.collectionID
|
||||
)
|
||||
}
|
||||
this.$toast.success("Requested added", {
|
||||
icon: "done",
|
||||
})
|
||||
this.hideModal()
|
||||
this.syncCollections()
|
||||
},
|
||||
hideModal() {
|
||||
this.$emit("hide-modal")
|
||||
@@ -195,12 +228,12 @@ export default {
|
||||
},
|
||||
}
|
||||
|
||||
function getFolderNames(folders, namesList) {
|
||||
function getFolderNames(folders, namesList, folderName = "") {
|
||||
if (folders.length) {
|
||||
folders.forEach((folder) => {
|
||||
namesList.push(folder.name)
|
||||
namesList.push(folderName + folder.name)
|
||||
if (folder.folders && folder.folders.length) {
|
||||
getFolderNames(folder.folders, namesList)
|
||||
getFolderNames(folder.folders, namesList, folder.name + "/")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -13,9 +13,9 @@
|
||||
<div slot="body" class="flex flex-col">
|
||||
<label for="selectLabel">{{ $t("label") }}</label>
|
||||
<input
|
||||
type="text"
|
||||
id="selectLabel"
|
||||
v-model="name"
|
||||
type="text"
|
||||
:placeholder="$t('my_new_collection')"
|
||||
@keyup.enter="addNewCollection"
|
||||
/>
|
||||
@@ -45,7 +45,7 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
name: undefined,
|
||||
name: null,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@@ -53,7 +53,9 @@ export default {
|
||||
if (fb.currentUser !== null && fb.currentSettings[0]) {
|
||||
if (fb.currentSettings[0].value) {
|
||||
fb.writeCollections(
|
||||
JSON.parse(JSON.stringify(this.$store.state.postwoman.collectionsGraphql)),
|
||||
JSON.parse(
|
||||
JSON.stringify(this.$store.state.postwoman.collectionsGraphql)
|
||||
),
|
||||
"collectionsGraphql"
|
||||
)
|
||||
}
|
||||
@@ -68,12 +70,12 @@ export default {
|
||||
name: this.$data.name,
|
||||
flag: "graphql",
|
||||
})
|
||||
this.$emit("hide-modal")
|
||||
this.syncCollections()
|
||||
this.hideModal()
|
||||
},
|
||||
hideModal() {
|
||||
this.name = null
|
||||
this.$emit("hide-modal")
|
||||
this.$data.name = undefined
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<SmartModal v-if="show" @close="show = false">
|
||||
<SmartModal v-if="show" @close="$emit('hide-modal')">
|
||||
<div slot="header">
|
||||
<div class="row-wrapper">
|
||||
<h3 class="title">{{ $t("new_folder") }}</h3>
|
||||
@@ -13,9 +13,9 @@
|
||||
<div slot="body" class="flex flex-col">
|
||||
<label for="selectLabel">{{ $t("label") }}</label>
|
||||
<input
|
||||
type="text"
|
||||
id="selectLabel"
|
||||
v-model="name"
|
||||
type="text"
|
||||
:placeholder="$t('my_new_folder')"
|
||||
@keyup.enter="addFolder"
|
||||
/>
|
||||
@@ -37,27 +37,44 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { fb } from "~/helpers/fb"
|
||||
|
||||
export default {
|
||||
props: {
|
||||
show: Boolean,
|
||||
folder: Object,
|
||||
folderPath: String,
|
||||
collectionIndex: Number,
|
||||
folder: { type: Object, default: () => {} },
|
||||
folderPath: { type: String, default: null },
|
||||
collectionIndex: { type: Number, default: null },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
name: undefined,
|
||||
name: null,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
syncCollections() {
|
||||
if (fb.currentUser !== null && fb.currentSettings[0]) {
|
||||
if (fb.currentSettings[0].value) {
|
||||
fb.writeCollections(
|
||||
JSON.parse(
|
||||
JSON.stringify(this.$store.state.postwoman.collectionsGraphql)
|
||||
),
|
||||
"collectionsGraphql"
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
addFolder() {
|
||||
this.$emit("add-folder", {
|
||||
name: this.name,
|
||||
folder: this.folder,
|
||||
path: this.folderPath || `${this.collectionIndex}`,
|
||||
})
|
||||
this.syncCollections()
|
||||
this.hideModal()
|
||||
},
|
||||
hideModal() {
|
||||
this.name = null
|
||||
this.$emit("hide-modal")
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
<template>
|
||||
<div>
|
||||
<div
|
||||
:class="['row-wrapper transition duration-150 ease-in-out', { 'bg-bgDarkColor': dragging }]"
|
||||
:class="[
|
||||
'row-wrapper transition duration-150 ease-in-out',
|
||||
{ 'bg-bgDarkColor': dragging },
|
||||
]"
|
||||
@dragover.prevent
|
||||
@drop.prevent="dropEvent"
|
||||
@dragover="dragging = true"
|
||||
@@ -10,43 +13,60 @@
|
||||
@dragend="dragging = false"
|
||||
>
|
||||
<button class="icon" @click="toggleShowChildren">
|
||||
<i class="material-icons" v-show="!showChildren && !isFiltered">arrow_right</i>
|
||||
<i class="material-icons" v-show="showChildren || isFiltered">arrow_drop_down</i>
|
||||
<i v-show="!showChildren && !isFiltered" class="material-icons"
|
||||
>arrow_right</i
|
||||
>
|
||||
<i v-show="showChildren || isFiltered" class="material-icons"
|
||||
>arrow_drop_down</i
|
||||
>
|
||||
<i class="material-icons">folder</i>
|
||||
<span>{{ collection.name }}</span>
|
||||
</button>
|
||||
<div>
|
||||
<button
|
||||
v-if="doc"
|
||||
v-tooltip.left="$t('import')"
|
||||
class="icon"
|
||||
@click="$emit('select-collection')"
|
||||
v-tooltip.left="$t('import')"
|
||||
>
|
||||
<i class="material-icons">topic</i>
|
||||
</button>
|
||||
<v-popover>
|
||||
<button class="tooltip-target icon" v-tooltip.left="$t('more')">
|
||||
<button v-tooltip.left="$t('more')" class="tooltip-target icon">
|
||||
<i class="material-icons">more_vert</i>
|
||||
</button>
|
||||
<template slot="popover">
|
||||
<div>
|
||||
<button
|
||||
class="icon"
|
||||
@click="$emit('add-folder', { folder: collection, path: `${collectionIndex}` })"
|
||||
v-close-popover
|
||||
class="icon"
|
||||
@click="
|
||||
$emit('add-folder', {
|
||||
folder: collection,
|
||||
path: `${collectionIndex}`,
|
||||
})
|
||||
"
|
||||
>
|
||||
<i class="material-icons">create_new_folder</i>
|
||||
<span>{{ $t("new_folder") }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<button class="icon" @click="$emit('edit-collection')" v-close-popover>
|
||||
<button
|
||||
v-close-popover
|
||||
class="icon"
|
||||
@click="$emit('edit-collection')"
|
||||
>
|
||||
<i class="material-icons">create</i>
|
||||
<span>{{ $t("edit") }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<button class="icon" @click="confirmRemove = true" v-close-popover>
|
||||
<button
|
||||
v-close-popover
|
||||
class="icon"
|
||||
@click="confirmRemove = true"
|
||||
>
|
||||
<i class="material-icons">delete</i>
|
||||
<span>{{ $t("delete") }}</span>
|
||||
</button>
|
||||
@@ -68,7 +88,7 @@
|
||||
:folder-path="`${collectionIndex}/${index}`"
|
||||
:collection-index="collectionIndex"
|
||||
:doc="doc"
|
||||
:isFiltered="isFiltered"
|
||||
:is-filtered="isFiltered"
|
||||
@add-folder="$emit('add-folder', $event)"
|
||||
@edit-folder="$emit('edit-folder', $event)"
|
||||
@edit-request="$emit('edit-request', $event)"
|
||||
@@ -94,11 +114,14 @@
|
||||
</ul>
|
||||
<ul>
|
||||
<li
|
||||
v-if="collection.folders.length === 0 && collection.requests.length === 0"
|
||||
v-if="
|
||||
collection.folders.length === 0 && collection.requests.length === 0
|
||||
"
|
||||
class="flex ml-8 border-l border-brdColor"
|
||||
>
|
||||
<p class="info">
|
||||
<i class="material-icons">not_interested</i> {{ $t("collection_empty") }}
|
||||
<i class="material-icons">not_interested</i>
|
||||
{{ $t("collection_empty") }}
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -117,8 +140,8 @@ import { fb } from "~/helpers/fb"
|
||||
|
||||
export default {
|
||||
props: {
|
||||
collectionIndex: Number,
|
||||
collection: Object,
|
||||
collectionIndex: { type: Number, default: null },
|
||||
collection: { type: Object, default: () => {} },
|
||||
doc: Boolean,
|
||||
isFiltered: Boolean,
|
||||
},
|
||||
@@ -135,7 +158,9 @@ export default {
|
||||
if (fb.currentUser !== null && fb.currentSettings[0]) {
|
||||
if (fb.currentSettings[0].value) {
|
||||
fb.writeCollections(
|
||||
JSON.parse(JSON.stringify(this.$store.state.postwoman.collectionsGraphql)),
|
||||
JSON.parse(
|
||||
JSON.stringify(this.$store.state.postwoman.collectionsGraphql)
|
||||
),
|
||||
"collectionsGraphql"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -13,9 +13,9 @@
|
||||
<div slot="body" class="flex flex-col">
|
||||
<label for="selectLabel">{{ $t("label") }}</label>
|
||||
<input
|
||||
type="text"
|
||||
id="selectLabel"
|
||||
v-model="name"
|
||||
type="text"
|
||||
:placeholder="editingCollection.name"
|
||||
@keyup.enter="saveCollection"
|
||||
/>
|
||||
@@ -42,12 +42,12 @@ import { fb } from "~/helpers/fb"
|
||||
export default {
|
||||
props: {
|
||||
show: Boolean,
|
||||
editingCollection: Object,
|
||||
editingCollectionIndex: Number,
|
||||
editingCollection: { type: Object, default: () => {} },
|
||||
editingCollectionIndex: { type: Number, default: null },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
name: undefined,
|
||||
name: null,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@@ -55,7 +55,9 @@ export default {
|
||||
if (fb.currentUser !== null && fb.currentSettings[0]) {
|
||||
if (fb.currentSettings[0].value) {
|
||||
fb.writeCollections(
|
||||
JSON.parse(JSON.stringify(this.$store.state.postwoman.collectionsGraphql)),
|
||||
JSON.parse(
|
||||
JSON.stringify(this.$store.state.postwoman.collectionsGraphql)
|
||||
),
|
||||
"collectionsGraphql"
|
||||
)
|
||||
}
|
||||
@@ -75,10 +77,11 @@ export default {
|
||||
collectionIndex: this.$props.editingCollectionIndex,
|
||||
flag: "graphql",
|
||||
})
|
||||
this.$emit("hide-modal")
|
||||
this.syncCollections()
|
||||
this.hideModal()
|
||||
},
|
||||
hideModal() {
|
||||
this.name = null
|
||||
this.$emit("hide-modal")
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<SmartModal v-if="show" @close="show = false">
|
||||
<SmartModal v-if="show" @close="$emit('hide-modal')">
|
||||
<div slot="header">
|
||||
<div class="row-wrapper">
|
||||
<h3 class="title">{{ $t("edit_folder") }}</h3>
|
||||
@@ -13,9 +13,9 @@
|
||||
<div slot="body" class="flex flex-col">
|
||||
<label for="selectLabel">{{ $t("label") }}</label>
|
||||
<input
|
||||
type="text"
|
||||
id="selectLabel"
|
||||
v-model="name"
|
||||
type="text"
|
||||
:placeholder="folder.name"
|
||||
@keyup.enter="editFolder"
|
||||
/>
|
||||
@@ -42,13 +42,13 @@ import { fb } from "~/helpers/fb"
|
||||
export default {
|
||||
props: {
|
||||
show: Boolean,
|
||||
collectionIndex: Number,
|
||||
folder: Object,
|
||||
folderIndex: Number,
|
||||
collectionIndex: { type: Number, default: null },
|
||||
folder: { type: Object, default: () => {} },
|
||||
folderIndex: { type: Number, default: null },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
name: undefined,
|
||||
name: null,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@@ -56,7 +56,9 @@ export default {
|
||||
if (fb.currentUser !== null && fb.currentSettings[0]) {
|
||||
if (fb.currentSettings[0].value) {
|
||||
fb.writeCollections(
|
||||
JSON.parse(JSON.stringify(this.$store.state.postwoman.collectionsGraphql)),
|
||||
JSON.parse(
|
||||
JSON.stringify(this.$store.state.postwoman.collectionsGraphql)
|
||||
),
|
||||
"collectionsGraphql"
|
||||
)
|
||||
}
|
||||
@@ -70,10 +72,11 @@ export default {
|
||||
folderName: this.$props.folder.name,
|
||||
flag: "graphql",
|
||||
})
|
||||
this.hideModal()
|
||||
this.syncCollections()
|
||||
this.hideModal()
|
||||
},
|
||||
hideModal() {
|
||||
this.name = null
|
||||
this.$emit("hide-modal")
|
||||
},
|
||||
},
|
||||
|
||||
@@ -13,11 +13,11 @@
|
||||
<div slot="body" class="flex flex-col">
|
||||
<label for="selectLabel">{{ $t("label") }}</label>
|
||||
<input
|
||||
type="text"
|
||||
id="selectLabel"
|
||||
v-model="requestUpdateData.name"
|
||||
@keyup.enter="saveRequest"
|
||||
type="text"
|
||||
:placeholder="request.name"
|
||||
@keyup.enter="saveRequest"
|
||||
/>
|
||||
</div>
|
||||
<div slot="footer">
|
||||
@@ -42,16 +42,16 @@ import { fb } from "~/helpers/fb"
|
||||
export default {
|
||||
props: {
|
||||
show: Boolean,
|
||||
collectionIndex: Number,
|
||||
folderIndex: Number,
|
||||
folderName: String,
|
||||
request: Object,
|
||||
requestIndex: Number,
|
||||
collectionIndex: { type: Number, default: null },
|
||||
folderIndex: { type: Number, default: null },
|
||||
folderName: { type: String, default: null },
|
||||
request: { type: Object, default: () => {} },
|
||||
requestIndex: { type: Number, default: null },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
requestUpdateData: {
|
||||
name: undefined,
|
||||
name: null,
|
||||
},
|
||||
}
|
||||
},
|
||||
@@ -60,7 +60,9 @@ export default {
|
||||
if (fb.currentUser !== null && fb.currentSettings[0]) {
|
||||
if (fb.currentSettings[0].value) {
|
||||
fb.writeCollections(
|
||||
JSON.parse(JSON.stringify(this.$store.state.postwoman.collectionsGraphql)),
|
||||
JSON.parse(
|
||||
JSON.stringify(this.$store.state.postwoman.collectionsGraphql)
|
||||
),
|
||||
"collectionsGraphql"
|
||||
)
|
||||
}
|
||||
@@ -80,11 +82,11 @@ export default {
|
||||
requestIndex: this.$props.requestIndex,
|
||||
flag: "graphql",
|
||||
})
|
||||
|
||||
this.hideModal()
|
||||
this.syncCollections()
|
||||
this.hideModal()
|
||||
},
|
||||
hideModal() {
|
||||
this.requestUpdateData = { name: null }
|
||||
this.$emit("hide-modal")
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
<template>
|
||||
<div>
|
||||
<div
|
||||
:class="['row-wrapper transition duration-150 ease-in-out', { 'bg-bgDarkColor': dragging }]"
|
||||
:class="[
|
||||
'row-wrapper transition duration-150 ease-in-out',
|
||||
{ 'bg-bgDarkColor': dragging },
|
||||
]"
|
||||
@dragover.prevent
|
||||
@drop.prevent="dropEvent"
|
||||
@dragover="dragging = true"
|
||||
@@ -11,22 +14,26 @@
|
||||
>
|
||||
<div>
|
||||
<button class="icon" @click="toggleShowChildren">
|
||||
<i class="material-icons" v-show="!showChildren && !isFiltered">arrow_right</i>
|
||||
<i class="material-icons" v-show="showChildren || isFiltered">arrow_drop_down</i>
|
||||
<i v-show="!showChildren && !isFiltered" class="material-icons"
|
||||
>arrow_right</i
|
||||
>
|
||||
<i v-show="showChildren || isFiltered" class="material-icons"
|
||||
>arrow_drop_down</i
|
||||
>
|
||||
<i class="material-icons">folder_open</i>
|
||||
<span>{{ folder.name }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<v-popover>
|
||||
<button class="tooltip-target icon" v-tooltip.left="$t('more')">
|
||||
<button v-tooltip.left="$t('more')" class="tooltip-target icon">
|
||||
<i class="material-icons">more_vert</i>
|
||||
</button>
|
||||
<template slot="popover">
|
||||
<div>
|
||||
<button
|
||||
v-close-popover
|
||||
class="icon"
|
||||
@click="$emit('add-folder', { folder, path: folderPath })"
|
||||
v-close-popover
|
||||
>
|
||||
<i class="material-icons">create_new_folder</i>
|
||||
<span>{{ $t("new_folder") }}</span>
|
||||
@@ -34,16 +41,18 @@
|
||||
</div>
|
||||
<div>
|
||||
<button
|
||||
class="icon"
|
||||
@click="$emit('edit-folder', { folder, folderIndex, collectionIndex })"
|
||||
v-close-popover
|
||||
class="icon"
|
||||
@click="
|
||||
$emit('edit-folder', { folder, folderIndex, collectionIndex })
|
||||
"
|
||||
>
|
||||
<i class="material-icons">edit</i>
|
||||
<span>{{ $t("edit") }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<button class="icon" @click="confirmRemove = true" v-close-popover>
|
||||
<button v-close-popover class="icon" @click="confirmRemove = true">
|
||||
<i class="material-icons">delete</i>
|
||||
<span>{{ $t("delete") }}</span>
|
||||
</button>
|
||||
@@ -52,7 +61,7 @@
|
||||
</v-popover>
|
||||
</div>
|
||||
<div v-show="showChildren || isFiltered">
|
||||
<ul v-if="folder.folders && folder.folders.length" class="flex-col">
|
||||
<ul class="flex-col">
|
||||
<li
|
||||
v-for="(subFolder, subFolderIndex) in folder.folders"
|
||||
:key="subFolder.name"
|
||||
@@ -61,9 +70,10 @@
|
||||
<CollectionsGraphqlFolder
|
||||
:folder="subFolder"
|
||||
:folder-index="subFolderIndex"
|
||||
:folder-path="`${folderPath}/${subFolderIndex}`"
|
||||
:collection-index="collectionIndex"
|
||||
:doc="doc"
|
||||
:folder-path="`${folderPath}/${subFolderIndex}`"
|
||||
:is-filtered="isFiltered"
|
||||
@add-folder="$emit('add-folder', $event)"
|
||||
@edit-folder="$emit('edit-folder', $event)"
|
||||
@edit-request="$emit('edit-request', $event)"
|
||||
@@ -96,7 +106,10 @@
|
||||
"
|
||||
>
|
||||
<li class="flex ml-8 border-l border-brdColor">
|
||||
<p class="info"><i class="material-icons">not_interested</i> {{ $t("folder_empty") }}</p>
|
||||
<p class="info">
|
||||
<i class="material-icons">not_interested</i>
|
||||
{{ $t("folder_empty") }}
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -113,12 +126,12 @@
|
||||
import { fb } from "~/helpers/fb"
|
||||
|
||||
export default {
|
||||
name: "folder",
|
||||
name: "Folder",
|
||||
props: {
|
||||
folder: Object,
|
||||
folderIndex: Number,
|
||||
collectionIndex: Number,
|
||||
folderPath: String,
|
||||
folder: { type: Object, default: () => {} },
|
||||
folderIndex: { type: Number, default: null },
|
||||
collectionIndex: { type: Number, default: null },
|
||||
folderPath: { type: String, default: null },
|
||||
doc: Boolean,
|
||||
isFiltered: Boolean,
|
||||
},
|
||||
@@ -134,7 +147,9 @@ export default {
|
||||
if (fb.currentUser !== null && fb.currentSettings[0]) {
|
||||
if (fb.currentSettings[0].value) {
|
||||
fb.writeCollections(
|
||||
JSON.parse(JSON.stringify(this.$store.state.postwoman.collectionsGraphql)),
|
||||
JSON.parse(
|
||||
JSON.stringify(this.$store.state.postwoman.collectionsGraphql)
|
||||
),
|
||||
"collectionsGraphql"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -5,12 +5,16 @@
|
||||
<h3 class="title">{{ $t("import_export") }} {{ $t("collections") }}</h3>
|
||||
<div>
|
||||
<v-popover>
|
||||
<button class="tooltip-target icon" v-tooltip.left="$t('more')">
|
||||
<button v-tooltip.left="$t('more')" class="tooltip-target icon">
|
||||
<i class="material-icons">more_vert</i>
|
||||
</button>
|
||||
<template slot="popover">
|
||||
<div>
|
||||
<button class="icon" @click="readCollectionGist" v-close-popover>
|
||||
<button
|
||||
v-close-popover
|
||||
class="icon"
|
||||
@click="readCollectionGist"
|
||||
>
|
||||
<i class="material-icons">assignment_returned</i>
|
||||
<span>{{ $t("import_from_gist") }}</span>
|
||||
</button>
|
||||
@@ -25,12 +29,16 @@
|
||||
}"
|
||||
>
|
||||
<button
|
||||
v-close-popover
|
||||
:disabled="
|
||||
!fb.currentUser ? true : fb.currentUser.provider !== 'github.com' ? true : false
|
||||
!fb.currentUser
|
||||
? true
|
||||
: fb.currentUser.provider !== 'github.com'
|
||||
? true
|
||||
: false
|
||||
"
|
||||
class="icon"
|
||||
@click="createCollectionGist"
|
||||
v-close-popover
|
||||
>
|
||||
<i class="material-icons">assignment_turned_in</i>
|
||||
<span>{{ $t("create_secret_gist") }}</span>
|
||||
@@ -48,42 +56,48 @@
|
||||
<div class="flex flex-col items-start p-2">
|
||||
<span
|
||||
v-tooltip="{
|
||||
content: !fb.currentUser ? $t('login_first') : $t('replace_current'),
|
||||
content: !fb.currentUser
|
||||
? $t('login_first')
|
||||
: $t('replace_current'),
|
||||
}"
|
||||
>
|
||||
<button :disabled="!fb.currentUser" class="icon" @click="syncCollections">
|
||||
<button
|
||||
:disabled="!fb.currentUser"
|
||||
class="icon"
|
||||
@click="syncCollections"
|
||||
>
|
||||
<i class="material-icons">folder_shared</i>
|
||||
<span>{{ $t("import_from_sync") }}</span>
|
||||
</button>
|
||||
</span>
|
||||
<button
|
||||
v-tooltip="$t('replace_current')"
|
||||
class="icon"
|
||||
@click="openDialogChooseFileToReplaceWith"
|
||||
v-tooltip="$t('replace_current')"
|
||||
>
|
||||
<i class="material-icons">create_new_folder</i>
|
||||
<span>{{ $t("replace_json") }}</span>
|
||||
<input
|
||||
type="file"
|
||||
@change="replaceWithJSON"
|
||||
style="display: none"
|
||||
ref="inputChooseFileToReplaceWith"
|
||||
type="file"
|
||||
style="display: none"
|
||||
accept="application/json"
|
||||
@change="replaceWithJSON"
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
v-tooltip="$t('preserve_current')"
|
||||
class="icon"
|
||||
@click="openDialogChooseFileToImportFrom"
|
||||
v-tooltip="$t('preserve_current')"
|
||||
>
|
||||
<i class="material-icons">folder_special</i>
|
||||
<span>{{ $t("import_json") }}</span>
|
||||
<input
|
||||
type="file"
|
||||
@change="importFromJSON"
|
||||
style="display: none"
|
||||
ref="inputChooseFileToImportFrom"
|
||||
type="file"
|
||||
style="display: none"
|
||||
accept="application/json"
|
||||
@change="importFromJSON"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
@@ -102,7 +116,11 @@
|
||||
<button class="icon" @click="hideModal">
|
||||
{{ $t("cancel") }}
|
||||
</button>
|
||||
<button class="icon primary" @click="exportJSON" v-tooltip="$t('download_file')">
|
||||
<button
|
||||
v-tooltip="$t('download_file')"
|
||||
class="icon primary"
|
||||
@click="exportJSON"
|
||||
>
|
||||
{{ $t("export") }}
|
||||
</button>
|
||||
</span>
|
||||
@@ -115,18 +133,22 @@
|
||||
import { fb } from "~/helpers/fb"
|
||||
|
||||
export default {
|
||||
props: {
|
||||
show: Boolean,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
fb,
|
||||
showJsonCode: false,
|
||||
}
|
||||
},
|
||||
props: {
|
||||
show: Boolean,
|
||||
},
|
||||
computed: {
|
||||
collectionJson() {
|
||||
return JSON.stringify(this.$store.state.postwoman.collectionsGraphql, null, 2)
|
||||
return JSON.stringify(
|
||||
this.$store.state.postwoman.collectionsGraphql,
|
||||
null,
|
||||
2
|
||||
)
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
@@ -148,11 +170,11 @@ export default {
|
||||
},
|
||||
}
|
||||
)
|
||||
.then(({ html_url }) => {
|
||||
.then((res) => {
|
||||
this.$toast.success(this.$t("gist_created"), {
|
||||
icon: "done",
|
||||
})
|
||||
window.open(html_url)
|
||||
window.open(res.html_url)
|
||||
})
|
||||
.catch((error) => {
|
||||
this.$toast.error(this.$t("something_went_wrong"), {
|
||||
@@ -162,7 +184,7 @@ export default {
|
||||
})
|
||||
},
|
||||
async readCollectionGist() {
|
||||
let gist = prompt(this.$t("enter_gist_url"))
|
||||
const gist = prompt(this.$t("enter_gist_url"))
|
||||
if (!gist) return
|
||||
await this.$axios
|
||||
.$get(`https://api.github.com/gists/${gist.split("/").pop()}`, {
|
||||
@@ -171,8 +193,11 @@ export default {
|
||||
},
|
||||
})
|
||||
.then(({ files }) => {
|
||||
let collections = JSON.parse(Object.values(files)[0].content)
|
||||
this.$store.commit("postwoman/replaceCollections", { data: collections, flag: "graphql" })
|
||||
const collections = JSON.parse(Object.values(files)[0].content)
|
||||
this.$store.commit("postwoman/replaceCollections", {
|
||||
data: collections,
|
||||
flag: "graphql",
|
||||
})
|
||||
this.fileImported()
|
||||
this.syncToFBCollections()
|
||||
})
|
||||
@@ -191,22 +216,32 @@ export default {
|
||||
this.$refs.inputChooseFileToImportFrom.click()
|
||||
},
|
||||
replaceWithJSON() {
|
||||
let reader = new FileReader()
|
||||
const reader = new FileReader()
|
||||
reader.onload = ({ target }) => {
|
||||
let content = target.result
|
||||
const content = target.result
|
||||
let collections = JSON.parse(content)
|
||||
if (collections[0]) {
|
||||
let [name, folders, requests] = Object.keys(collections[0])
|
||||
if (name === "name" && folders === "folders" && requests === "requests") {
|
||||
const [name, folders, requests] = Object.keys(collections[0])
|
||||
if (
|
||||
name === "name" &&
|
||||
folders === "folders" &&
|
||||
requests === "requests"
|
||||
) {
|
||||
// Do nothing
|
||||
}
|
||||
} else if (collections.info && collections.info.schema.includes("v2.1.0")) {
|
||||
} else if (
|
||||
collections.info &&
|
||||
collections.info.schema.includes("v2.1.0")
|
||||
) {
|
||||
collections = [this.parsePostmanCollection(collections)]
|
||||
} else {
|
||||
this.failedImport()
|
||||
return
|
||||
}
|
||||
this.$store.commit("postwoman/replaceCollections", { data: collections, flag: "graphql" })
|
||||
this.$store.commit("postwoman/replaceCollections", {
|
||||
data: collections,
|
||||
flag: "graphql",
|
||||
})
|
||||
this.fileImported()
|
||||
this.syncToFBCollections()
|
||||
}
|
||||
@@ -214,24 +249,36 @@ export default {
|
||||
this.$refs.inputChooseFileToReplaceWith.value = ""
|
||||
},
|
||||
importFromJSON() {
|
||||
let reader = new FileReader()
|
||||
const reader = new FileReader()
|
||||
reader.onload = ({ target }) => {
|
||||
let content = target.result
|
||||
const content = target.result
|
||||
let collections = JSON.parse(content)
|
||||
if (collections[0]) {
|
||||
let [name, folders, requests] = Object.keys(collections[0])
|
||||
if (name === "name" && folders === "folders" && requests === "requests") {
|
||||
const [name, folders, requests] = Object.keys(collections[0])
|
||||
if (
|
||||
name === "name" &&
|
||||
folders === "folders" &&
|
||||
requests === "requests"
|
||||
) {
|
||||
// Do nothing
|
||||
}
|
||||
} else if (collections.info && collections.info.schema.includes("v2.1.0")) {
|
||||
//replace the variables, postman uses {{var}}, Hoppscotch uses <<var>>
|
||||
collections = JSON.parse(content.replaceAll(/{{([a-z]+)}}/gi, "<<$1>>"))
|
||||
} else if (
|
||||
collections.info &&
|
||||
collections.info.schema.includes("v2.1.0")
|
||||
) {
|
||||
// replace the variables, postman uses {{var}}, Hoppscotch uses <<var>>
|
||||
collections = JSON.parse(
|
||||
content.replaceAll(/{{([a-z]+)}}/gi, "<<$1>>")
|
||||
)
|
||||
collections = [this.parsePostmanCollection(collections)]
|
||||
} else {
|
||||
this.failedImport()
|
||||
return
|
||||
}
|
||||
this.$store.commit("postwoman/importCollections", { data: collections, flag: "graphql" })
|
||||
this.$store.commit("postwoman/importCollections", {
|
||||
data: collections,
|
||||
flag: "graphql",
|
||||
})
|
||||
this.fileImported()
|
||||
this.syncToFBCollections()
|
||||
}
|
||||
@@ -241,10 +288,10 @@ export default {
|
||||
exportJSON() {
|
||||
let text = this.collectionJson
|
||||
text = text.replace(/\n/g, "\r\n")
|
||||
let blob = new Blob([text], {
|
||||
const blob = new Blob([text], {
|
||||
type: "text/json",
|
||||
})
|
||||
let anchor = document.createElement("a")
|
||||
const anchor = document.createElement("a")
|
||||
anchor.download = "hoppscotch-collection.json"
|
||||
anchor.href = window.URL.createObjectURL(blob)
|
||||
anchor.target = "_blank"
|
||||
@@ -267,7 +314,9 @@ export default {
|
||||
if (fb.currentUser !== null && fb.currentSettings[0]) {
|
||||
if (fb.currentSettings[0].value) {
|
||||
fb.writeCollections(
|
||||
JSON.parse(JSON.stringify(this.$store.state.postwoman.collectionsGraphql)),
|
||||
JSON.parse(
|
||||
JSON.stringify(this.$store.state.postwoman.collectionsGraphql)
|
||||
),
|
||||
"collectionsGraphql"
|
||||
)
|
||||
}
|
||||
@@ -284,7 +333,7 @@ export default {
|
||||
})
|
||||
},
|
||||
parsePostmanCollection({ info, name, item }) {
|
||||
let postwomanCollection = {
|
||||
const postwomanCollection = {
|
||||
name: "",
|
||||
folders: [],
|
||||
requests: [],
|
||||
@@ -293,26 +342,39 @@ export default {
|
||||
postwomanCollection.name = info ? info.name : name
|
||||
|
||||
if (item && item.length > 0) {
|
||||
for (let collectionItem of item) {
|
||||
for (const collectionItem of item) {
|
||||
if (collectionItem.request) {
|
||||
if (postwomanCollection.hasOwnProperty("folders")) {
|
||||
if (
|
||||
Object.prototype.hasOwnProperty.call(
|
||||
postwomanCollection,
|
||||
"folders"
|
||||
)
|
||||
) {
|
||||
postwomanCollection.name = info ? info.name : name
|
||||
postwomanCollection.requests.push(this.parsePostmanRequest(collectionItem))
|
||||
postwomanCollection.requests.push(
|
||||
this.parsePostmanRequest(collectionItem)
|
||||
)
|
||||
} else {
|
||||
postwomanCollection.name = name ? name : ""
|
||||
postwomanCollection.requests.push(this.parsePostmanRequest(collectionItem))
|
||||
postwomanCollection.name = name || ""
|
||||
postwomanCollection.requests.push(
|
||||
this.parsePostmanRequest(collectionItem)
|
||||
)
|
||||
}
|
||||
} else if (this.hasFolder(collectionItem)) {
|
||||
postwomanCollection.folders.push(this.parsePostmanCollection(collectionItem))
|
||||
postwomanCollection.folders.push(
|
||||
this.parsePostmanCollection(collectionItem)
|
||||
)
|
||||
} else {
|
||||
postwomanCollection.requests.push(this.parsePostmanRequest(collectionItem))
|
||||
postwomanCollection.requests.push(
|
||||
this.parsePostmanRequest(collectionItem)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
return postwomanCollection
|
||||
},
|
||||
parsePostmanRequest({ name, request }) {
|
||||
let pwRequest = {
|
||||
const pwRequest = {
|
||||
url: "",
|
||||
path: "",
|
||||
method: "",
|
||||
@@ -332,20 +394,26 @@ export default {
|
||||
}
|
||||
|
||||
pwRequest.name = name
|
||||
let requestObjectUrl = request.url.raw.match(/^(.+:\/\/[^\/]+|{[^\/]+})(\/[^\?]+|).*$/)
|
||||
const requestObjectUrl = request.url.raw.match(
|
||||
/^(.+:\/\/[^/]+|{[^/]+})(\/[^?]+|).*$/
|
||||
)
|
||||
if (requestObjectUrl) {
|
||||
pwRequest.url = requestObjectUrl[1]
|
||||
pwRequest.path = requestObjectUrl[2] ? requestObjectUrl[2] : ""
|
||||
}
|
||||
pwRequest.method = request.method
|
||||
let itemAuth = request.auth ? request.auth : ""
|
||||
let authType = itemAuth ? itemAuth.type : ""
|
||||
const itemAuth = request.auth ? request.auth : ""
|
||||
const authType = itemAuth ? itemAuth.type : ""
|
||||
if (authType === "basic") {
|
||||
pwRequest.auth = "Basic Auth"
|
||||
pwRequest.httpUser =
|
||||
itemAuth.basic[0].key === "username" ? itemAuth.basic[0].value : itemAuth.basic[1].value
|
||||
itemAuth.basic[0].key === "username"
|
||||
? itemAuth.basic[0].value
|
||||
: itemAuth.basic[1].value
|
||||
pwRequest.httpPassword =
|
||||
itemAuth.basic[0].key === "password" ? itemAuth.basic[0].value : itemAuth.basic[1].value
|
||||
itemAuth.basic[0].key === "password"
|
||||
? itemAuth.basic[0].value
|
||||
: itemAuth.basic[1].value
|
||||
} else if (authType === "oauth2") {
|
||||
pwRequest.auth = "OAuth 2.0"
|
||||
pwRequest.bearerToken =
|
||||
@@ -356,26 +424,26 @@ export default {
|
||||
pwRequest.auth = "Bearer Token"
|
||||
pwRequest.bearerToken = itemAuth.bearer[0].value
|
||||
}
|
||||
let requestObjectHeaders = request.header
|
||||
const requestObjectHeaders = request.header
|
||||
if (requestObjectHeaders) {
|
||||
pwRequest.headers = requestObjectHeaders
|
||||
for (let header of pwRequest.headers) {
|
||||
for (const header of pwRequest.headers) {
|
||||
delete header.name
|
||||
delete header.type
|
||||
}
|
||||
}
|
||||
let requestObjectParams = request.url.query
|
||||
const requestObjectParams = request.url.query
|
||||
if (requestObjectParams) {
|
||||
pwRequest.params = requestObjectParams
|
||||
for (let param of pwRequest.params) {
|
||||
for (const param of pwRequest.params) {
|
||||
delete param.disabled
|
||||
}
|
||||
}
|
||||
if (request.body) {
|
||||
if (request.body.mode === "urlencoded") {
|
||||
let params = request.body.urlencoded
|
||||
pwRequest.bodyParams = params ? params : []
|
||||
for (let param of pwRequest.bodyParams) {
|
||||
const params = request.body.urlencoded
|
||||
pwRequest.bodyParams = params || []
|
||||
for (const param of pwRequest.bodyParams) {
|
||||
delete param.type
|
||||
}
|
||||
} else if (request.body.mode === "raw") {
|
||||
@@ -386,7 +454,7 @@ export default {
|
||||
return pwRequest
|
||||
},
|
||||
hasFolder(item) {
|
||||
return item.hasOwnProperty("item")
|
||||
return Object.prototype.hasOwnProperty.call(item, "item")
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
<template>
|
||||
<div>
|
||||
<div
|
||||
:class="['row-wrapper transition duration-150 ease-in-out', { 'bg-bgDarkColor': dragging }]"
|
||||
:class="[
|
||||
'row-wrapper transition duration-150 ease-in-out',
|
||||
{ 'bg-bgDarkColor': dragging },
|
||||
]"
|
||||
draggable="true"
|
||||
@dragstart="dragStart"
|
||||
@dragover.stop
|
||||
@@ -10,20 +13,22 @@
|
||||
>
|
||||
<div>
|
||||
<button
|
||||
v-tooltip="!doc ? $t('use_request') : ''"
|
||||
class="icon"
|
||||
@click="!doc ? selectRequest() : {}"
|
||||
v-tooltip="!doc ? $t('use_request') : ''"
|
||||
>
|
||||
<i class="material-icons">description</i>
|
||||
<span>{{ request.name }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<v-popover>
|
||||
<button class="tooltip-target icon" v-tooltip="$t('more')">
|
||||
<button v-tooltip="$t('more')" class="tooltip-target icon">
|
||||
<i class="material-icons">more_vert</i>
|
||||
</button>
|
||||
<template slot="popover">
|
||||
<div>
|
||||
<button
|
||||
v-close-popover
|
||||
class="icon"
|
||||
@click="
|
||||
$emit('edit-request', {
|
||||
@@ -34,14 +39,13 @@
|
||||
requestIndex,
|
||||
})
|
||||
"
|
||||
v-close-popover
|
||||
>
|
||||
<i class="material-icons">edit</i>
|
||||
<span>{{ $t("edit") }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<button class="icon" @click="confirmRemove = true" v-close-popover>
|
||||
<button v-close-popover class="icon" @click="confirmRemove = true">
|
||||
<i class="material-icons">delete</i>
|
||||
<span>{{ $t("delete") }}</span>
|
||||
</button>
|
||||
@@ -63,11 +67,11 @@ import { fb } from "~/helpers/fb"
|
||||
|
||||
export default {
|
||||
props: {
|
||||
request: Object,
|
||||
collectionIndex: Number,
|
||||
folderIndex: Number,
|
||||
folderName: String,
|
||||
requestIndex: Number,
|
||||
request: { type: Object, default: () => {} },
|
||||
collectionIndex: { type: Number, default: null },
|
||||
folderIndex: { type: Number, default: null },
|
||||
folderName: { type: String, default: null },
|
||||
requestIndex: { type: Number, default: null },
|
||||
doc: Boolean,
|
||||
},
|
||||
data() {
|
||||
@@ -81,14 +85,18 @@ export default {
|
||||
if (fb.currentUser !== null && fb.currentSettings[0]) {
|
||||
if (fb.currentSettings[0].value) {
|
||||
fb.writeCollections(
|
||||
JSON.parse(JSON.stringify(this.$store.state.postwoman.collectionsGraphql)),
|
||||
JSON.parse(
|
||||
JSON.stringify(this.$store.state.postwoman.collectionsGraphql)
|
||||
),
|
||||
"collectionsGraphql"
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
selectRequest() {
|
||||
this.$store.commit("postwoman/selectGraphqlRequest", { request: this.request })
|
||||
this.$store.commit("postwoman/selectGraphqlRequest", {
|
||||
request: this.request,
|
||||
})
|
||||
},
|
||||
dragStart({ dataTransfer }) {
|
||||
this.dragging = !this.dragging
|
||||
|
||||
@@ -12,17 +12,33 @@
|
||||
</div>
|
||||
<div slot="body" class="flex flex-col">
|
||||
<label for="selectLabel">{{ $t("token_req_name") }}</label>
|
||||
<input type="text" id="selectLabel" v-model="requestData.name" @keyup.enter="saveRequestAs" />
|
||||
<input
|
||||
id="selectLabel"
|
||||
v-model="requestData.name"
|
||||
type="text"
|
||||
@keyup.enter="saveRequestAs"
|
||||
/>
|
||||
<ul>
|
||||
<li>
|
||||
<label for="selectCollection">{{ $t("collection") }}</label>
|
||||
<span class="select-wrapper">
|
||||
<select type="text" id="selectCollection" v-model="requestData.collectionIndex">
|
||||
<option :key="undefined" :value="undefined" hidden disabled selected>
|
||||
<select
|
||||
id="selectCollection"
|
||||
v-model="requestData.collectionIndex"
|
||||
type="text"
|
||||
>
|
||||
<option
|
||||
:key="undefined"
|
||||
:value="undefined"
|
||||
hidden
|
||||
disabled
|
||||
selected
|
||||
>
|
||||
{{ $t("select_collection") }}
|
||||
</option>
|
||||
<option
|
||||
v-for="(collection, index) in $store.state.postwoman.collectionsGraphql"
|
||||
v-for="(collection, index) in $store.state.postwoman
|
||||
.collectionsGraphql"
|
||||
:key="index"
|
||||
:value="index"
|
||||
>
|
||||
@@ -34,18 +50,26 @@
|
||||
</ul>
|
||||
<label>{{ $t("folder") }}</label>
|
||||
<SmartAutoComplete
|
||||
v-model="requestData.folderName"
|
||||
:placeholder="$t('search')"
|
||||
:source="folders"
|
||||
:spellcheck="false"
|
||||
v-model="requestData.folderName"
|
||||
/>
|
||||
<ul>
|
||||
<li>
|
||||
<label for="selectRequest">{{ $t("request") }}</label>
|
||||
<span class="select-wrapper">
|
||||
<select type="text" id="selectRequest" v-model="requestData.requestIndex">
|
||||
<select
|
||||
id="selectRequest"
|
||||
v-model="requestData.requestIndex"
|
||||
type="text"
|
||||
>
|
||||
<option :key="undefined" :value="undefined">/</option>
|
||||
<option v-for="(folder, index) in requests" :key="index" :value="index">
|
||||
<option
|
||||
v-for="(folder, index) in requests"
|
||||
:key="index"
|
||||
:value="index"
|
||||
>
|
||||
{{ folder.name }}
|
||||
</option>
|
||||
</select>
|
||||
@@ -75,7 +99,7 @@ import { fb } from "~/helpers/fb"
|
||||
export default {
|
||||
props: {
|
||||
show: Boolean,
|
||||
editingRequest: Object,
|
||||
editingRequest: { type: Object, default: () => {} },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -88,20 +112,6 @@ export default {
|
||||
},
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
"requestData.collectionIndex": function resetFolderAndRequestIndex() {
|
||||
// if user has chosen some folder, than selected other collection, which doesn't have any folders
|
||||
// than `requestUpdateData.folderName` won't be reseted
|
||||
this.$data.requestData.folderName = undefined
|
||||
this.$data.requestData.requestIndex = undefined
|
||||
},
|
||||
"requestData.folderName": function resetRequestIndex() {
|
||||
this.$data.requestData.requestIndex = undefined
|
||||
},
|
||||
editingRequest({ name }) {
|
||||
this.$data.requestData.name = name || this.$data.defaultRequestName
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
folders() {
|
||||
const collections = this.$store.state.postwoman.collectionsGraphql
|
||||
@@ -124,7 +134,8 @@ export default {
|
||||
return []
|
||||
}
|
||||
|
||||
const userSelectedAnyFolder = folderName !== undefined && folderName !== ""
|
||||
const userSelectedAnyFolder =
|
||||
folderName !== undefined && folderName !== ""
|
||||
|
||||
if (userSelectedAnyFolder) {
|
||||
const collection = collections[collectionIndex]
|
||||
@@ -142,19 +153,36 @@ export default {
|
||||
}
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
"requestData.collectionIndex": function resetFolderAndRequestIndex() {
|
||||
// if user has chosen some folder, than selected other collection, which doesn't have any folders
|
||||
// than `requestUpdateData.folderName` won't be reseted
|
||||
this.$data.requestData.folderName = undefined
|
||||
this.$data.requestData.requestIndex = undefined
|
||||
},
|
||||
"requestData.folderName": function resetRequestIndex() {
|
||||
this.$data.requestData.requestIndex = undefined
|
||||
},
|
||||
editingRequest({ name }) {
|
||||
this.$data.requestData.name = name || this.$data.defaultRequestName
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
syncCollections() {
|
||||
if (fb.currentUser !== null && fb.currentSettings[0]) {
|
||||
if (fb.currentSettings[0].value) {
|
||||
fb.writeCollections(
|
||||
JSON.parse(JSON.stringify(this.$store.state.postwoman.collectionsGraphql)),
|
||||
JSON.parse(
|
||||
JSON.stringify(this.$store.state.postwoman.collectionsGraphql)
|
||||
),
|
||||
"collectionsGraphql"
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
saveRequestAs() {
|
||||
const userDidntSpecifyCollection = this.$data.requestData.collectionIndex === undefined
|
||||
const userDidntSpecifyCollection =
|
||||
this.$data.requestData.collectionIndex === undefined
|
||||
if (userDidntSpecifyCollection) {
|
||||
this.$toast.error(this.$t("select_collection"), {
|
||||
icon: "error",
|
||||
|
||||
@@ -1,15 +1,23 @@
|
||||
<template>
|
||||
<AppSection class="yellow" :label="$t('collections')" ref="collections" no-legend>
|
||||
<AppSection
|
||||
ref="collections"
|
||||
class="yellow"
|
||||
:label="$t('collections')"
|
||||
no-legend
|
||||
>
|
||||
<div class="show-on-large-screen">
|
||||
<input
|
||||
v-model="filterText"
|
||||
aria-label="Search"
|
||||
type="search"
|
||||
:placeholder="$t('search')"
|
||||
v-model="filterText"
|
||||
class="rounded-t-lg"
|
||||
/>
|
||||
</div>
|
||||
<CollectionsGraphqlAdd :show="showModalAdd" @hide-modal="displayModalAdd(false)" />
|
||||
<CollectionsGraphqlAdd
|
||||
:show="showModalAdd"
|
||||
@hide-modal="displayModalAdd(false)"
|
||||
/>
|
||||
<CollectionsGraphqlEdit
|
||||
:show="showModalEdit"
|
||||
:editing-collection="editingCollection"
|
||||
@@ -53,17 +61,21 @@
|
||||
</button>
|
||||
</div>
|
||||
<p v-if="collections.length === 0" class="info">
|
||||
<i class="material-icons">help_outline</i> {{ $t("create_new_collection") }}
|
||||
<i class="material-icons">help_outline</i>
|
||||
{{ $t("create_new_collection") }}
|
||||
</p>
|
||||
<div class="virtual-list">
|
||||
<ul class="flex-col">
|
||||
<li v-for="(collection, index) in filteredCollections" :key="collection.name">
|
||||
<li
|
||||
v-for="(collection, index) in filteredCollections"
|
||||
:key="collection.name"
|
||||
>
|
||||
<CollectionsGraphqlCollection
|
||||
:name="collection.name"
|
||||
:collection-index="index"
|
||||
:collection="collection"
|
||||
:doc="doc"
|
||||
:isFiltered="filterText.length > 0"
|
||||
:is-filtered="filterText.length > 0"
|
||||
@edit-collection="editCollection(collection, index)"
|
||||
@add-folder="addFolder($event)"
|
||||
@edit-folder="editFolder($event)"
|
||||
@@ -74,17 +86,13 @@
|
||||
</ul>
|
||||
</div>
|
||||
<p v-if="filterText && filteredCollections.length === 0" class="info">
|
||||
<i class="material-icons">not_interested</i> {{ $t("nothing_found") }} "{{ filterText }}"
|
||||
<i class="material-icons">not_interested</i> {{ $t("nothing_found") }} "{{
|
||||
filterText
|
||||
}}"
|
||||
</p>
|
||||
</AppSection>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.virtual-list {
|
||||
max-height: calc(100vh - 270px);
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import { fb } from "~/helpers/fb"
|
||||
|
||||
@@ -128,15 +136,16 @@ export default {
|
||||
const filterText = this.filterText.toLowerCase()
|
||||
const filteredCollections = []
|
||||
|
||||
for (let collection of collections) {
|
||||
for (const collection of collections) {
|
||||
const filteredRequests = []
|
||||
const filteredFolders = []
|
||||
for (let request of collection.requests) {
|
||||
if (request.name.toLowerCase().includes(filterText)) filteredRequests.push(request)
|
||||
for (const request of collection.requests) {
|
||||
if (request.name.toLowerCase().includes(filterText))
|
||||
filteredRequests.push(request)
|
||||
}
|
||||
for (let folder of collection.folders) {
|
||||
for (const folder of collection.folders) {
|
||||
const filteredFolderRequests = []
|
||||
for (let request of folder.requests) {
|
||||
for (const request of folder.requests) {
|
||||
if (request.name.toLowerCase().includes(filterText))
|
||||
filteredFolderRequests.push(request)
|
||||
}
|
||||
@@ -158,15 +167,24 @@ export default {
|
||||
return filteredCollections
|
||||
},
|
||||
},
|
||||
async mounted() {
|
||||
mounted() {
|
||||
this._keyListener = function (e) {
|
||||
if (e.key === "Escape") {
|
||||
e.preventDefault()
|
||||
this.showModalAdd = this.showModalEdit = this.showModalImportExport = this.showModalAddFolder = this.showModalEditFolder = this.showModalEditRequest = false
|
||||
this.showModalAdd =
|
||||
this.showModalEdit =
|
||||
this.showModalImportExport =
|
||||
this.showModalAddFolder =
|
||||
this.showModalEditFolder =
|
||||
this.showModalEditRequest =
|
||||
false
|
||||
}
|
||||
}
|
||||
document.addEventListener("keydown", this._keyListener.bind(this))
|
||||
},
|
||||
beforeDestroy() {
|
||||
document.removeEventListener("keydown", this._keyListener)
|
||||
},
|
||||
methods: {
|
||||
displayModalAdd(shouldDisplay) {
|
||||
this.showModalAdd = shouldDisplay
|
||||
@@ -226,7 +244,13 @@ export default {
|
||||
this.syncCollections()
|
||||
},
|
||||
editRequest(payload) {
|
||||
const { collectionIndex, folderIndex, folderName, request, requestIndex } = payload
|
||||
const {
|
||||
collectionIndex,
|
||||
folderIndex,
|
||||
folderName,
|
||||
request,
|
||||
requestIndex,
|
||||
} = payload
|
||||
this.$data.editingCollectionIndex = collectionIndex
|
||||
this.$data.editingFolderIndex = folderIndex
|
||||
this.$data.editingFolderName = folderName
|
||||
@@ -247,15 +271,20 @@ export default {
|
||||
if (fb.currentUser !== null && fb.currentSettings[0]) {
|
||||
if (fb.currentSettings[0].value) {
|
||||
fb.writeCollections(
|
||||
JSON.parse(JSON.stringify(this.$store.state.postwoman.collectionsGraphql)),
|
||||
JSON.parse(
|
||||
JSON.stringify(this.$store.state.postwoman.collectionsGraphql)
|
||||
),
|
||||
"collectionsGraphql"
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
beforeDestroy() {
|
||||
document.removeEventListener("keydown", this._keyListener)
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.virtual-list {
|
||||
max-height: calc(100vh - 270px);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,20 +1,32 @@
|
||||
<template>
|
||||
<AppSection :label="$t('collections')" ref="collections" no-legend>
|
||||
<AppSection ref="collections" :label="$t('collections')" no-legend>
|
||||
<div class="show-on-large-screen">
|
||||
<input
|
||||
v-if="!saveRequest"
|
||||
v-model="filterText"
|
||||
aria-label="Search"
|
||||
type="search"
|
||||
:placeholder="$t('search')"
|
||||
v-model="filterText"
|
||||
class="rounded-t-lg"
|
||||
/>
|
||||
</div>
|
||||
<CollectionsAdd :show="showModalAdd" @hide-modal="displayModalAdd(false)" />
|
||||
<CollectionsChooseType
|
||||
:collections-type="collectionsType"
|
||||
:show="showTeamCollections"
|
||||
:doc="doc"
|
||||
@update-collection-type="updateCollectionType"
|
||||
@update-selected-team="updateSelectedTeam"
|
||||
/>
|
||||
<CollectionsAdd
|
||||
:show="showModalAdd"
|
||||
@submit="addNewRootCollection"
|
||||
@hide-modal="displayModalAdd(false)"
|
||||
/>
|
||||
<CollectionsEdit
|
||||
:show="showModalEdit"
|
||||
:editing-collection="editingCollection"
|
||||
:editing-collection-index="editingCollectionIndex"
|
||||
:editing-coll-name="editingCollection ? editingCollection.name : ''"
|
||||
@hide-modal="displayModalEdit(false)"
|
||||
@submit="updateEditingCollection"
|
||||
/>
|
||||
<CollectionsAddFolder
|
||||
:show="showModalAddFolder"
|
||||
@@ -25,73 +37,120 @@
|
||||
/>
|
||||
<CollectionsEditFolder
|
||||
:show="showModalEditFolder"
|
||||
:collection-index="editingCollectionIndex"
|
||||
:folder="editingFolder"
|
||||
:folder-index="editingFolderIndex"
|
||||
@submit="updateEditingFolder"
|
||||
@hide-modal="displayModalEditFolder(false)"
|
||||
/>
|
||||
<CollectionsEditRequest
|
||||
:show="showModalEditRequest"
|
||||
:collection-index="editingCollectionIndex"
|
||||
:folder-index="editingFolderIndex"
|
||||
:folder-name="editingFolderName"
|
||||
:request="editingRequest"
|
||||
:request-index="editingRequestIndex"
|
||||
:placeholder-req-name="editingRequest ? editingRequest.name : ''"
|
||||
@submit="updateEditingRequest"
|
||||
@hide-modal="displayModalEditRequest(false)"
|
||||
/>
|
||||
<CollectionsImportExport
|
||||
:show="showModalImportExport"
|
||||
:collections-type="collectionsType"
|
||||
@hide-modal="displayModalImportExport(false)"
|
||||
@update-team-collections="updateTeamCollections"
|
||||
/>
|
||||
<div class="border-b row-wrapper border-brdColor">
|
||||
<button class="icon" @click="displayModalAdd(true)">
|
||||
<button
|
||||
v-if="
|
||||
collectionsType.type == 'team-collections' &&
|
||||
(collectionsType.selectedTeam == undefined ||
|
||||
collectionsType.selectedTeam.myRole == 'VIEWER') &&
|
||||
!saveRequest
|
||||
"
|
||||
class="icon"
|
||||
disabled
|
||||
@click="displayModalAdd(true)"
|
||||
>
|
||||
<i class="material-icons">add</i>
|
||||
<div v-tooltip.left="$t('disable_new_collection')">
|
||||
<span>{{ $t("new") }}</span>
|
||||
</div>
|
||||
</button>
|
||||
<button
|
||||
v-else-if="!saveRequest"
|
||||
class="icon"
|
||||
@click="displayModalAdd(true)"
|
||||
>
|
||||
<i class="material-icons">add</i>
|
||||
<span>{{ $t("new") }}</span>
|
||||
</button>
|
||||
<button class="icon" @click="displayModalImportExport(true)">
|
||||
<button
|
||||
v-if="!saveRequest"
|
||||
:disabled="
|
||||
collectionsType.type == 'team-collections' &&
|
||||
collectionsType.selectedTeam == undefined
|
||||
"
|
||||
class="icon"
|
||||
@click="displayModalImportExport(true)"
|
||||
>
|
||||
{{ $t("import_export") }}
|
||||
</button>
|
||||
</div>
|
||||
<p v-if="collections.length === 0" class="info">
|
||||
<i class="material-icons">help_outline</i> {{ $t("create_new_collection") }}
|
||||
<i class="material-icons">help_outline</i>
|
||||
{{ $t("create_new_collection") }}
|
||||
</p>
|
||||
<div class="virtual-list">
|
||||
<ul class="flex-col">
|
||||
<li v-for="(collection, index) in filteredCollections" :key="collection.name">
|
||||
<CollectionsCollection
|
||||
<li
|
||||
v-for="(collection, index) in filteredCollections"
|
||||
:key="collection.name"
|
||||
>
|
||||
<component
|
||||
:is="
|
||||
collectionsType.type == 'my-collections'
|
||||
? 'CollectionsMyCollection'
|
||||
: 'CollectionsTeamsCollection'
|
||||
"
|
||||
:name="collection.name"
|
||||
:collection-index="index"
|
||||
:collection="collection"
|
||||
:doc="doc"
|
||||
:isFiltered="filterText.length > 0"
|
||||
:is-filtered="filterText.length > 0"
|
||||
:selected="selected.some((coll) => coll == collection)"
|
||||
:save-request="saveRequest"
|
||||
:collections-type="collectionsType"
|
||||
:picked="picked"
|
||||
@edit-collection="editCollection(collection, index)"
|
||||
@add-folder="addFolder($event)"
|
||||
@edit-folder="editFolder($event)"
|
||||
@edit-request="editRequest($event)"
|
||||
@update-team-collections="updateTeamCollections"
|
||||
@select-collection="$emit('use-collection', collection)"
|
||||
@unselect-collection="$emit('remove-collection', collection)"
|
||||
@select="$emit('select', $event)"
|
||||
@expand-collection="expandCollection"
|
||||
@remove-collection="removeCollection"
|
||||
@remove-request="removeRequest"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<p v-if="filterText && filteredCollections.length === 0" class="info">
|
||||
<i class="material-icons">not_interested</i> {{ $t("nothing_found") }} "{{ filterText }}"
|
||||
<i class="material-icons">not_interested</i> {{ $t("nothing_found") }} "{{
|
||||
filterText
|
||||
}}"
|
||||
</p>
|
||||
</AppSection>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.virtual-list {
|
||||
max-height: calc(100vh - 270px);
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import gql from "graphql-tag"
|
||||
import cloneDeep from "lodash/cloneDeep"
|
||||
import { fb } from "~/helpers/fb"
|
||||
import { getSettingSubject } from "~/newstore/settings"
|
||||
import TeamCollectionAdapter from "~/helpers/teams/TeamCollectionAdapter"
|
||||
import * as teamUtils from "~/helpers/teams/utils"
|
||||
|
||||
export default {
|
||||
props: {
|
||||
doc: Boolean,
|
||||
selected: { type: Array, default: () => [] },
|
||||
saveRequest: Boolean,
|
||||
picked: { type: Object, default: () => {} },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -110,6 +169,12 @@ export default {
|
||||
editingRequest: undefined,
|
||||
editingRequestIndex: undefined,
|
||||
filterText: "",
|
||||
collectionsType: {
|
||||
type: "my-collections",
|
||||
selectedTeam: undefined,
|
||||
},
|
||||
teamCollectionAdapter: new TeamCollectionAdapter(null),
|
||||
teamCollectionsNew: [],
|
||||
}
|
||||
},
|
||||
subscriptions() {
|
||||
@@ -118,29 +183,51 @@ export default {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
showTeamCollections() {
|
||||
if (fb.currentUser == null) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
},
|
||||
collections() {
|
||||
return fb.currentUser !== null
|
||||
? fb.currentCollections
|
||||
: this.$store.state.postwoman.collections
|
||||
},
|
||||
filteredCollections() {
|
||||
const collections =
|
||||
fb.currentUser !== null ? fb.currentCollections : this.$store.state.postwoman.collections
|
||||
let collections = null
|
||||
if (this.collectionsType.type === "my-collections") {
|
||||
collections =
|
||||
fb.currentUser !== null
|
||||
? fb.currentCollections
|
||||
: this.$store.state.postwoman.collections
|
||||
} else {
|
||||
collections = this.teamCollectionsNew
|
||||
}
|
||||
|
||||
if (!this.filterText) return collections
|
||||
if (!this.filterText) {
|
||||
return collections
|
||||
}
|
||||
|
||||
if (this.collectionsType.type === "team-collections") {
|
||||
return []
|
||||
}
|
||||
|
||||
const filterText = this.filterText.toLowerCase()
|
||||
const filteredCollections = []
|
||||
|
||||
for (let collection of collections) {
|
||||
for (const collection of collections) {
|
||||
const filteredRequests = []
|
||||
const filteredFolders = []
|
||||
for (let request of collection.requests) {
|
||||
if (request.name.toLowerCase().includes(filterText)) filteredRequests.push(request)
|
||||
for (const request of collection.requests) {
|
||||
if (request.name.toLowerCase().includes(filterText))
|
||||
filteredRequests.push(request)
|
||||
}
|
||||
for (let folder of collection.folders) {
|
||||
for (const folder of this.collectionsType.type === "team-collections"
|
||||
? collection.children
|
||||
: collection.folders) {
|
||||
const filteredFolderRequests = []
|
||||
for (let request of folder.requests) {
|
||||
for (const request of folder.requests) {
|
||||
if (request.name.toLowerCase().includes(filterText))
|
||||
filteredFolderRequests.push(request)
|
||||
}
|
||||
@@ -151,7 +238,10 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
if (filteredRequests.length + filteredFolders.length > 0) {
|
||||
if (
|
||||
filteredRequests.length + filteredFolders.length > 0 ||
|
||||
collection.name.toLowerCase().includes(filterText)
|
||||
) {
|
||||
const filteredCollection = Object.assign({}, collection)
|
||||
filteredCollection.requests = filteredRequests
|
||||
filteredCollection.folders = filteredFolders
|
||||
@@ -162,16 +252,202 @@ export default {
|
||||
return filteredCollections
|
||||
},
|
||||
},
|
||||
async mounted() {
|
||||
watch: {
|
||||
"collectionsType.type": function emitstuff() {
|
||||
this.$emit("update-collection", this.$data.collectionsType.type)
|
||||
},
|
||||
"collectionsType.selectedTeam"(value) {
|
||||
if (value?.id) this.teamCollectionAdapter.changeTeamID(value.id)
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this._keyListener = function (e) {
|
||||
if (e.key === "Escape") {
|
||||
e.preventDefault()
|
||||
this.showModalAdd = this.showModalEdit = this.showModalImportExport = this.showModalAddFolder = this.showModalEditFolder = this.showModalEditRequest = false
|
||||
this.showModalAdd =
|
||||
this.showModalEdit =
|
||||
this.showModalImportExport =
|
||||
this.showModalAddFolder =
|
||||
this.showModalEditFolder =
|
||||
this.showModalEditRequest =
|
||||
false
|
||||
}
|
||||
}
|
||||
document.addEventListener("keydown", this._keyListener.bind(this))
|
||||
|
||||
this.$subscribeTo(this.teamCollectionAdapter.collections$, (colls) => {
|
||||
this.teamCollectionsNew = cloneDeep(colls)
|
||||
})
|
||||
},
|
||||
beforeDestroy() {
|
||||
document.removeEventListener("keydown", this._keyListener)
|
||||
},
|
||||
methods: {
|
||||
updateTeamCollections() {
|
||||
// TODO: Remove this at some point
|
||||
},
|
||||
updateSelectedTeam(newSelectedTeam) {
|
||||
this.collectionsType.selectedTeam = newSelectedTeam
|
||||
this.$emit("update-coll-type", this.collectionsType)
|
||||
},
|
||||
updateCollectionType(newCollectionType) {
|
||||
this.collectionsType.type = newCollectionType
|
||||
this.$emit("update-coll-type", this.collectionsType)
|
||||
},
|
||||
// Intented to be called by the CollectionAdd modal submit event
|
||||
addNewRootCollection(name) {
|
||||
if (!name) {
|
||||
this.$toast.info(this.$t("invalid_collection_name"))
|
||||
return
|
||||
}
|
||||
if (this.collectionsType.type === "my-collections") {
|
||||
this.$store.commit("postwoman/addNewCollection", {
|
||||
name,
|
||||
flag: "rest",
|
||||
})
|
||||
|
||||
this.syncCollections()
|
||||
} else if (
|
||||
this.collectionsType.type === "team-collections" &&
|
||||
this.collectionsType.selectedTeam.myRole !== "VIEWER"
|
||||
) {
|
||||
teamUtils
|
||||
.createNewRootCollection(
|
||||
this.$apollo,
|
||||
name,
|
||||
this.collectionsType.selectedTeam.id
|
||||
)
|
||||
.then(() => {
|
||||
this.$toast.success(this.$t("collection_added"), {
|
||||
icon: "done",
|
||||
})
|
||||
})
|
||||
.catch((error) => {
|
||||
this.$toast.error(this.$t("error_occurred"), {
|
||||
icon: "done",
|
||||
})
|
||||
console.error(error)
|
||||
})
|
||||
}
|
||||
this.displayModalAdd(false)
|
||||
},
|
||||
// Intented to be called by CollectionEdit modal submit event
|
||||
updateEditingCollection(newName) {
|
||||
if (!newName) {
|
||||
this.$toast.info(this.$t("invalid_collection_name"))
|
||||
return
|
||||
}
|
||||
if (this.collectionsType.type === "my-collections") {
|
||||
const collectionUpdated = {
|
||||
...this.editingCollection,
|
||||
name: newName,
|
||||
}
|
||||
this.$store.commit("postwoman/editCollection", {
|
||||
collection: collectionUpdated,
|
||||
collectionIndex: this.editingCollectionIndex,
|
||||
flag: "rest",
|
||||
})
|
||||
this.syncCollections()
|
||||
} else if (
|
||||
this.collectionsType.type === "team-collections" &&
|
||||
this.collectionsType.selectedTeam.myRole !== "VIEWER"
|
||||
) {
|
||||
teamUtils
|
||||
.renameCollection(this.$apollo, newName, this.editingCollection.id)
|
||||
.then(() => {
|
||||
// TODO: $t translations ?
|
||||
this.$toast.success("Collection Renamed", {
|
||||
icon: "done",
|
||||
})
|
||||
})
|
||||
.catch((error) => {
|
||||
this.$toast.error(this.$t("error_occurred"), {
|
||||
icon: "done",
|
||||
})
|
||||
console.error(error)
|
||||
})
|
||||
}
|
||||
this.displayModalEdit(false)
|
||||
},
|
||||
// Intended to be called by CollectionEditFolder modal submit event
|
||||
updateEditingFolder(name) {
|
||||
if (this.collectionsType.type === "my-collections") {
|
||||
this.$store.commit("postwoman/editFolder", {
|
||||
collectionIndex: this.editingCollectionIndex,
|
||||
folder: { ...this.editingFolder, name },
|
||||
folderIndex: this.editingFolderIndex,
|
||||
folderName: this.editingFolder.name,
|
||||
flag: "rest",
|
||||
})
|
||||
this.syncCollections()
|
||||
} else if (
|
||||
this.collectionsType.type === "team-collections" &&
|
||||
this.collectionsType.selectedTeam.myRole !== "VIEWER"
|
||||
) {
|
||||
teamUtils
|
||||
.renameCollection(this.$apollo, name, this.editingFolder.id)
|
||||
.then(() => {
|
||||
// Result
|
||||
this.$toast.success(this.$t("folder_renamed"), {
|
||||
icon: "done",
|
||||
})
|
||||
})
|
||||
.catch((error) => {
|
||||
// Error
|
||||
this.$toast.error(this.$t("error_occurred"), {
|
||||
icon: "done",
|
||||
})
|
||||
console.error(error)
|
||||
})
|
||||
}
|
||||
|
||||
this.displayModalEditFolder(false)
|
||||
},
|
||||
// Intented to by called by CollectionsEditRequest modal submit event
|
||||
updateEditingRequest(requestUpdateData) {
|
||||
const requestUpdated = {
|
||||
...this.editingRequest,
|
||||
name: requestUpdateData.name || this.editingRequest.name,
|
||||
}
|
||||
|
||||
if (this.collectionsType.type === "my-collections") {
|
||||
this.$store.commit("postwoman/editRequest", {
|
||||
requestCollectionIndex: this.editingCollectionIndex,
|
||||
requestFolderName: this.editingFolderName,
|
||||
requestFolderIndex: this.editingFolderIndex,
|
||||
requestNew: requestUpdated,
|
||||
requestIndex: this.editingRequestIndex,
|
||||
flag: "rest",
|
||||
})
|
||||
this.syncCollections()
|
||||
} else if (
|
||||
this.collectionsType.type === "team-collections" &&
|
||||
this.collectionsType.selectedTeam.myRole !== "VIEWER"
|
||||
) {
|
||||
const requestName = requestUpdateData.name || this.editingRequest.name
|
||||
teamUtils
|
||||
.updateRequest(
|
||||
this.$apollo,
|
||||
requestUpdated,
|
||||
requestName,
|
||||
this.editingRequestIndex
|
||||
)
|
||||
.then(() => {
|
||||
this.$toast.success("Request Renamed", {
|
||||
icon: "done",
|
||||
})
|
||||
this.$emit("update-team-collections")
|
||||
})
|
||||
.catch((error) => {
|
||||
this.$toast.error(this.$t("error_occurred"), {
|
||||
icon: "done",
|
||||
})
|
||||
console.error(error)
|
||||
})
|
||||
}
|
||||
|
||||
this.displayModalEditRequest(false)
|
||||
},
|
||||
displayModalAdd(shouldDisplay) {
|
||||
this.showModalAdd = shouldDisplay
|
||||
},
|
||||
@@ -204,16 +480,56 @@ export default {
|
||||
this.displayModalEdit(true)
|
||||
this.syncCollections()
|
||||
},
|
||||
onAddFolder({ name, path }) {
|
||||
onAddFolder({ name, folder, path }) {
|
||||
const flag = "rest"
|
||||
this.$store.commit("postwoman/addFolder", {
|
||||
name,
|
||||
path,
|
||||
flag,
|
||||
})
|
||||
if (this.collectionsType.type === "my-collections") {
|
||||
this.$store.commit("postwoman/addFolder", {
|
||||
name,
|
||||
path,
|
||||
flag,
|
||||
})
|
||||
this.syncCollections()
|
||||
} else if (this.collectionsType.type === "team-collections") {
|
||||
if (this.collectionsType.selectedTeam.myRole !== "VIEWER") {
|
||||
this.$apollo
|
||||
.mutate({
|
||||
mutation: gql`
|
||||
mutation CreateChildCollection(
|
||||
$childTitle: String!
|
||||
$collectionID: String!
|
||||
) {
|
||||
createChildCollection(
|
||||
childTitle: $childTitle
|
||||
collectionID: $collectionID
|
||||
) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`,
|
||||
// Parameters
|
||||
variables: {
|
||||
childTitle: name,
|
||||
collectionID: folder.id,
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
// Result
|
||||
this.$toast.success(this.$t("folder_added"), {
|
||||
icon: "done",
|
||||
})
|
||||
this.$emit("update-team-collections")
|
||||
})
|
||||
.catch((error) => {
|
||||
// Error
|
||||
this.$toast.error(this.$t("error_occurred"), {
|
||||
icon: "done",
|
||||
})
|
||||
console.error(error)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
this.displayModalAddFolder(false)
|
||||
this.syncCollections()
|
||||
},
|
||||
addFolder(payload) {
|
||||
const { folder, path } = payload
|
||||
@@ -226,16 +542,24 @@ export default {
|
||||
this.$data.editingCollectionIndex = collectionIndex
|
||||
this.$data.editingFolder = folder
|
||||
this.$data.editingFolderIndex = folderIndex
|
||||
this.$data.collectionsType = this.collectionsType
|
||||
this.displayModalEditFolder(true)
|
||||
this.syncCollections()
|
||||
},
|
||||
editRequest(payload) {
|
||||
const { collectionIndex, folderIndex, folderName, request, requestIndex } = payload
|
||||
const {
|
||||
collectionIndex,
|
||||
folderIndex,
|
||||
folderName,
|
||||
request,
|
||||
requestIndex,
|
||||
} = payload
|
||||
this.$data.editingCollectionIndex = collectionIndex
|
||||
this.$data.editingFolderIndex = folderIndex
|
||||
this.$data.editingFolderName = folderName
|
||||
this.$data.editingRequest = request
|
||||
this.$data.editingRequestIndex = requestIndex
|
||||
this.$emit("select-request", requestIndex)
|
||||
this.displayModalEditRequest(true)
|
||||
this.syncCollections()
|
||||
},
|
||||
@@ -255,9 +579,86 @@ export default {
|
||||
)
|
||||
}
|
||||
},
|
||||
},
|
||||
beforeDestroy() {
|
||||
document.removeEventListener("keydown", this._keyListener)
|
||||
expandCollection(collectionID) {
|
||||
this.teamCollectionAdapter.expandCollection(collectionID)
|
||||
},
|
||||
removeCollection({ collectionsType, collectionIndex, collectionID }) {
|
||||
if (collectionsType.type === "my-collections") {
|
||||
this.$store.commit("postwoman/removeCollection", {
|
||||
collectionIndex,
|
||||
flag: "rest",
|
||||
})
|
||||
this.$toast.error(this.$t("deleted"), {
|
||||
icon: "delete",
|
||||
})
|
||||
this.syncCollections()
|
||||
} else if (collectionsType.type === "team-collections") {
|
||||
if (collectionsType.selectedTeam.myRole !== "VIEWER") {
|
||||
this.$apollo
|
||||
.mutate({
|
||||
// Query
|
||||
mutation: gql`
|
||||
mutation ($collectionID: String!) {
|
||||
deleteCollection(collectionID: $collectionID)
|
||||
}
|
||||
`,
|
||||
// Parameters
|
||||
variables: {
|
||||
collectionID,
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
// Result
|
||||
this.$toast.success(this.$t("deleted"), {
|
||||
icon: "delete",
|
||||
})
|
||||
})
|
||||
.catch((error) => {
|
||||
// Error
|
||||
this.$toast.error(this.$t("error_occurred"), {
|
||||
icon: "done",
|
||||
})
|
||||
console.error(error)
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
removeRequest({ collectionIndex, folderName, requestIndex }) {
|
||||
if (this.collectionsType.type === "my-collections") {
|
||||
this.$store.commit("postwoman/removeRequest", {
|
||||
collectionIndex,
|
||||
folderName,
|
||||
requestIndex,
|
||||
flag: "rest",
|
||||
})
|
||||
this.$toast.error(this.$t("deleted"), {
|
||||
icon: "delete",
|
||||
})
|
||||
this.syncCollections()
|
||||
} else if (this.collectionsType.type === "team-collections") {
|
||||
teamUtils
|
||||
.deleteRequest(this.$apollo, requestIndex)
|
||||
.then(() => {
|
||||
// Result
|
||||
this.$toast.success(this.$t("deleted"), {
|
||||
icon: "delete",
|
||||
})
|
||||
})
|
||||
.catch((error) => {
|
||||
// Error
|
||||
this.$toast.error(this.$t("error_occurred"), {
|
||||
icon: "done",
|
||||
})
|
||||
console.error(error)
|
||||
})
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.virtual-list {
|
||||
max-height: calc(100vh - 270px);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
<template>
|
||||
<div>
|
||||
<div
|
||||
:class="['row-wrapper transition duration-150 ease-in-out', { 'bg-bgDarkColor': dragging }]"
|
||||
:class="[
|
||||
'row-wrapper transition duration-150 ease-in-out',
|
||||
{ 'bg-bgDarkColor': dragging },
|
||||
]"
|
||||
@dragover.prevent
|
||||
@drop.prevent="dropEvent"
|
||||
@dragover="dragging = true"
|
||||
@@ -10,43 +13,73 @@
|
||||
@dragend="dragging = false"
|
||||
>
|
||||
<button class="icon" @click="toggleShowChildren">
|
||||
<i class="material-icons" v-show="!showChildren && !isFiltered">arrow_right</i>
|
||||
<i class="material-icons" v-show="showChildren || isFiltered">arrow_drop_down</i>
|
||||
<i class="material-icons">folder</i>
|
||||
<i v-show="!showChildren && !isFiltered" class="material-icons"
|
||||
>arrow_right</i
|
||||
>
|
||||
<i v-show="showChildren || isFiltered" class="material-icons"
|
||||
>arrow_drop_down</i
|
||||
>
|
||||
|
||||
<i v-if="isSelected" class="text-green-400 material-icons"
|
||||
>check_circle</i
|
||||
>
|
||||
|
||||
<i v-else class="material-icons">folder</i>
|
||||
<span>{{ collection.name }}</span>
|
||||
</button>
|
||||
<div>
|
||||
<button
|
||||
v-if="doc"
|
||||
v-if="doc && !selected"
|
||||
v-tooltip.left="$t('import')"
|
||||
class="icon"
|
||||
@click="$emit('select-collection')"
|
||||
v-tooltip.left="$t('import')"
|
||||
>
|
||||
<i class="material-icons">topic</i>
|
||||
<i class="material-icons">check_box_outline_blank</i>
|
||||
</button>
|
||||
<v-popover>
|
||||
<button class="tooltip-target icon" v-tooltip.left="$t('more')">
|
||||
<button
|
||||
v-if="doc && selected"
|
||||
v-tooltip.left="$t('delete')"
|
||||
class="icon"
|
||||
@click="$emit('unselect-collection')"
|
||||
>
|
||||
<i class="material-icons">check_box</i>
|
||||
</button>
|
||||
<v-popover v-if="!saveRequest">
|
||||
<button v-tooltip.left="$t('more')" class="tooltip-target icon">
|
||||
<i class="material-icons">more_vert</i>
|
||||
</button>
|
||||
<template slot="popover">
|
||||
<div>
|
||||
<button
|
||||
class="icon"
|
||||
@click="$emit('add-folder', { folder: collection, path: `${collectionIndex}` })"
|
||||
v-close-popover
|
||||
class="icon"
|
||||
@click="
|
||||
$emit('add-folder', {
|
||||
folder: collection,
|
||||
path: `${collectionIndex}`,
|
||||
})
|
||||
"
|
||||
>
|
||||
<i class="material-icons">create_new_folder</i>
|
||||
<span>{{ $t("new_folder") }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<button class="icon" @click="$emit('edit-collection')" v-close-popover>
|
||||
<button
|
||||
v-close-popover
|
||||
class="icon"
|
||||
@click="$emit('edit-collection')"
|
||||
>
|
||||
<i class="material-icons">create</i>
|
||||
<span>{{ $t("edit") }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<button class="icon" @click="confirmRemove = true" v-close-popover>
|
||||
<button
|
||||
v-close-popover
|
||||
class="icon"
|
||||
@click="confirmRemove = true"
|
||||
>
|
||||
<i class="material-icons">delete</i>
|
||||
<span>{{ $t("delete") }}</span>
|
||||
</button>
|
||||
@@ -59,19 +92,24 @@
|
||||
<ul class="flex-col">
|
||||
<li
|
||||
v-for="(folder, index) in collection.folders"
|
||||
:key="folder.name"
|
||||
:key="index"
|
||||
class="ml-8 border-l border-brdColor"
|
||||
>
|
||||
<CollectionsFolder
|
||||
<CollectionsMyFolder
|
||||
:folder="folder"
|
||||
:folder-index="index"
|
||||
:folder-path="`${collectionIndex}/${index}`"
|
||||
:collection-index="collectionIndex"
|
||||
:doc="doc"
|
||||
:isFiltered="isFiltered"
|
||||
:save-request="saveRequest"
|
||||
:collections-type="collectionsType"
|
||||
:is-filtered="isFiltered"
|
||||
:picked="picked"
|
||||
@add-folder="$emit('add-folder', $event)"
|
||||
@edit-folder="$emit('edit-folder', $event)"
|
||||
@edit-request="$emit('edit-request', $event)"
|
||||
@select="$emit('select', $event)"
|
||||
@remove-request="removeRequest"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -81,24 +119,36 @@
|
||||
:key="index"
|
||||
class="ml-8 border-l border-brdColor"
|
||||
>
|
||||
<CollectionsRequest
|
||||
<CollectionsMyRequest
|
||||
:request="request"
|
||||
:collection-index="collectionIndex"
|
||||
:folder-index="-1"
|
||||
:folder-name="collection.name"
|
||||
:folder-path="collectionIndex.toString()"
|
||||
:request-index="index"
|
||||
:doc="doc"
|
||||
@edit-request="$emit('edit-request', $event)"
|
||||
:save-request="saveRequest"
|
||||
:collections-type="collectionsType"
|
||||
:picked="picked"
|
||||
@edit-request="editRequest($event)"
|
||||
@select="$emit('select', $event)"
|
||||
@remove-request="removeRequest"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li
|
||||
v-if="collection.folders.length === 0 && collection.requests.length === 0"
|
||||
v-if="
|
||||
(collection.folders == undefined ||
|
||||
collection.folders.length === 0) &&
|
||||
(collection.requests == undefined ||
|
||||
collection.requests.length === 0)
|
||||
"
|
||||
class="flex ml-8 border-l border-brdColor"
|
||||
>
|
||||
<p class="info">
|
||||
<i class="material-icons">not_interested</i> {{ $t("collection_empty") }}
|
||||
<i class="material-icons">not_interested</i>
|
||||
{{ $t("collection_empty") }}
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -118,10 +168,14 @@ import { getSettingSubject } from "~/newstore/settings"
|
||||
|
||||
export default {
|
||||
props: {
|
||||
collectionIndex: Number,
|
||||
collection: Object,
|
||||
collectionIndex: { type: Number, default: null },
|
||||
collection: { type: Object, default: () => {} },
|
||||
doc: Boolean,
|
||||
isFiltered: Boolean,
|
||||
selected: Boolean,
|
||||
saveRequest: Boolean,
|
||||
collectionsType: { type: Object, default: () => {} },
|
||||
picked: { type: Object, default: () => {} },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -129,6 +183,9 @@ export default {
|
||||
dragging: false,
|
||||
selectedFolder: {},
|
||||
confirmRemove: false,
|
||||
prevCursor: "",
|
||||
cursor: "",
|
||||
pageNo: 0,
|
||||
}
|
||||
},
|
||||
subscriptions() {
|
||||
@@ -136,7 +193,19 @@ export default {
|
||||
SYNC_COLLECTIONS: getSettingSubject("syncCollections"),
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isSelected() {
|
||||
return (
|
||||
this.picked &&
|
||||
this.picked.pickedType === "my-collection" &&
|
||||
this.picked.collectionIndex === this.collectionIndex
|
||||
)
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
editRequest(event) {
|
||||
this.$emit("edit-request", event)
|
||||
},
|
||||
syncCollections() {
|
||||
if (fb.currentUser !== null && this.SYNC_COLLECTIONS) {
|
||||
fb.writeCollections(
|
||||
@@ -146,17 +215,24 @@ export default {
|
||||
}
|
||||
},
|
||||
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.$store.commit("postwoman/removeCollection", {
|
||||
this.$emit("remove-collection", {
|
||||
collectionsType: this.collectionsType,
|
||||
collectionIndex: this.collectionIndex,
|
||||
flag: "rest",
|
||||
collectionID: this.collection.id,
|
||||
})
|
||||
this.$toast.error(this.$t("deleted"), {
|
||||
icon: "delete",
|
||||
})
|
||||
this.syncCollections()
|
||||
this.confirmRemove = false
|
||||
},
|
||||
dropEvent({ dataTransfer }) {
|
||||
this.dragging = !this.dragging
|
||||
@@ -177,6 +253,13 @@ export default {
|
||||
})
|
||||
this.syncCollections()
|
||||
},
|
||||
removeRequest({ collectionIndex, folderName, requestIndex }) {
|
||||
this.$emit("remove-request", {
|
||||
collectionIndex,
|
||||
folderName,
|
||||
requestIndex,
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -1,7 +1,10 @@
|
||||
<template>
|
||||
<div>
|
||||
<div
|
||||
:class="['row-wrapper transition duration-150 ease-in-out', { 'bg-bgDarkColor': dragging }]"
|
||||
:class="[
|
||||
'row-wrapper transition duration-150 ease-in-out',
|
||||
{ 'bg-bgDarkColor': dragging },
|
||||
]"
|
||||
@dragover.prevent
|
||||
@drop.prevent="dropEvent"
|
||||
@dragover="dragging = true"
|
||||
@@ -11,22 +14,29 @@
|
||||
>
|
||||
<div>
|
||||
<button class="icon" @click="toggleShowChildren">
|
||||
<i class="material-icons" v-show="!showChildren && !isFiltered">arrow_right</i>
|
||||
<i class="material-icons" v-show="showChildren || isFiltered">arrow_drop_down</i>
|
||||
<i class="material-icons">folder_open</i>
|
||||
<span>{{ folder.name }}</span>
|
||||
<i v-show="!showChildren && !isFiltered" class="material-icons"
|
||||
>arrow_right</i
|
||||
>
|
||||
<i v-show="showChildren || isFiltered" class="material-icons"
|
||||
>arrow_drop_down</i
|
||||
>
|
||||
<i v-if="isSelected" class="text-green-400 material-icons"
|
||||
>check_circle</i
|
||||
>
|
||||
<i v-else class="material-icons">folder_open</i>
|
||||
<span>{{ folder.name ? folder.name : folder.title }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<v-popover>
|
||||
<button class="tooltip-target icon" v-tooltip.left="$t('more')">
|
||||
<v-popover v-if="!saveRequest">
|
||||
<button v-tooltip.left="$t('more')" class="tooltip-target icon">
|
||||
<i class="material-icons">more_vert</i>
|
||||
</button>
|
||||
<template slot="popover">
|
||||
<div>
|
||||
<button
|
||||
v-close-popover
|
||||
class="icon"
|
||||
@click="$emit('add-folder', { folder, path: folderPath })"
|
||||
v-close-popover
|
||||
>
|
||||
<i class="material-icons">create_new_folder</i>
|
||||
<span>{{ $t("new_folder") }}</span>
|
||||
@@ -34,16 +44,18 @@
|
||||
</div>
|
||||
<div>
|
||||
<button
|
||||
class="icon"
|
||||
@click="$emit('edit-folder', { folder, folderIndex, collectionIndex })"
|
||||
v-close-popover
|
||||
class="icon"
|
||||
@click="
|
||||
$emit('edit-folder', { folder, folderIndex, collectionIndex })
|
||||
"
|
||||
>
|
||||
<i class="material-icons">edit</i>
|
||||
<span>{{ $t("edit") }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<button class="icon" @click="confirmRemove = true" v-close-popover>
|
||||
<button v-close-popover class="icon" @click="confirmRemove = true">
|
||||
<i class="material-icons">delete</i>
|
||||
<span>{{ $t("delete") }}</span>
|
||||
</button>
|
||||
@@ -52,21 +64,27 @@
|
||||
</v-popover>
|
||||
</div>
|
||||
<div v-show="showChildren || isFiltered">
|
||||
<ul v-if="folder.folders && folder.folders.length" class="flex-col">
|
||||
<ul class="flex-col">
|
||||
<li
|
||||
v-for="(subFolder, subFolderIndex) in folder.folders"
|
||||
:key="subFolder.name"
|
||||
class="ml-8 border-l border-brdColor"
|
||||
>
|
||||
<CollectionsFolder
|
||||
<CollectionsMyFolder
|
||||
:folder="subFolder"
|
||||
:folder-index="subFolderIndex"
|
||||
:collection-index="collectionIndex"
|
||||
:doc="doc"
|
||||
:save-request="saveRequest"
|
||||
:collections-type="collectionsType"
|
||||
:folder-path="`${folderPath}/${subFolderIndex}`"
|
||||
:picked="picked"
|
||||
@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)"
|
||||
@remove-request="removeRequest"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -76,14 +94,20 @@
|
||||
:key="index"
|
||||
class="flex ml-8 border-l border-brdColor"
|
||||
>
|
||||
<CollectionsRequest
|
||||
<CollectionsMyRequest
|
||||
:request="request"
|
||||
:collection-index="collectionIndex"
|
||||
:folder-index="folderIndex"
|
||||
:folder-name="folder.name"
|
||||
:request-index="index"
|
||||
:folder-path="folderPath"
|
||||
:doc="doc"
|
||||
:picked="picked"
|
||||
:save-request="saveRequest"
|
||||
:collections-type="collectionsType"
|
||||
@edit-request="$emit('edit-request', $event)"
|
||||
@select="$emit('select', $event)"
|
||||
@remove-request="removeRequest"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -96,7 +120,10 @@
|
||||
"
|
||||
>
|
||||
<li class="flex ml-8 border-l border-brdColor">
|
||||
<p class="info"><i class="material-icons">not_interested</i> {{ $t("folder_empty") }}</p>
|
||||
<p class="info">
|
||||
<i class="material-icons">not_interested</i>
|
||||
{{ $t("folder_empty") }}
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -114,20 +141,25 @@ import { fb } from "~/helpers/fb"
|
||||
import { getSettingSubject } from "~/newstore/settings"
|
||||
|
||||
export default {
|
||||
name: "folder",
|
||||
name: "Folder",
|
||||
props: {
|
||||
folder: Object,
|
||||
folderIndex: Number,
|
||||
collectionIndex: Number,
|
||||
folderPath: String,
|
||||
folder: { type: Object, default: () => {} },
|
||||
folderIndex: { type: Number, default: null },
|
||||
collectionIndex: { type: Number, default: null },
|
||||
folderPath: { type: String, default: null },
|
||||
doc: Boolean,
|
||||
saveRequest: Boolean,
|
||||
isFiltered: Boolean,
|
||||
collectionsType: { type: Object, default: () => {} },
|
||||
picked: { type: Object, default: () => {} },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showChildren: false,
|
||||
dragging: false,
|
||||
confirmRemove: false,
|
||||
prevCursor: "",
|
||||
cursor: "",
|
||||
}
|
||||
},
|
||||
subscriptions() {
|
||||
@@ -135,6 +167,15 @@ export default {
|
||||
SYNC_COLLECTIONS: getSettingSubject("syncCollections"),
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isSelected() {
|
||||
return (
|
||||
this.picked &&
|
||||
this.picked.pickedType === "my-folder" &&
|
||||
this.picked.folderPath === this.folderPath
|
||||
)
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
syncCollections() {
|
||||
if (fb.currentUser !== null && this.SYNC_COLLECTIONS) {
|
||||
@@ -145,6 +186,15 @@ export default {
|
||||
}
|
||||
},
|
||||
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() {
|
||||
@@ -179,6 +229,13 @@ export default {
|
||||
})
|
||||
this.syncCollections()
|
||||
},
|
||||
removeRequest({ collectionIndex, folderName, requestIndex }) {
|
||||
this.$emit("remove-request", {
|
||||
collectionIndex,
|
||||
folderName,
|
||||
requestIndex,
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -1,7 +1,10 @@
|
||||
<template>
|
||||
<div>
|
||||
<div
|
||||
:class="['row-wrapper transition duration-150 ease-in-out', { 'bg-bgDarkColor': dragging }]"
|
||||
:class="[
|
||||
'row-wrapper transition duration-150 ease-in-out',
|
||||
{ 'bg-bgDarkColor': dragging },
|
||||
]"
|
||||
draggable="true"
|
||||
@dragstart="dragStart"
|
||||
@dragover.stop
|
||||
@@ -10,21 +13,28 @@
|
||||
>
|
||||
<div>
|
||||
<button
|
||||
v-tooltip="!doc ? $t('use_request') : ''"
|
||||
class="icon"
|
||||
@click="!doc ? selectRequest() : {}"
|
||||
v-tooltip="!doc ? $t('use_request') : ''"
|
||||
>
|
||||
<span :class="getRequestLabelColor(request.method)">{{ request.method }}</span>
|
||||
<i v-if="isSelected" class="mx-3 text-green-400 material-icons"
|
||||
>check_circle</i
|
||||
>
|
||||
|
||||
<span v-else :class="getRequestLabelColor(request.method)">{{
|
||||
request.method
|
||||
}}</span>
|
||||
<span>{{ request.name }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<v-popover>
|
||||
<button class="tooltip-target icon" v-tooltip="$t('more')">
|
||||
<v-popover v-if="!saveRequest">
|
||||
<button v-tooltip="$t('more')" class="tooltip-target icon">
|
||||
<i class="material-icons">more_vert</i>
|
||||
</button>
|
||||
<template slot="popover">
|
||||
<div>
|
||||
<button
|
||||
v-close-popover
|
||||
class="icon"
|
||||
@click="
|
||||
$emit('edit-request', {
|
||||
@@ -35,14 +45,13 @@
|
||||
requestIndex,
|
||||
})
|
||||
"
|
||||
v-close-popover
|
||||
>
|
||||
<i class="material-icons">edit</i>
|
||||
<span>{{ $t("edit") }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<button class="icon" @click="confirmRemove = true" v-close-popover>
|
||||
<button v-close-popover class="icon" @click="confirmRemove = true">
|
||||
<i class="material-icons">delete</i>
|
||||
<span>{{ $t("delete") }}</span>
|
||||
</button>
|
||||
@@ -60,17 +69,19 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { fb } from "~/helpers/fb"
|
||||
import { getSettingSubject } from "~/newstore/settings"
|
||||
|
||||
export default {
|
||||
props: {
|
||||
request: Object,
|
||||
collectionIndex: Number,
|
||||
folderIndex: Number,
|
||||
folderName: String,
|
||||
requestIndex: Number,
|
||||
request: { type: Object, default: () => {} },
|
||||
collectionIndex: { type: Number, default: null },
|
||||
folderIndex: { type: Number, default: null },
|
||||
folderName: { type: String, default: null },
|
||||
// eslint-disable-next-line vue/require-default-prop
|
||||
requestIndex: [Number, String],
|
||||
doc: Boolean,
|
||||
saveRequest: Boolean,
|
||||
collectionsType: { type: Object, default: () => {} },
|
||||
folderPath: { type: String, default: null },
|
||||
picked: { type: Object, default: () => {} },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -85,22 +96,30 @@ export default {
|
||||
confirmRemove: false,
|
||||
}
|
||||
},
|
||||
subscriptions() {
|
||||
return {
|
||||
SYNC_COLLECTIONS: getSettingSubject("syncCollections"),
|
||||
}
|
||||
computed: {
|
||||
isSelected() {
|
||||
return (
|
||||
this.picked &&
|
||||
this.picked.pickedType === "my-request" &&
|
||||
this.picked.folderPath === this.folderPath &&
|
||||
this.picked.requestIndex === this.requestIndex
|
||||
)
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
syncCollections() {
|
||||
if (fb.currentUser !== null && this.SYNC_COLLECTIONS) {
|
||||
fb.writeCollections(
|
||||
JSON.parse(JSON.stringify(this.$store.state.postwoman.collections)),
|
||||
"collections"
|
||||
)
|
||||
}
|
||||
},
|
||||
selectRequest() {
|
||||
this.$store.commit("postwoman/selectRequest", { request: this.request })
|
||||
if (this.$props.saveRequest)
|
||||
this.$emit("select", {
|
||||
picked: {
|
||||
pickedType: "my-request",
|
||||
collectionIndex: this.collectionIndex,
|
||||
folderPath: this.folderPath,
|
||||
folderName: this.folderName,
|
||||
requestIndex: this.requestIndex,
|
||||
},
|
||||
})
|
||||
else
|
||||
this.$store.commit("postwoman/selectRequest", { request: this.request })
|
||||
},
|
||||
dragStart({ dataTransfer }) {
|
||||
this.dragging = !this.dragging
|
||||
@@ -110,20 +129,17 @@ export default {
|
||||
dataTransfer.setData("requestIndex", this.$props.requestIndex)
|
||||
},
|
||||
removeRequest() {
|
||||
this.$store.commit("postwoman/removeRequest", {
|
||||
this.$emit("remove-request", {
|
||||
collectionIndex: this.$props.collectionIndex,
|
||||
folderName: this.$props.folderName,
|
||||
requestIndex: this.$props.requestIndex,
|
||||
flag: "rest",
|
||||
})
|
||||
this.$toast.error(this.$t("deleted"), {
|
||||
icon: "delete",
|
||||
})
|
||||
this.confirmRemove = false
|
||||
this.syncCollections()
|
||||
},
|
||||
getRequestLabelColor(method) {
|
||||
return this.requestMethodLabels[method.toLowerCase()] || this.requestMethodLabels.default
|
||||
return (
|
||||
this.requestMethodLabels[method.toLowerCase()] ||
|
||||
this.requestMethodLabels.default
|
||||
)
|
||||
},
|
||||
},
|
||||
}
|
||||
236
components/collections/teams/Collection.vue
Normal file
236
components/collections/teams/Collection.vue
Normal file
@@ -0,0 +1,236 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="transition duration-150 ease-in-out row-wrapper">
|
||||
<button class="icon" @click="toggleShowChildren">
|
||||
<i v-show="!showChildren && !isFiltered" class="material-icons"
|
||||
>arrow_right</i
|
||||
>
|
||||
<i v-show="showChildren || isFiltered" class="material-icons"
|
||||
>arrow_drop_down</i
|
||||
>
|
||||
|
||||
<i v-if="isSelected" class="text-green-400 material-icons"
|
||||
>check_circle</i
|
||||
>
|
||||
|
||||
<i v-else class="material-icons">folder</i>
|
||||
<span>{{ collection.title }}</span>
|
||||
</button>
|
||||
<div>
|
||||
<button
|
||||
v-if="doc && !selected"
|
||||
v-tooltip.left="$t('import')"
|
||||
class="icon"
|
||||
@click="$emit('select-collection')"
|
||||
>
|
||||
<i class="material-icons">check_box_outline_blank</i>
|
||||
</button>
|
||||
<button
|
||||
v-if="doc && selected"
|
||||
v-tooltip.left="$t('delete')"
|
||||
class="icon"
|
||||
@click="$emit('unselect-collection')"
|
||||
>
|
||||
<i class="material-icons">check_box</i>
|
||||
</button>
|
||||
<v-popover v-if="!saveRequest">
|
||||
<button
|
||||
v-if="collectionsType.selectedTeam.myRole !== 'VIEWER'"
|
||||
v-tooltip.left="$t('more')"
|
||||
class="tooltip-target icon"
|
||||
>
|
||||
<i class="material-icons">more_vert</i>
|
||||
</button>
|
||||
<template slot="popover">
|
||||
<div>
|
||||
<button
|
||||
v-if="collectionsType.selectedTeam.myRole !== 'VIEWER'"
|
||||
v-close-popover
|
||||
class="icon"
|
||||
@click="
|
||||
$emit('add-folder', {
|
||||
folder: collection,
|
||||
path: `${collectionIndex}`,
|
||||
})
|
||||
"
|
||||
>
|
||||
<i class="material-icons">create_new_folder</i>
|
||||
<span>{{ $t("new_folder") }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<button
|
||||
v-if="collectionsType.selectedTeam.myRole !== 'VIEWER'"
|
||||
v-close-popover
|
||||
class="icon"
|
||||
@click="$emit('edit-collection')"
|
||||
>
|
||||
<i class="material-icons">create</i>
|
||||
<span>{{ $t("edit") }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<button
|
||||
v-if="collectionsType.selectedTeam.myRole !== 'VIEWER'"
|
||||
v-close-popover
|
||||
class="icon"
|
||||
@click="confirmRemove = true"
|
||||
>
|
||||
<i class="material-icons">delete</i>
|
||||
<span>{{ $t("delete") }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
</v-popover>
|
||||
</div>
|
||||
</div>
|
||||
<div v-show="showChildren || isFiltered">
|
||||
<ul class="flex-col">
|
||||
<li
|
||||
v-for="(folder, index) in collection.children"
|
||||
:key="folder.title"
|
||||
class="ml-8 border-l border-brdColor"
|
||||
>
|
||||
<CollectionsTeamsFolder
|
||||
:folder="folder"
|
||||
:folder-index="index"
|
||||
:folder-path="`${collectionIndex}/${index}`"
|
||||
:collection-index="collectionIndex"
|
||||
:doc="doc"
|
||||
:save-request="saveRequest"
|
||||
:collections-type="collectionsType"
|
||||
:is-filtered="isFiltered"
|
||||
:picked="picked"
|
||||
@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="removeRequest"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="flex-col">
|
||||
<li
|
||||
v-for="(request, index) in collection.requests"
|
||||
:key="index"
|
||||
class="ml-8 border-l border-brdColor"
|
||||
>
|
||||
<CollectionsTeamsRequest
|
||||
:request="request.request"
|
||||
:collection-index="collectionIndex"
|
||||
:folder-index="-1"
|
||||
:folder-name="collection.name"
|
||||
:request-index="request.id"
|
||||
:doc="doc"
|
||||
:save-request="saveRequest"
|
||||
:collections-type="collectionsType"
|
||||
:picked="picked"
|
||||
@edit-request="editRequest($event)"
|
||||
@select="$emit('select', $event)"
|
||||
@remove-request="removeRequest"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li
|
||||
v-if="
|
||||
(collection.children == undefined ||
|
||||
collection.children.length === 0) &&
|
||||
(collection.requests == undefined ||
|
||||
collection.requests.length === 0)
|
||||
"
|
||||
class="flex ml-8 border-l border-brdColor"
|
||||
>
|
||||
<p class="info">
|
||||
<i class="material-icons">not_interested</i>
|
||||
{{ $t("collection_empty") }}
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<SmartConfirmModal
|
||||
:show="confirmRemove"
|
||||
:title="$t('are_you_sure_remove_collection')"
|
||||
@hide-modal="confirmRemove = false"
|
||||
@resolve="removeCollection"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
collectionIndex: { type: Number, default: null },
|
||||
collection: { type: Object, default: () => {} },
|
||||
doc: Boolean,
|
||||
isFiltered: Boolean,
|
||||
selected: Boolean,
|
||||
saveRequest: Boolean,
|
||||
collectionsType: { type: Object, default: () => {} },
|
||||
picked: { type: Object, default: () => {} },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showChildren: false,
|
||||
dragging: false,
|
||||
selectedFolder: {},
|
||||
confirmRemove: false,
|
||||
prevCursor: "",
|
||||
cursor: "",
|
||||
pageNo: 0,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isSelected() {
|
||||
return (
|
||||
this.picked &&
|
||||
this.picked.pickedType === "teams-collection" &&
|
||||
this.picked.collectionID === this.collection.id
|
||||
)
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
editRequest(event) {
|
||||
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", {
|
||||
collectionsType: this.collectionsType,
|
||||
collectionIndex: this.collectionIndex,
|
||||
collectionID: this.collection.id,
|
||||
})
|
||||
this.confirmRemove = false
|
||||
},
|
||||
expandCollection(collectionID) {
|
||||
this.$emit("expand-collection", collectionID)
|
||||
},
|
||||
removeRequest({ collectionIndex, folderName, requestIndex }) {
|
||||
this.$emit("remove-request", {
|
||||
collectionIndex,
|
||||
folderName,
|
||||
requestIndex,
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
219
components/collections/teams/Folder.vue
Normal file
219
components/collections/teams/Folder.vue
Normal file
@@ -0,0 +1,219 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="transition duration-150 ease-in-out row-wrapper">
|
||||
<div>
|
||||
<button class="icon" @click="toggleShowChildren">
|
||||
<i v-show="!showChildren && !isFiltered" class="material-icons"
|
||||
>arrow_right</i
|
||||
>
|
||||
<i v-show="showChildren || isFiltered" class="material-icons"
|
||||
>arrow_drop_down</i
|
||||
>
|
||||
|
||||
<i v-if="isSelected" class="text-green-400 material-icons"
|
||||
>check_circle</i
|
||||
>
|
||||
|
||||
<i v-else class="material-icons">folder_open</i>
|
||||
<span>{{ folder.name ? folder.name : folder.title }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<v-popover v-if="!saveRequest">
|
||||
<button
|
||||
v-if="collectionsType.selectedTeam.myRole !== 'VIEWER'"
|
||||
v-tooltip.left="$t('more')"
|
||||
class="tooltip-target icon"
|
||||
>
|
||||
<i class="material-icons">more_vert</i>
|
||||
</button>
|
||||
<template slot="popover">
|
||||
<div>
|
||||
<button
|
||||
v-if="collectionsType.selectedTeam.myRole !== 'VIEWER'"
|
||||
v-close-popover
|
||||
class="icon"
|
||||
@click="$emit('add-folder', { folder, path: folderPath })"
|
||||
>
|
||||
<i class="material-icons">create_new_folder</i>
|
||||
<span>{{ $t("new_folder") }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<button
|
||||
v-if="collectionsType.selectedTeam.myRole !== 'VIEWER'"
|
||||
v-close-popover
|
||||
class="icon"
|
||||
@click="
|
||||
$emit('edit-folder', { folder, folderIndex, collectionIndex })
|
||||
"
|
||||
>
|
||||
<i class="material-icons">edit</i>
|
||||
<span>{{ $t("edit") }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<button
|
||||
v-if="collectionsType.selectedTeam.myRole !== 'VIEWER'"
|
||||
v-close-popover
|
||||
class="icon"
|
||||
@click="confirmRemove = true"
|
||||
>
|
||||
<i class="material-icons">delete</i>
|
||||
<span>{{ $t("delete") }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
</v-popover>
|
||||
</div>
|
||||
<div v-show="showChildren || isFiltered">
|
||||
<ul class="flex-col">
|
||||
<li
|
||||
v-for="(subFolder, subFolderIndex) in folder.children"
|
||||
:key="subFolder.name"
|
||||
class="ml-8 border-l border-brdColor"
|
||||
>
|
||||
<CollectionsTeamsFolder
|
||||
:folder="subFolder"
|
||||
:folder-index="subFolderIndex"
|
||||
:collection-index="collectionIndex"
|
||||
:doc="doc"
|
||||
:save-request="saveRequest"
|
||||
:collections-type="collectionsType"
|
||||
:folder-path="`${folderPath}/${subFolderIndex}`"
|
||||
:picked="picked"
|
||||
@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="removeRequest"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="flex-col">
|
||||
<li
|
||||
v-for="(request, index) in folder.requests"
|
||||
:key="index"
|
||||
class="flex ml-8 border-l border-brdColor"
|
||||
>
|
||||
<CollectionsTeamsRequest
|
||||
:request="request.request"
|
||||
:collection-index="collectionIndex"
|
||||
:folder-index="folderIndex"
|
||||
:folder-name="folder.name"
|
||||
:request-index="request.id"
|
||||
:doc="doc"
|
||||
:save-request="saveRequest"
|
||||
:collections-type="collectionsType"
|
||||
:picked="picked"
|
||||
@edit-request="$emit('edit-request', $event)"
|
||||
@select="$emit('select', $event)"
|
||||
@remove-request="removeRequest"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
<ul
|
||||
v-if="
|
||||
(folder.children == undefined || folder.children.length === 0) &&
|
||||
(folder.requests == undefined || folder.requests.length === 0)
|
||||
"
|
||||
>
|
||||
<li class="flex ml-8 border-l border-brdColor">
|
||||
<p class="info">
|
||||
<i class="material-icons">not_interested</i>
|
||||
{{ $t("folder_empty") }}
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<SmartConfirmModal
|
||||
:show="confirmRemove"
|
||||
:title="$t('are_you_sure_remove_folder')"
|
||||
@hide-modal="confirmRemove = false"
|
||||
@resolve="removeFolder"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as teamUtils from "~/helpers/teams/utils"
|
||||
|
||||
export default {
|
||||
name: "Folder",
|
||||
props: {
|
||||
folder: { type: Object, default: () => {} },
|
||||
folderIndex: { type: Number, default: null },
|
||||
collectionIndex: { type: Number, default: null },
|
||||
folderPath: { type: String, default: null },
|
||||
doc: Boolean,
|
||||
saveRequest: Boolean,
|
||||
isFiltered: Boolean,
|
||||
collectionsType: { type: Object, default: () => {} },
|
||||
picked: { type: Object, default: () => {} },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showChildren: false,
|
||||
confirmRemove: false,
|
||||
prevCursor: "",
|
||||
cursor: "",
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isSelected() {
|
||||
return (
|
||||
this.picked &&
|
||||
this.picked.pickedType === "teams-folder" &&
|
||||
this.picked.folderID === this.folder.id
|
||||
)
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
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() {
|
||||
if (this.collectionsType.selectedTeam.myRole !== "VIEWER") {
|
||||
teamUtils
|
||||
.deleteCollection(this.$apollo, this.folder.id)
|
||||
.then(() => {
|
||||
// Result
|
||||
this.$toast.success(this.$t("deleted"), {
|
||||
icon: "delete",
|
||||
})
|
||||
this.$emit("update-team-collections")
|
||||
this.confirmRemove = false
|
||||
})
|
||||
.catch((error) => {
|
||||
// Error
|
||||
this.$toast.error(this.$t("error_occurred"), {
|
||||
icon: "done",
|
||||
})
|
||||
console.error(error)
|
||||
})
|
||||
this.$emit("update-team-collections")
|
||||
}
|
||||
},
|
||||
expandCollection(collectionID) {
|
||||
this.$emit("expand-collection", collectionID)
|
||||
},
|
||||
removeRequest({ collectionIndex, folderName, requestIndex }) {
|
||||
this.$emit("remove-request", {
|
||||
collectionIndex,
|
||||
folderName,
|
||||
requestIndex,
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
128
components/collections/teams/Request.vue
Normal file
128
components/collections/teams/Request.vue
Normal file
@@ -0,0 +1,128 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="transition duration-150 ease-in-out row-wrapper">
|
||||
<div>
|
||||
<button
|
||||
v-tooltip="!doc ? $t('use_request') : ''"
|
||||
class="icon"
|
||||
@click="!doc ? selectRequest() : {}"
|
||||
>
|
||||
<i v-if="isSelected" class="mx-3 text-green-400 material-icons"
|
||||
>check_circle</i
|
||||
>
|
||||
|
||||
<span v-else :class="getRequestLabelColor(request.method)">{{
|
||||
request.method
|
||||
}}</span>
|
||||
<span>{{ request.name }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<v-popover v-if="!saveRequest">
|
||||
<button
|
||||
v-if="collectionsType.selectedTeam.myRole !== 'VIEWER'"
|
||||
v-tooltip="$t('more')"
|
||||
class="tooltip-target icon"
|
||||
>
|
||||
<i class="material-icons">more_vert</i>
|
||||
</button>
|
||||
<template slot="popover">
|
||||
<div>
|
||||
<button
|
||||
v-close-popover
|
||||
class="icon"
|
||||
@click="
|
||||
$emit('edit-request', {
|
||||
collectionIndex,
|
||||
folderIndex,
|
||||
folderName,
|
||||
request,
|
||||
requestIndex,
|
||||
})
|
||||
"
|
||||
>
|
||||
<i class="material-icons">edit</i>
|
||||
<span>{{ $t("edit") }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<button v-close-popover class="icon" @click="confirmRemove = true">
|
||||
<i class="material-icons">delete</i>
|
||||
<span>{{ $t("delete") }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
</v-popover>
|
||||
</div>
|
||||
<SmartConfirmModal
|
||||
:show="confirmRemove"
|
||||
:title="$t('are_you_sure_remove_request')"
|
||||
@hide-modal="confirmRemove = false"
|
||||
@resolve="removeRequest"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
request: { type: Object, default: () => {} },
|
||||
collectionIndex: { type: Number, default: null },
|
||||
folderIndex: { type: Number, default: null },
|
||||
folderName: { type: String, default: null },
|
||||
// eslint-disable-next-line vue/require-default-prop
|
||||
requestIndex: [Number, String],
|
||||
doc: Boolean,
|
||||
saveRequest: Boolean,
|
||||
collectionsType: { type: Object, default: () => {} },
|
||||
picked: { type: Object, default: () => {} },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dragging: false,
|
||||
requestMethodLabels: {
|
||||
get: "text-green-400",
|
||||
post: "text-yellow-400",
|
||||
put: "text-blue-400",
|
||||
delete: "text-red-400",
|
||||
default: "text-gray-400",
|
||||
},
|
||||
confirmRemove: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isSelected() {
|
||||
return (
|
||||
this.picked &&
|
||||
this.picked.pickedType === "teams-request" &&
|
||||
this.picked.requestID === this.requestIndex
|
||||
)
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
selectRequest() {
|
||||
if (this.$props.saveRequest)
|
||||
this.$emit("select", {
|
||||
picked: {
|
||||
pickedType: "teams-request",
|
||||
requestID: this.requestIndex,
|
||||
},
|
||||
})
|
||||
else
|
||||
this.$store.commit("postwoman/selectRequest", { request: this.request })
|
||||
},
|
||||
removeRequest() {
|
||||
this.$emit("remove-request", {
|
||||
collectionIndex: this.$props.collectionIndex,
|
||||
folderName: this.$props.folderName,
|
||||
requestIndex: this.$props.requestIndex,
|
||||
})
|
||||
},
|
||||
getRequestLabelColor(method) {
|
||||
return (
|
||||
this.requestMethodLabels[method.toLowerCase()] ||
|
||||
this.requestMethodLabels.default
|
||||
)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
43
components/docs/Collection.vue
Normal file
43
components/docs/Collection.vue
Normal file
@@ -0,0 +1,43 @@
|
||||
<template>
|
||||
<div class="collection">
|
||||
<h2>
|
||||
<i class="material-icons">folder</i>
|
||||
{{ collection.name || $t("none") }}
|
||||
</h2>
|
||||
<span
|
||||
v-for="(folder, index) in collection.folders"
|
||||
:key="`sub-collection-${index}`"
|
||||
class="folder"
|
||||
>
|
||||
<DocsFolder :folder="folder" />
|
||||
</span>
|
||||
<div
|
||||
v-for="(request, index) in collection.requests"
|
||||
:key="`request-${index}`"
|
||||
>
|
||||
<DocsRequest :request="request" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
collection: { type: Object, default: () => {} },
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.collection {
|
||||
@apply flex;
|
||||
@apply flex-col;
|
||||
@apply justify-center;
|
||||
@apply flex-1;
|
||||
@apply p-4;
|
||||
|
||||
.material-icons {
|
||||
@apply mr-4;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
39
components/docs/Folder.vue
Normal file
39
components/docs/Folder.vue
Normal file
@@ -0,0 +1,39 @@
|
||||
<template>
|
||||
<div class="folder">
|
||||
<h3>
|
||||
<i class="material-icons">folder_open</i>
|
||||
{{ folder.name || $t("none") }}
|
||||
</h3>
|
||||
<div v-for="(subFolder, index) in folder.folders" :key="index">
|
||||
<DocsFolder :folder="subFolder" />
|
||||
</div>
|
||||
<div v-for="(request, index) in folder.requests" :key="index">
|
||||
<DocsRequest :request="request" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
folder: { type: Object, default: () => {} },
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.folder {
|
||||
@apply flex;
|
||||
@apply flex-col;
|
||||
@apply justify-center;
|
||||
@apply flex-1;
|
||||
@apply p-4;
|
||||
@apply border-l;
|
||||
@apply border-brdColor;
|
||||
@apply mt-4;
|
||||
|
||||
.material-icons {
|
||||
@apply mr-4;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
153
components/docs/Request.vue
Normal file
153
components/docs/Request.vue
Normal file
@@ -0,0 +1,153 @@
|
||||
<template>
|
||||
<div class="request">
|
||||
<h4>
|
||||
<i class="material-icons">insert_drive_file</i>
|
||||
{{ request.name || $t("none") }}
|
||||
</h4>
|
||||
<p v-if="request.url" class="doc-desc">
|
||||
<span>
|
||||
{{ $t("url") }}: <code>{{ request.url || $t("none") }}</code>
|
||||
</span>
|
||||
</p>
|
||||
<p v-if="request.path" class="doc-desc">
|
||||
<span>
|
||||
{{ $t("path") }}:
|
||||
<code>{{ request.path || $t("none") }}</code>
|
||||
</span>
|
||||
</p>
|
||||
<p v-if="request.method" class="doc-desc">
|
||||
<span>
|
||||
{{ $t("method") }}:
|
||||
<code>{{ request.method || $t("none") }}</code>
|
||||
</span>
|
||||
</p>
|
||||
<p v-if="request.auth" class="doc-desc">
|
||||
<span>
|
||||
{{ $t("authentication") }}:
|
||||
<code>{{ request.auth || $t("none") }}</code>
|
||||
</span>
|
||||
</p>
|
||||
<p v-if="request.httpUser" class="doc-desc">
|
||||
<span>
|
||||
{{ $t("username") }}:
|
||||
<code>{{ request.httpUser || $t("none") }}</code>
|
||||
</span>
|
||||
</p>
|
||||
<p v-if="request.httpPassword" class="doc-desc">
|
||||
<span>
|
||||
{{ $t("password") }}:
|
||||
<code>{{ request.httpPassword || $t("none") }}</code>
|
||||
</span>
|
||||
</p>
|
||||
<p v-if="request.bearerToken" class="doc-desc">
|
||||
<span>
|
||||
{{ $t("token") }}:
|
||||
<code>{{ request.bearerToken || $t("none") }}</code>
|
||||
</span>
|
||||
</p>
|
||||
<h4 v-if="request.headers">{{ $t("headers") }}</h4>
|
||||
<span v-if="request.headers">
|
||||
<p v-for="header in request.headers" :key="header.key" class="doc-desc">
|
||||
<span>
|
||||
{{ header.key || $t("none") }}:
|
||||
<code>{{ header.value || $t("none") }}</code>
|
||||
</span>
|
||||
</p>
|
||||
</span>
|
||||
<h4 v-if="request.params">{{ $t("parameters") }}</h4>
|
||||
<span v-if="request.params">
|
||||
<p
|
||||
v-for="parameter in request.params"
|
||||
:key="parameter.key"
|
||||
class="doc-desc"
|
||||
>
|
||||
<span>
|
||||
{{ parameter.key || $t("none") }}:
|
||||
<code>{{ parameter.value || $t("none") }}</code>
|
||||
</span>
|
||||
</p>
|
||||
</span>
|
||||
<h4 v-if="request.bodyParams">{{ $t("payload") }}</h4>
|
||||
<span v-if="request.bodyParams">
|
||||
<p
|
||||
v-for="payload in request.bodyParams"
|
||||
:key="payload.key"
|
||||
class="doc-desc"
|
||||
>
|
||||
<span>
|
||||
{{ payload.key || $t("none") }}:
|
||||
<code>{{ payload.value || $t("none") }}</code>
|
||||
</span>
|
||||
</p>
|
||||
</span>
|
||||
<p v-if="request.rawParams" class="doc-desc">
|
||||
<span>
|
||||
{{ $t("parameters") }}:
|
||||
<code>{{ request.rawParams || $t("none") }}</code>
|
||||
</span>
|
||||
</p>
|
||||
<p v-if="request.contentType" class="doc-desc">
|
||||
<span>
|
||||
{{ $t("content_type") }}:
|
||||
<code>{{ request.contentType || $t("none") }}</code>
|
||||
</span>
|
||||
</p>
|
||||
<p v-if="request.requestType" class="doc-desc">
|
||||
<span>
|
||||
{{ $t("request_type") }}:
|
||||
<code>{{ request.requestType || $t("none") }}</code>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
request: { type: Object, default: () => {} },
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.request {
|
||||
@apply flex;
|
||||
@apply flex-col;
|
||||
@apply justify-center;
|
||||
@apply flex-1;
|
||||
@apply p-4;
|
||||
@apply border;
|
||||
@apply border-brdColor;
|
||||
@apply rounded-lg;
|
||||
@apply mt-4;
|
||||
|
||||
h4 {
|
||||
@apply mt-4;
|
||||
}
|
||||
|
||||
.material-icons {
|
||||
@apply mr-4;
|
||||
}
|
||||
}
|
||||
|
||||
.doc-desc {
|
||||
@apply flex;
|
||||
@apply flex-col;
|
||||
@apply justify-center;
|
||||
@apply flex-1;
|
||||
@apply p-4;
|
||||
@apply text-fgLightColor;
|
||||
@apply border-b;
|
||||
@apply border-dashed;
|
||||
@apply border-brdColor;
|
||||
@apply m-0;
|
||||
|
||||
&:last-child {
|
||||
@apply border-b-0;
|
||||
}
|
||||
|
||||
.material-icons {
|
||||
@apply mr-4;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -13,9 +13,9 @@
|
||||
<div slot="body" class="flex flex-col">
|
||||
<label for="selectLabel">{{ $t("label") }}</label>
|
||||
<input
|
||||
type="text"
|
||||
id="selectLabel"
|
||||
v-model="name"
|
||||
type="text"
|
||||
:placeholder="$t('my_new_environment')"
|
||||
@keyup.enter="addNewEnvironment"
|
||||
/>
|
||||
@@ -46,18 +46,20 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
name: undefined,
|
||||
name: null,
|
||||
}
|
||||
},
|
||||
subscriptions() {
|
||||
return {
|
||||
SYNC_ENVIRONMENTS: getSettingSubject("syncEnvironments")
|
||||
SYNC_ENVIRONMENTS: getSettingSubject("syncEnvironments"),
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
syncEnvironments() {
|
||||
if (fb.currentUser !== null && this.SYNC_ENVIRONMENTS) {
|
||||
fb.writeEnvironments(JSON.parse(JSON.stringify(this.$store.state.postwoman.environments)))
|
||||
fb.writeEnvironments(
|
||||
JSON.parse(JSON.stringify(this.$store.state.postwoman.environments))
|
||||
)
|
||||
}
|
||||
},
|
||||
addNewEnvironment() {
|
||||
@@ -65,7 +67,7 @@ export default {
|
||||
this.$toast.info(this.$t("invalid_environment_name"))
|
||||
return
|
||||
}
|
||||
let newEnvironment = [
|
||||
const newEnvironment = [
|
||||
{
|
||||
name: this.$data.name,
|
||||
variables: [],
|
||||
@@ -75,12 +77,12 @@ export default {
|
||||
environments: newEnvironment,
|
||||
confirmation: "Environment added",
|
||||
})
|
||||
this.$emit("hide-modal")
|
||||
this.syncEnvironments()
|
||||
this.hideModal()
|
||||
},
|
||||
hideModal() {
|
||||
this.name = null
|
||||
this.$emit("hide-modal")
|
||||
this.$data.name = undefined
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -13,24 +13,35 @@
|
||||
<div slot="body" class="flex flex-col">
|
||||
<label for="selectLabel">{{ $t("label") }}</label>
|
||||
<input
|
||||
type="text"
|
||||
id="selectLabel"
|
||||
v-model="name"
|
||||
type="text"
|
||||
:placeholder="editingEnvironment.name"
|
||||
@keyup.enter="saveEnvironment"
|
||||
/>
|
||||
<div class="row-wrapper">
|
||||
<label for="variableList">{{ $t("env_variable_list") }}</label>
|
||||
<div>
|
||||
<button class="icon" @click="clearContent($event)" v-tooltip.bottom="$t('clear')">
|
||||
<button
|
||||
v-tooltip.bottom="$t('clear')"
|
||||
class="icon"
|
||||
@click="clearContent($event)"
|
||||
>
|
||||
<i class="material-icons">clear_all</i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<ul
|
||||
v-for="(variable, index) in this.editingEnvCopy.variables"
|
||||
v-for="(variable, index) in editingEnvCopy.variables"
|
||||
:key="index"
|
||||
class="border-b border-dashed divide-y md:divide-x border-brdColor divide-dashed divide-brdColor md:divide-y-0"
|
||||
class="
|
||||
border-b border-dashed
|
||||
divide-y
|
||||
md:divide-x
|
||||
border-brdColor
|
||||
divide-dashed divide-brdColor
|
||||
md:divide-y-0
|
||||
"
|
||||
:class="{ 'border-t': index == 0 }"
|
||||
>
|
||||
<li>
|
||||
@@ -38,13 +49,13 @@
|
||||
:placeholder="$t('variable_count', { count: index + 1 })"
|
||||
:name="'param' + index"
|
||||
:value="variable.key"
|
||||
autofocus
|
||||
@change="
|
||||
$store.commit('postwoman/setVariableKey', {
|
||||
index,
|
||||
value: $event.target.value,
|
||||
})
|
||||
"
|
||||
autofocus
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
@@ -52,7 +63,9 @@
|
||||
:placeholder="$t('value_count', { count: index + 1 })"
|
||||
:name="'value' + index"
|
||||
:value="
|
||||
typeof variable.value === 'string' ? variable.value : JSON.stringify(variable.value)
|
||||
typeof variable.value === 'string'
|
||||
? variable.value
|
||||
: JSON.stringify(variable.value)
|
||||
"
|
||||
@change="
|
||||
$store.commit('postwoman/setVariableValue', {
|
||||
@@ -65,10 +78,10 @@
|
||||
<div>
|
||||
<li>
|
||||
<button
|
||||
id="variable"
|
||||
v-tooltip.bottom="$t('delete')"
|
||||
class="icon"
|
||||
@click="removeEnvironmentVariable(index)"
|
||||
v-tooltip.bottom="$t('delete')"
|
||||
id="variable"
|
||||
>
|
||||
<i class="material-icons">delete</i>
|
||||
</button>
|
||||
@@ -107,29 +120,20 @@ import { getSettingSubject } from "~/newstore/settings"
|
||||
export default {
|
||||
props: {
|
||||
show: Boolean,
|
||||
editingEnvironment: Object,
|
||||
editingEnvironmentIndex: Number,
|
||||
editingEnvironment: { type: Object, default: () => {} },
|
||||
editingEnvironmentIndex: { type: Number, default: null },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
name: undefined,
|
||||
name: null,
|
||||
doneButton: '<i class="material-icons">done</i>',
|
||||
}
|
||||
},
|
||||
subscriptions() {
|
||||
return {
|
||||
SYNC_ENVIRONMENTS: getSettingSubject("syncEnvironments")
|
||||
SYNC_ENVIRONMENTS: getSettingSubject("syncEnvironments"),
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
editingEnvironment(update) {
|
||||
this.name =
|
||||
this.$props.editingEnvironment && this.$props.editingEnvironment.name
|
||||
? this.$props.editingEnvironment.name
|
||||
: undefined
|
||||
this.$store.commit("postwoman/setEditingEnvironment", this.$props.editingEnvironment)
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
editingEnvCopy() {
|
||||
return this.$store.state.postwoman.editingEnvironment
|
||||
@@ -139,10 +143,24 @@ export default {
|
||||
return result === "" ? "" : JSON.stringify(result)
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
editingEnvironment() {
|
||||
this.name =
|
||||
this.$props.editingEnvironment && this.$props.editingEnvironment.name
|
||||
? this.$props.editingEnvironment.name
|
||||
: undefined
|
||||
this.$store.commit(
|
||||
"postwoman/setEditingEnvironment",
|
||||
this.$props.editingEnvironment
|
||||
)
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
syncEnvironments() {
|
||||
if (fb.currentUser !== null && this.SYNC_ENVIRONMENTS) {
|
||||
fb.writeEnvironments(JSON.parse(JSON.stringify(this.$store.state.postwoman.environments)))
|
||||
fb.writeEnvironments(
|
||||
JSON.parse(JSON.stringify(this.$store.state.postwoman.environments))
|
||||
)
|
||||
}
|
||||
},
|
||||
clearContent({ target }) {
|
||||
@@ -151,18 +169,21 @@ export default {
|
||||
this.$toast.info(this.$t("cleared"), {
|
||||
icon: "clear_all",
|
||||
})
|
||||
setTimeout(() => (target.innerHTML = '<i class="material-icons">clear_all</i>'), 1000)
|
||||
setTimeout(
|
||||
() => (target.innerHTML = '<i class="material-icons">clear_all</i>'),
|
||||
1000
|
||||
)
|
||||
},
|
||||
addEnvironmentVariable() {
|
||||
let value = { key: "", value: "" }
|
||||
const value = { key: "", value: "" }
|
||||
this.$store.commit("postwoman/addVariable", value)
|
||||
this.syncEnvironments()
|
||||
},
|
||||
removeEnvironmentVariable(index) {
|
||||
let variableIndex = index
|
||||
const variableIndex = index
|
||||
const oldVariables = this.editingEnvCopy.variables.slice()
|
||||
const newVariables = this.editingEnvCopy.variables.filter(
|
||||
(variable, index) => variableIndex !== index
|
||||
(_, index) => variableIndex !== index
|
||||
)
|
||||
|
||||
this.$store.commit("postwoman/removeVariable", newVariables)
|
||||
@@ -170,7 +191,7 @@ export default {
|
||||
icon: "delete",
|
||||
action: {
|
||||
text: this.$t("undo"),
|
||||
onClick: (e, toastObject) => {
|
||||
onClick: (_, toastObject) => {
|
||||
this.$store.commit("postwoman/removeVariable", oldVariables)
|
||||
toastObject.remove()
|
||||
},
|
||||
@@ -191,12 +212,12 @@ export default {
|
||||
environment: environmentUpdated,
|
||||
environmentIndex: this.$props.editingEnvironmentIndex,
|
||||
})
|
||||
this.$emit("hide-modal")
|
||||
this.syncEnvironments()
|
||||
this.hideModal()
|
||||
},
|
||||
hideModal() {
|
||||
this.name = null
|
||||
this.$emit("hide-modal")
|
||||
this.$data.name = undefined
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -8,18 +8,22 @@
|
||||
</button>
|
||||
</div>
|
||||
<v-popover>
|
||||
<button class="tooltip-target icon" v-tooltip.left="$t('more')">
|
||||
<button v-tooltip.left="$t('more')" class="tooltip-target icon">
|
||||
<i class="material-icons">more_vert</i>
|
||||
</button>
|
||||
<template slot="popover">
|
||||
<div>
|
||||
<button class="icon" @click="$emit('edit-environment')" v-close-popover>
|
||||
<button
|
||||
v-close-popover
|
||||
class="icon"
|
||||
@click="$emit('edit-environment')"
|
||||
>
|
||||
<i class="material-icons">create</i>
|
||||
<span>{{ $t("edit") }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<button class="icon" @click="confirmRemove = true" v-close-popover>
|
||||
<button v-close-popover class="icon" @click="confirmRemove = true">
|
||||
<i class="material-icons">delete</i>
|
||||
<span>{{ $t("delete") }}</span>
|
||||
</button>
|
||||
@@ -42,8 +46,8 @@ import { getSettingSubject } from "~/newstore/settings"
|
||||
|
||||
export default {
|
||||
props: {
|
||||
environment: Object,
|
||||
environmentIndex: Number,
|
||||
environment: { type: Object, default: () => {} },
|
||||
environmentIndex: { type: Number, default: null },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -52,13 +56,15 @@ export default {
|
||||
},
|
||||
subscriptions() {
|
||||
return {
|
||||
SYNC_ENVIRONMENTS: getSettingSubject("syncEnvironments")
|
||||
SYNC_ENVIRONMENTS: getSettingSubject("syncEnvironments"),
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
syncEnvironments() {
|
||||
if (fb.currentUser !== null && this.SYNC_ENVIRONMENTS) {
|
||||
fb.writeEnvironments(JSON.parse(JSON.stringify(this.$store.state.postwoman.environments)))
|
||||
fb.writeEnvironments(
|
||||
JSON.parse(JSON.stringify(this.$store.state.postwoman.environments))
|
||||
)
|
||||
}
|
||||
},
|
||||
removeEnvironment() {
|
||||
|
||||
@@ -2,15 +2,21 @@
|
||||
<SmartModal v-if="show" @close="hideModal">
|
||||
<div slot="header">
|
||||
<div class="row-wrapper">
|
||||
<h3 class="title">{{ $t("import_export") }} {{ $t("environments") }}</h3>
|
||||
<h3 class="title">
|
||||
{{ $t("import_export") }} {{ $t("environments") }}
|
||||
</h3>
|
||||
<div>
|
||||
<v-popover>
|
||||
<button class="tooltip-target icon" v-tooltip.left="$t('more')">
|
||||
<button v-tooltip.left="$t('more')" class="tooltip-target icon">
|
||||
<i class="material-icons">more_vert</i>
|
||||
</button>
|
||||
<template slot="popover">
|
||||
<div>
|
||||
<button class="icon" @click="readEnvironmentGist" v-close-popover>
|
||||
<button
|
||||
v-close-popover
|
||||
class="icon"
|
||||
@click="readEnvironmentGist"
|
||||
>
|
||||
<i class="material-icons">assignment_returned</i>
|
||||
<span>{{ $t("import_from_gist") }}</span>
|
||||
</button>
|
||||
@@ -25,12 +31,16 @@
|
||||
}"
|
||||
>
|
||||
<button
|
||||
v-close-popover
|
||||
:disabled="
|
||||
!fb.currentUser ? true : fb.currentUser.provider !== 'github.com' ? true : false
|
||||
!fb.currentUser
|
||||
? true
|
||||
: fb.currentUser.provider !== 'github.com'
|
||||
? true
|
||||
: false
|
||||
"
|
||||
class="icon"
|
||||
@click="createEnvironmentGist"
|
||||
v-close-popover
|
||||
>
|
||||
<i class="material-icons">assignment_turned_in</i>
|
||||
<span>{{ $t("create_secret_gist") }}</span>
|
||||
@@ -48,42 +58,48 @@
|
||||
<div class="flex flex-col items-start p-2">
|
||||
<span
|
||||
v-tooltip="{
|
||||
content: !fb.currentUser ? $t('login_first') : $t('replace_current'),
|
||||
content: !fb.currentUser
|
||||
? $t('login_first')
|
||||
: $t('replace_current'),
|
||||
}"
|
||||
>
|
||||
<button :disabled="!fb.currentUser" class="icon" @click="syncEnvironments">
|
||||
<button
|
||||
:disabled="!fb.currentUser"
|
||||
class="icon"
|
||||
@click="syncEnvironments"
|
||||
>
|
||||
<i class="material-icons">folder_shared</i>
|
||||
<span>{{ $t("import_from_sync") }}</span>
|
||||
</button>
|
||||
</span>
|
||||
<button
|
||||
v-tooltip="$t('replace_current')"
|
||||
class="icon"
|
||||
@click="openDialogChooseFileToReplaceWith"
|
||||
v-tooltip="$t('replace_current')"
|
||||
>
|
||||
<i class="material-icons">create_new_folder</i>
|
||||
<span>{{ $t("replace_json") }}</span>
|
||||
<input
|
||||
type="file"
|
||||
@change="replaceWithJSON"
|
||||
style="display: none"
|
||||
ref="inputChooseFileToReplaceWith"
|
||||
type="file"
|
||||
style="display: none"
|
||||
accept="application/json"
|
||||
@change="replaceWithJSON"
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
v-tooltip="$t('preserve_current')"
|
||||
class="icon"
|
||||
@click="openDialogChooseFileToImportFrom"
|
||||
v-tooltip="$t('preserve_current')"
|
||||
>
|
||||
<i class="material-icons">folder_special</i>
|
||||
<span>{{ $t("import_json") }}</span>
|
||||
<input
|
||||
type="file"
|
||||
@change="importFromJSON"
|
||||
style="display: none"
|
||||
ref="inputChooseFileToImportFrom"
|
||||
type="file"
|
||||
style="display: none"
|
||||
accept="application/json"
|
||||
@change="importFromJSON"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
@@ -102,7 +118,11 @@
|
||||
<button class="icon" @click="hideModal">
|
||||
{{ $t("cancel") }}
|
||||
</button>
|
||||
<button class="icon primary" @click="exportJSON" v-tooltip="$t('download_file')">
|
||||
<button
|
||||
v-tooltip="$t('download_file')"
|
||||
class="icon primary"
|
||||
@click="exportJSON"
|
||||
>
|
||||
{{ $t("export") }}
|
||||
</button>
|
||||
</span>
|
||||
@@ -116,6 +136,9 @@ import { fb } from "~/helpers/fb"
|
||||
import { getSettingSubject } from "~/newstore/settings"
|
||||
|
||||
export default {
|
||||
props: {
|
||||
show: Boolean,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
fb,
|
||||
@@ -124,12 +147,9 @@ export default {
|
||||
},
|
||||
subscriptions() {
|
||||
return {
|
||||
SYNC_ENVIRONMENTS: getSettingSubject("syncEnvironments")
|
||||
SYNC_ENVIRONMENTS: getSettingSubject("syncEnvironments"),
|
||||
}
|
||||
},
|
||||
props: {
|
||||
show: Boolean,
|
||||
},
|
||||
computed: {
|
||||
environmentJson() {
|
||||
return JSON.stringify(this.$store.state.postwoman.environments, null, 2)
|
||||
@@ -154,11 +174,11 @@ export default {
|
||||
},
|
||||
}
|
||||
)
|
||||
.then(({ html_url }) => {
|
||||
.then((res) => {
|
||||
this.$toast.success(this.$t("gist_created"), {
|
||||
icon: "done",
|
||||
})
|
||||
window.open(html_url)
|
||||
window.open(res.html_url)
|
||||
})
|
||||
.catch((error) => {
|
||||
this.$toast.error(this.$t("something_went_wrong"), {
|
||||
@@ -168,7 +188,7 @@ export default {
|
||||
})
|
||||
},
|
||||
async readEnvironmentGist() {
|
||||
let gist = prompt(this.$t("enter_gist_url"))
|
||||
const gist = prompt(this.$t("enter_gist_url"))
|
||||
if (!gist) return
|
||||
await this.$axios
|
||||
.$get(`https://api.github.com/gists/${gist.split("/").pop()}`, {
|
||||
@@ -177,7 +197,7 @@ export default {
|
||||
},
|
||||
})
|
||||
.then(({ files }) => {
|
||||
let environments = JSON.parse(Object.values(files)[0].content)
|
||||
const environments = JSON.parse(Object.values(files)[0].content)
|
||||
this.$store.commit("postwoman/replaceEnvironments", environments)
|
||||
this.fileImported()
|
||||
this.syncToFBEnvironments()
|
||||
@@ -197,10 +217,10 @@ export default {
|
||||
this.$refs.inputChooseFileToImportFrom.click()
|
||||
},
|
||||
replaceWithJSON() {
|
||||
let reader = new FileReader()
|
||||
const reader = new FileReader()
|
||||
reader.onload = ({ target }) => {
|
||||
let content = target.result
|
||||
let environments = JSON.parse(content)
|
||||
const content = target.result
|
||||
const environments = JSON.parse(content)
|
||||
this.$store.commit("postwoman/replaceEnvironments", environments)
|
||||
}
|
||||
reader.readAsText(this.$refs.inputChooseFileToReplaceWith.files[0])
|
||||
@@ -209,13 +229,13 @@ export default {
|
||||
this.$refs.inputChooseFileToReplaceWith.value = ""
|
||||
},
|
||||
importFromJSON() {
|
||||
let reader = new FileReader()
|
||||
const reader = new FileReader()
|
||||
reader.onload = ({ target }) => {
|
||||
let content = target.result
|
||||
let importFileObj = JSON.parse(content)
|
||||
const content = target.result
|
||||
const importFileObj = JSON.parse(content)
|
||||
if (
|
||||
importFileObj["_postman_variable_scope"] === "environment" ||
|
||||
importFileObj["_postman_variable_scope"] === "globals"
|
||||
importFileObj._postman_variable_scope === "environment" ||
|
||||
importFileObj._postman_variable_scope === "globals"
|
||||
) {
|
||||
this.importFromPostman(importFileObj)
|
||||
} else {
|
||||
@@ -227,25 +247,27 @@ export default {
|
||||
this.$refs.inputChooseFileToImportFrom.value = ""
|
||||
},
|
||||
importFromPostwoman(environments) {
|
||||
let confirmation = this.$t("file_imported")
|
||||
const confirmation = this.$t("file_imported")
|
||||
this.$store.commit("postwoman/importAddEnvironments", {
|
||||
environments,
|
||||
confirmation,
|
||||
})
|
||||
},
|
||||
importFromPostman({ name, values }) {
|
||||
let environment = { name, variables: [] }
|
||||
values.forEach(({ key, value }) => environment.variables.push({ key, value }))
|
||||
let environments = [environment]
|
||||
const environment = { name, variables: [] }
|
||||
values.forEach(({ key, value }) =>
|
||||
environment.variables.push({ key, value })
|
||||
)
|
||||
const environments = [environment]
|
||||
this.importFromPostwoman(environments)
|
||||
},
|
||||
exportJSON() {
|
||||
let text = this.environmentJson
|
||||
text = text.replace(/\n/g, "\r\n")
|
||||
let blob = new Blob([text], {
|
||||
const blob = new Blob([text], {
|
||||
type: "text/json",
|
||||
})
|
||||
let anchor = document.createElement("a")
|
||||
const anchor = document.createElement("a")
|
||||
anchor.download = "hoppscotch-environment.json"
|
||||
anchor.href = window.URL.createObjectURL(blob)
|
||||
anchor.target = "_blank"
|
||||
@@ -258,12 +280,17 @@ export default {
|
||||
})
|
||||
},
|
||||
syncEnvironments() {
|
||||
this.$store.commit("postwoman/replaceEnvironments", fb.currentEnvironments)
|
||||
this.$store.commit(
|
||||
"postwoman/replaceEnvironments",
|
||||
fb.currentEnvironments
|
||||
)
|
||||
this.fileImported()
|
||||
},
|
||||
syncToFBEnvironments() {
|
||||
if (fb.currentUser !== null && this.SYNC_ENVIRONMENTS) {
|
||||
fb.writeEnvironments(JSON.parse(JSON.stringify(this.$store.state.postwoman.environments)))
|
||||
fb.writeEnvironments(
|
||||
JSON.parse(JSON.stringify(this.$store.state.postwoman.environments))
|
||||
)
|
||||
}
|
||||
},
|
||||
fileImported() {
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
<template>
|
||||
<AppSection icon="history" :label="$t('environments')" ref="environments" no-legend>
|
||||
<AppSection
|
||||
ref="environments"
|
||||
icon="history"
|
||||
:label="$t('environments')"
|
||||
no-legend
|
||||
>
|
||||
<div class="show-on-large-screen">
|
||||
<span class="select-wrapper">
|
||||
<select
|
||||
@@ -11,17 +16,24 @@
|
||||
<option v-if="environments.length === 0" value="0">
|
||||
{{ $t("create_new_environment") }}
|
||||
</option>
|
||||
<option v-for="(environment, index) in environments" :value="index" :key="index">
|
||||
<option
|
||||
v-for="(environment, index) in environments"
|
||||
:key="index"
|
||||
:value="index"
|
||||
>
|
||||
{{ environment.name }}
|
||||
</option>
|
||||
</select>
|
||||
</span>
|
||||
</div>
|
||||
<EnvironmentsAdd :show="showModalAdd" @hide-modal="displayModalAdd(false)" />
|
||||
<EnvironmentsAdd
|
||||
:show="showModalAdd"
|
||||
@hide-modal="displayModalAdd(false)"
|
||||
/>
|
||||
<EnvironmentsEdit
|
||||
:show="showModalEdit"
|
||||
:editingEnvironment="editingEnvironment"
|
||||
:editingEnvironmentIndex="editingEnvironmentIndex"
|
||||
:editing-environment="editingEnvironment"
|
||||
:editing-environment-index="editingEnvironmentIndex"
|
||||
@hide-modal="displayModalEdit(false)"
|
||||
/>
|
||||
<EnvironmentsImportExport
|
||||
@@ -42,13 +54,17 @@
|
||||
</div>
|
||||
</div>
|
||||
<p v-if="environments.length === 0" class="info">
|
||||
<i class="material-icons">help_outline</i> {{ $t("create_new_environment") }}
|
||||
<i class="material-icons">help_outline</i>
|
||||
{{ $t("create_new_environment") }}
|
||||
</p>
|
||||
<div class="virtual-list">
|
||||
<ul class="flex-col">
|
||||
<li v-for="(environment, index) in environments" :key="environment.name">
|
||||
<li
|
||||
v-for="(environment, index) in environments"
|
||||
:key="environment.name"
|
||||
>
|
||||
<EnvironmentsEnvironment
|
||||
:environmentIndex="index"
|
||||
:environment-index="index"
|
||||
:environment="environment"
|
||||
@edit-environment="editEnvironment(environment, index)"
|
||||
/>
|
||||
@@ -58,12 +74,6 @@
|
||||
</AppSection>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.virtual-list {
|
||||
max-height: calc(100vh - 270px);
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import { fb } from "~/helpers/fb"
|
||||
import { getSettingSubject } from "~/newstore/settings"
|
||||
@@ -116,26 +126,30 @@ export default {
|
||||
environment: this.defaultEnvironment,
|
||||
environments: this.environments,
|
||||
})
|
||||
} else {
|
||||
if (this.environments[this.selectedEnvironmentIndex])
|
||||
this.$emit("use-environment", {
|
||||
environment: this.environments[this.selectedEnvironmentIndex],
|
||||
environments: this.environments,
|
||||
})
|
||||
else this.selectedEnvironmentIndex = -1
|
||||
}
|
||||
} else if (this.environments[this.selectedEnvironmentIndex])
|
||||
this.$emit("use-environment", {
|
||||
environment: this.environments[this.selectedEnvironmentIndex],
|
||||
environments: this.environments,
|
||||
})
|
||||
else this.selectedEnvironmentIndex = -1
|
||||
},
|
||||
},
|
||||
},
|
||||
async mounted() {
|
||||
mounted() {
|
||||
this._keyListener = function (e) {
|
||||
if (e.key === "Escape") {
|
||||
e.preventDefault()
|
||||
this.showModalImportExport = this.showModalAdd = this.showModalEdit = false
|
||||
this.showModalImportExport =
|
||||
this.showModalAdd =
|
||||
this.showModalEdit =
|
||||
false
|
||||
}
|
||||
}
|
||||
document.addEventListener("keydown", this._keyListener.bind(this))
|
||||
},
|
||||
beforeDestroy() {
|
||||
document.removeEventListener("keydown", this._keyListener)
|
||||
},
|
||||
methods: {
|
||||
displayModalAdd(shouldDisplay) {
|
||||
this.showModalAdd = shouldDisplay
|
||||
@@ -160,12 +174,17 @@ export default {
|
||||
},
|
||||
syncEnvironments() {
|
||||
if (fb.currentUser !== null && this.SYNC_ENVIRONMENTS) {
|
||||
fb.writeEnvironments(JSON.parse(JSON.stringify(this.$store.state.postwoman.environments)))
|
||||
fb.writeEnvironments(
|
||||
JSON.parse(JSON.stringify(this.$store.state.postwoman.environments))
|
||||
)
|
||||
}
|
||||
},
|
||||
},
|
||||
beforeDestroy() {
|
||||
document.removeEventListener("keydown", this._keyListener)
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.virtual-list {
|
||||
max-height: calc(100vh - 270px);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -28,19 +28,6 @@
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.virtual-list {
|
||||
max-height: calc(100vh - 270px);
|
||||
}
|
||||
|
||||
.clamb-3 {
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 3;
|
||||
-webkit-box-orient: vertical;
|
||||
@apply overflow-hidden;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import { fb } from "~/helpers/fb"
|
||||
|
||||
@@ -60,3 +47,16 @@ export default {
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.virtual-list {
|
||||
max-height: calc(100vh - 270px);
|
||||
}
|
||||
|
||||
.clamb-3 {
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 3;
|
||||
-webkit-box-orient: vertical;
|
||||
@apply overflow-hidden;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -2,25 +2,30 @@
|
||||
<div class="flex-col">
|
||||
<div class="show-on-large-screen">
|
||||
<input
|
||||
v-model="message"
|
||||
:aria-label="$t('label')"
|
||||
type="text"
|
||||
autofocus
|
||||
v-model="message"
|
||||
:placeholder="$t('paste_a_note')"
|
||||
@keyup.enter="formPost"
|
||||
class="rounded-t-lg"
|
||||
@keyup.enter="formPost"
|
||||
/>
|
||||
</div>
|
||||
<div class="border-b show-on-large-screen border-brdColor">
|
||||
<input
|
||||
v-model="label"
|
||||
:aria-label="$t('label')"
|
||||
type="text"
|
||||
autofocus
|
||||
v-model="label"
|
||||
:placeholder="$t('label')"
|
||||
@keyup.enter="formPost"
|
||||
/>
|
||||
<button class="icon" :disabled="!(this.message || this.label)" value="Save" @click="formPost">
|
||||
<button
|
||||
class="icon"
|
||||
:disabled="!(message || label)"
|
||||
value="Save"
|
||||
@click="formPost"
|
||||
>
|
||||
<i class="material-icons">add</i>
|
||||
<span>Add</span>
|
||||
</button>
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
<template>
|
||||
<div>
|
||||
<div>
|
||||
<button class="icon" @click="signInWithGoogle" v-close-popover>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" class="material-icons">
|
||||
<button v-close-popover class="icon" @click="signInWithGoogle">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
class="material-icons"
|
||||
>
|
||||
<path
|
||||
d="M12.24 10.285V14.4h6.806c-.275 1.765-2.056 5.174-6.806 5.174-4.095 0-7.439-3.389-7.439-7.574s3.345-7.574 7.439-7.574c2.33 0 3.891.989 4.785 1.849l3.254-3.138C18.189 1.186 15.479 0 12.24 0c-6.635 0-12 5.365-12 12s5.365 12 12 12c6.926 0 11.52-4.869 11.52-11.726 0-.788-.085-1.39-.189-1.989H12.24z"
|
||||
/>
|
||||
@@ -11,8 +16,13 @@
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<button class="icon" @click="signInWithGithub" v-close-popover>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" class="material-icons">
|
||||
<button v-close-popover class="icon" @click="signInWithGithub">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
class="material-icons"
|
||||
>
|
||||
<path
|
||||
d="M12 0C5.374 0 0 5.373 0 12c0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23A11.509 11.509 0 0112 5.803c1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576C20.566 21.797 24 17.3 24 12c0-6.627-5.373-12-12-12z"
|
||||
/>
|
||||
@@ -49,7 +59,7 @@ export default {
|
||||
closeOnSwipe: false,
|
||||
action: {
|
||||
text: this.$t("yes"),
|
||||
onClick: (e, toastObject) => {
|
||||
onClick: (_, toastObject) => {
|
||||
fb.writeSettings("syncHistory", true)
|
||||
fb.writeSettings("syncCollections", true)
|
||||
fb.writeSettings("syncEnvironments", true)
|
||||
@@ -96,7 +106,7 @@ export default {
|
||||
closeOnSwipe: false,
|
||||
action: {
|
||||
text: this.$t("yes"),
|
||||
onClick: async (e, toastObject) => {
|
||||
onClick: async (_, toastObject) => {
|
||||
const { user } = await fb.signInWithGithub()
|
||||
await user.linkAndRetrieveDataWithCredential(pendingCred)
|
||||
|
||||
@@ -111,7 +121,8 @@ export default {
|
||||
},
|
||||
async signInWithGithub() {
|
||||
try {
|
||||
const { credential, additionalUserInfo } = await fb.signInUserWithGithub()
|
||||
const { credential, additionalUserInfo } =
|
||||
await fb.signInUserWithGithub()
|
||||
|
||||
fb.setProviderInfo(credential.providerId, credential.accessToken)
|
||||
|
||||
@@ -122,7 +133,7 @@ export default {
|
||||
closeOnSwipe: false,
|
||||
action: {
|
||||
text: this.$t("yes"),
|
||||
onClick: (e, toastObject) => {
|
||||
onClick: (_, toastObject) => {
|
||||
fb.writeSettings("syncHistory", true)
|
||||
fb.writeSettings("syncCollections", true)
|
||||
fb.writeSettings("syncEnvironments", true)
|
||||
@@ -169,7 +180,7 @@ export default {
|
||||
closeOnSwipe: false,
|
||||
action: {
|
||||
text: this.$t("yes"),
|
||||
onClick: async (e, toastObject) => {
|
||||
onClick: async (_, toastObject) => {
|
||||
const { user } = await fb.signInUserWithGoogle()
|
||||
await user.linkAndRetrieveDataWithCredential(pendingCred)
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div>
|
||||
<button class="icon" @click="logout" v-close-popover>
|
||||
<button v-close-popover class="icon" @click="logout">
|
||||
<i class="material-icons">exit_to_app</i>
|
||||
<span>{{ $t("logout") }}</span>
|
||||
</button>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import feeds from "../Feeds"
|
||||
import { shallowMount } from "@vue/test-utils"
|
||||
import feeds from "../Feeds"
|
||||
|
||||
import { fb } from "~/helpers/fb"
|
||||
|
||||
jest.mock("~/helpers/fb", () => ({
|
||||
__esModule: true,
|
||||
@@ -27,8 +29,6 @@ jest.mock("~/helpers/fb", () => ({
|
||||
},
|
||||
}))
|
||||
|
||||
import { fb } from "~/helpers/fb"
|
||||
|
||||
const factory = () =>
|
||||
shallowMount(feeds, {
|
||||
mocks: {
|
||||
@@ -53,7 +53,9 @@ describe("feeds", () => {
|
||||
test("renders all the current feeds", () => {
|
||||
const wrapper = factory()
|
||||
|
||||
expect(wrapper.findAll("div[data-test='list-item']").wrappers).toHaveLength(4)
|
||||
expect(wrapper.findAll("div[data-test='list-item']").wrappers).toHaveLength(
|
||||
4
|
||||
)
|
||||
})
|
||||
|
||||
test("feeds with no label displays the 'no_label' message", () => {
|
||||
@@ -63,7 +65,7 @@ describe("feeds", () => {
|
||||
wrapper
|
||||
.findAll("label[data-test='list-label']")
|
||||
.wrappers.map((e) => e.text())
|
||||
.filter((text) => text == "no_label")
|
||||
.filter((text) => text === "no_label")
|
||||
).toHaveLength(2)
|
||||
})
|
||||
|
||||
@@ -74,30 +76,28 @@ describe("feeds", () => {
|
||||
wrapper
|
||||
.findAll("li[data-test='list-message']")
|
||||
.wrappers.map((e) => e.text())
|
||||
.filter((text) => text == "empty")
|
||||
.filter((text) => text === "empty")
|
||||
).toHaveLength(2)
|
||||
})
|
||||
|
||||
test("labels in the list are proper", () => {
|
||||
const wrapper = factory()
|
||||
|
||||
expect(wrapper.findAll("label[data-test='list-label']").wrappers.map((e) => e.text())).toEqual([
|
||||
"First",
|
||||
"Second",
|
||||
"no_label",
|
||||
"no_label",
|
||||
])
|
||||
expect(
|
||||
wrapper
|
||||
.findAll("label[data-test='list-label']")
|
||||
.wrappers.map((e) => e.text())
|
||||
).toEqual(["First", "Second", "no_label", "no_label"])
|
||||
})
|
||||
|
||||
test("messages in the list are proper", () => {
|
||||
const wrapper = factory()
|
||||
|
||||
expect(wrapper.findAll("li[data-test='list-message']").wrappers.map((e) => e.text())).toEqual([
|
||||
"First Message",
|
||||
"empty",
|
||||
"Third Message",
|
||||
"empty",
|
||||
])
|
||||
expect(
|
||||
wrapper
|
||||
.findAll("li[data-test='list-message']")
|
||||
.wrappers.map((e) => e.text())
|
||||
).toEqual(["First Message", "empty", "Third Message", "empty"])
|
||||
})
|
||||
|
||||
test("clicking on the delete button deletes the feed", async () => {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import inputform from "../Inputform"
|
||||
import { shallowMount } from "@vue/test-utils"
|
||||
import inputform from "../Inputform"
|
||||
|
||||
import { fb } from "~/helpers/fb"
|
||||
|
||||
jest.mock("~/helpers/fb", () => ({
|
||||
__esModule: true,
|
||||
@@ -9,8 +11,6 @@ jest.mock("~/helpers/fb", () => ({
|
||||
},
|
||||
}))
|
||||
|
||||
import { fb } from "~/helpers/fb"
|
||||
|
||||
const factory = () =>
|
||||
shallowMount(inputform, {
|
||||
mocks: {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import logout from "../Logout"
|
||||
import { shallowMount, createLocalVue } from "@vue/test-utils"
|
||||
import logout from "../Logout"
|
||||
|
||||
import { fb } from "~/helpers/fb"
|
||||
|
||||
jest.mock("~/helpers/fb", () => ({
|
||||
__esModule: true,
|
||||
@@ -9,8 +11,6 @@ jest.mock("~/helpers/fb", () => ({
|
||||
},
|
||||
}))
|
||||
|
||||
import { fb } from "~/helpers/fb"
|
||||
|
||||
const $toast = {
|
||||
info: jest.fn(),
|
||||
show: jest.fn(),
|
||||
@@ -53,7 +53,9 @@ describe("logout", () => {
|
||||
})
|
||||
|
||||
test("failed signout request fires a error toast", async () => {
|
||||
fb.signOutUser.mockImplementationOnce(() => Promise.reject("test reject"))
|
||||
fb.signOutUser.mockImplementationOnce(() =>
|
||||
Promise.reject(new Error("test reject"))
|
||||
)
|
||||
|
||||
const wrapper = factory()
|
||||
const button = wrapper.find("button")
|
||||
|
||||
@@ -6,19 +6,36 @@
|
||||
(
|
||||
<span v-for="(field, index) in fieldArgs" :key="index">
|
||||
{{ field.name }}:
|
||||
<GraphqlTypeLink :gqlType="field.type" :jumpTypeCallback="jumpTypeCallback" />
|
||||
<GraphqlTypeLink
|
||||
:gql-type="field.type"
|
||||
:jump-type-callback="jumpTypeCallback"
|
||||
/>
|
||||
<span v-if="index !== fieldArgs.length - 1"> , </span>
|
||||
</span>
|
||||
) </span
|
||||
>:
|
||||
<GraphqlTypeLink :gqlType="gqlField.type" :jumpTypeCallback="jumpTypeCallback" />
|
||||
<GraphqlTypeLink
|
||||
:gql-type="gqlField.type"
|
||||
:jump-type-callback="jumpTypeCallback"
|
||||
/>
|
||||
</div>
|
||||
<div class="mt-2 text-fgLightColor field-desc" v-if="gqlField.description">
|
||||
<div v-if="gqlField.description" class="mt-2 text-fgLightColor field-desc">
|
||||
{{ gqlField.description }}
|
||||
</div>
|
||||
<div
|
||||
class="inline-block px-4 py-2 my-2 text-sm font-bold text-black bg-yellow-200 rounded-lg field-deprecated"
|
||||
v-if="gqlField.isDeprecated"
|
||||
class="
|
||||
inline-block
|
||||
px-4
|
||||
py-2
|
||||
my-2
|
||||
text-sm
|
||||
font-bold
|
||||
text-black
|
||||
bg-yellow-200
|
||||
rounded-lg
|
||||
field-deprecated
|
||||
"
|
||||
>
|
||||
{{ $t("deprecated") }}
|
||||
</div>
|
||||
@@ -27,8 +44,14 @@
|
||||
<div class="px-4 border-l-2 border-acColor">
|
||||
<div v-for="(field, index) in fieldArgs" :key="index">
|
||||
{{ field.name }}:
|
||||
<GraphqlTypeLink :gqlType="field.type" :jumpTypeCallback="jumpTypeCallback" />
|
||||
<div class="mt-2 text-fgLightColor field-desc" v-if="field.description">
|
||||
<GraphqlTypeLink
|
||||
:gql-type="field.type"
|
||||
:jump-type-callback="jumpTypeCallback"
|
||||
/>
|
||||
<div
|
||||
v-if="field.description"
|
||||
class="mt-2 text-fgLightColor field-desc"
|
||||
>
|
||||
{{ field.description }}
|
||||
</div>
|
||||
</div>
|
||||
@@ -37,18 +60,11 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.field-highlighted {
|
||||
@apply border-b-2;
|
||||
@apply border-acColor;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
gqlField: Object,
|
||||
jumpTypeCallback: Function,
|
||||
gqlField: { type: Object, default: () => {} },
|
||||
jumpTypeCallback: { type: Function, default: () => {} },
|
||||
isHighlighted: { type: Boolean, default: false },
|
||||
},
|
||||
computed: {
|
||||
@@ -62,3 +78,10 @@ export default {
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.field-highlighted {
|
||||
@apply border-b-2;
|
||||
@apply border-acColor;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -4,27 +4,15 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.show-if-initialized {
|
||||
&.initialized {
|
||||
@apply opacity-100;
|
||||
}
|
||||
|
||||
& > * {
|
||||
@apply transition-none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import ace from "ace-builds"
|
||||
import "ace-builds/webpack-resolver"
|
||||
import "ace-builds/src-noconflict/ext-language_tools"
|
||||
import "ace-builds/src-noconflict/mode-graphqlschema"
|
||||
import { defineGQLLanguageMode } from "~/helpers/syntax/gqlQueryLangMode"
|
||||
|
||||
import * as gql from "graphql"
|
||||
import { getAutocompleteSuggestions } from "graphql-language-service-interface"
|
||||
import { defineGQLLanguageMode } from "~/helpers/syntax/gqlQueryLangMode"
|
||||
import debounce from "~/helpers/utils/debounce"
|
||||
|
||||
export default {
|
||||
@@ -44,7 +32,7 @@ export default {
|
||||
},
|
||||
options: {
|
||||
type: Object,
|
||||
default: {},
|
||||
default: () => {},
|
||||
},
|
||||
styles: {
|
||||
type: String,
|
||||
@@ -84,7 +72,7 @@ export default {
|
||||
mounted() {
|
||||
defineGQLLanguageMode(ace)
|
||||
|
||||
let langTools = ace.require("ace/ext/language_tools")
|
||||
const langTools = ace.require("ace/ext/language_tools")
|
||||
|
||||
const editor = ace.edit(this.$refs.editor, {
|
||||
mode: `ace/mode/gql-query`,
|
||||
@@ -108,12 +96,22 @@ export default {
|
||||
})
|
||||
|
||||
const completer = {
|
||||
getCompletions: (editor, _session, { row, column }, _prefix, callback) => {
|
||||
getCompletions: (
|
||||
editor,
|
||||
_session,
|
||||
{ row, column },
|
||||
_prefix,
|
||||
callback
|
||||
) => {
|
||||
if (this.validationSchema) {
|
||||
const completions = getAutocompleteSuggestions(this.validationSchema, editor.getValue(), {
|
||||
line: row,
|
||||
character: column,
|
||||
})
|
||||
const completions = getAutocompleteSuggestions(
|
||||
this.validationSchema,
|
||||
editor.getValue(),
|
||||
{
|
||||
line: row,
|
||||
character: column,
|
||||
}
|
||||
)
|
||||
|
||||
callback(
|
||||
null,
|
||||
@@ -165,10 +163,14 @@ export default {
|
||||
this.parseContents(this.value)
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
this.editor.destroy()
|
||||
},
|
||||
|
||||
methods: {
|
||||
prettifyQuery() {
|
||||
try {
|
||||
this.value = gql.print(gql.parse(this.editor.getValue()))
|
||||
this.$emit("update-query", gql.print(gql.parse(this.editor.getValue())))
|
||||
} catch (e) {
|
||||
this.$toast.error(`${this.$t("gql_prettify_invalid_query")}`, {
|
||||
icon: "error",
|
||||
@@ -180,9 +182,12 @@ export default {
|
||||
if (this.theme) {
|
||||
return this.theme
|
||||
}
|
||||
const strip = (str) => str.replace(/#/g, "").replace(/ /g, "").replace(/"/g, "")
|
||||
const strip = (str) =>
|
||||
str.replace(/#/g, "").replace(/ /g, "").replace(/"/g, "")
|
||||
return strip(
|
||||
window.getComputedStyle(document.documentElement).getPropertyValue("--editor-theme")
|
||||
window
|
||||
.getComputedStyle(document.documentElement)
|
||||
.getPropertyValue("--editor-theme")
|
||||
)
|
||||
},
|
||||
|
||||
@@ -198,12 +203,14 @@ export default {
|
||||
|
||||
if (this.validationSchema) {
|
||||
this.editor.session.setAnnotations(
|
||||
gql.validate(this.validationSchema, doc).map(({ locations, message }) => ({
|
||||
row: locations[0].line - 1,
|
||||
column: locations[0].column - 1,
|
||||
text: message,
|
||||
type: "error",
|
||||
}))
|
||||
gql
|
||||
.validate(this.validationSchema, doc)
|
||||
.map(({ locations, message }) => ({
|
||||
row: locations[0].line - 1,
|
||||
column: locations[0].column - 1,
|
||||
text: message,
|
||||
type: "error",
|
||||
}))
|
||||
)
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -221,9 +228,17 @@ export default {
|
||||
}
|
||||
}, 2000),
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
this.editor.destroy()
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.show-if-initialized {
|
||||
&.initialized {
|
||||
@apply opacity-100;
|
||||
}
|
||||
|
||||
& > * {
|
||||
@apply transition-none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,65 +1,79 @@
|
||||
<template>
|
||||
<div :id="`type_${gqlType.name}`" class="p-2 m-2">
|
||||
<div class="font-bold type-title" :class="{ 'type-highlighted': isHighlighted }">
|
||||
<div
|
||||
class="font-bold type-title"
|
||||
:class="{ 'type-highlighted': isHighlighted }"
|
||||
>
|
||||
<span v-if="isInput" class="font-normal text-acColor">input </span>
|
||||
<span v-else-if="isInterface" class="font-normal text-acColor">interface </span>
|
||||
<span v-else-if="isInterface" class="font-normal text-acColor"
|
||||
>interface
|
||||
</span>
|
||||
<span v-else-if="isEnum" class="font-normal text-acColor">enum </span>
|
||||
{{ gqlType.name }}
|
||||
</div>
|
||||
<div class="mt-2 text-fgLightColor type-desc" v-if="gqlType.description">
|
||||
<div v-if="gqlType.description" class="mt-2 text-fgLightColor type-desc">
|
||||
{{ gqlType.description }}
|
||||
</div>
|
||||
<div v-if="interfaces.length > 0" class="mb-2">
|
||||
<h5>{{ $t("interfaces") }}</h5>
|
||||
<div v-for="gqlInterface in interfaces" :key="gqlInterface.name" class="m-2 ml-4">
|
||||
<GraphqlTypeLink :gqlType="gqlInterface" :jumpTypeCallback="jumpTypeCallback" />
|
||||
<div
|
||||
v-for="gqlInterface in interfaces"
|
||||
:key="gqlInterface.name"
|
||||
class="m-2 ml-4"
|
||||
>
|
||||
<GraphqlTypeLink
|
||||
:gql-type="gqlInterface"
|
||||
:jump-type-callback="jumpTypeCallback"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="children.length > 0" class="mb-2">
|
||||
<h5>{{ $t("children") }}</h5>
|
||||
<div v-for="child in children" :key="child.name" class="m-2 ml-4">
|
||||
<GraphqlTypeLink :gqlType="child" :jumpTypeCallback="jumpTypeCallback" />
|
||||
<GraphqlTypeLink
|
||||
:gql-type="child"
|
||||
:jump-type-callback="jumpTypeCallback"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="gqlType.getFields">
|
||||
<h5>{{ $t("fields") }}</h5>
|
||||
<div v-for="field in gqlType.getFields()" :key="field.name">
|
||||
<GraphqlField
|
||||
:gqlField="field"
|
||||
:isHighlighted="isFieldHighlighted({ field })"
|
||||
:jumpTypeCallback="jumpTypeCallback"
|
||||
:gql-field="field"
|
||||
:is-highlighted="isFieldHighlighted({ field })"
|
||||
:jump-type-callback="jumpTypeCallback"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="isEnum">
|
||||
<h5>{{ $t("values") }}</h5>
|
||||
<div :key="value.name" v-for="value in gqlType.getValues()" class="m-4" v-text="value.name" />
|
||||
<div
|
||||
v-for="value in gqlType.getValues()"
|
||||
:key="value.name"
|
||||
class="m-4"
|
||||
v-text="value.name"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.type-highlighted {
|
||||
@apply text-acColor;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import { GraphQLEnumType, GraphQLInputObjectType, GraphQLInterfaceType } from "graphql"
|
||||
import {
|
||||
GraphQLEnumType,
|
||||
GraphQLInputObjectType,
|
||||
GraphQLInterfaceType,
|
||||
} from "graphql"
|
||||
|
||||
export default {
|
||||
props: {
|
||||
// eslint-disable-next-line vue/require-default-prop, vue/require-prop-types
|
||||
gqlType: {},
|
||||
gqlTypes: Array,
|
||||
jumpTypeCallback: Function,
|
||||
gqlTypes: { type: Array, default: () => [] },
|
||||
jumpTypeCallback: { type: Function, default: () => {} },
|
||||
isHighlighted: { type: Boolean, default: false },
|
||||
highlightedFields: { type: Array, default: () => [] },
|
||||
},
|
||||
methods: {
|
||||
isFieldHighlighted({ field }) {
|
||||
return !!this.highlightedFields.find(({ name }) => name === field.name)
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
isInput() {
|
||||
return this.gqlType instanceof GraphQLInputObjectType
|
||||
@@ -75,9 +89,21 @@ export default {
|
||||
},
|
||||
children() {
|
||||
return this.gqlTypes.filter(
|
||||
(type) => type.getInterfaces && type.getInterfaces().includes(this.gqlType)
|
||||
(type) =>
|
||||
type.getInterfaces && type.getInterfaces().includes(this.gqlType)
|
||||
)
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
isFieldHighlighted({ field }) {
|
||||
return !!this.highlightedFields.find(({ name }) => name === field.name)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.type-highlighted {
|
||||
@apply text-acColor;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -13,8 +13,10 @@ import { GraphQLScalarType } from "graphql"
|
||||
|
||||
export default {
|
||||
props: {
|
||||
// eslint-disable-next-line vue/require-default-prop
|
||||
gqlType: null,
|
||||
// (typeName: string) => void
|
||||
// eslint-disable-next-line vue/require-default-prop
|
||||
jumpTypeCallback: Function,
|
||||
},
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import field from "../Field"
|
||||
import { shallowMount } from "@vue/test-utils"
|
||||
import field from "../Field"
|
||||
|
||||
const gqlField = {
|
||||
name: "testField",
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import type from "../Type"
|
||||
|
||||
import { shallowMount } from "@vue/test-utils"
|
||||
import {GraphQLEnumType, GraphQLInputObjectType, GraphQLInterfaceType, GraphQLObjectType} from "graphql"
|
||||
import {
|
||||
GraphQLEnumType,
|
||||
GraphQLInputObjectType,
|
||||
GraphQLInterfaceType,
|
||||
GraphQLObjectType,
|
||||
} from "graphql"
|
||||
import type from "../Type"
|
||||
|
||||
const gqlType = {
|
||||
name: "TestType",
|
||||
@@ -77,11 +81,11 @@ describe("type", () => {
|
||||
test("prepends 'input' to type name for Input Types", () => {
|
||||
const testType = new GraphQLInputObjectType({
|
||||
name: "TestType",
|
||||
fields: {}
|
||||
fields: {},
|
||||
})
|
||||
|
||||
const wrapper = factory({
|
||||
gqlType: testType
|
||||
gqlType: testType,
|
||||
})
|
||||
|
||||
expect(wrapper.find(".type-title").text().startsWith("input")).toEqual(true)
|
||||
@@ -90,24 +94,26 @@ describe("type", () => {
|
||||
test("prepends 'interface' to type name for Interface Types", () => {
|
||||
const testType = new GraphQLInterfaceType({
|
||||
name: "TestType",
|
||||
fields: {}
|
||||
fields: {},
|
||||
})
|
||||
|
||||
const wrapper = factory({
|
||||
gqlType: testType
|
||||
gqlType: testType,
|
||||
})
|
||||
|
||||
expect(wrapper.find(".type-title").text().startsWith("interface")).toEqual(true)
|
||||
expect(wrapper.find(".type-title").text().startsWith("interface")).toEqual(
|
||||
true
|
||||
)
|
||||
})
|
||||
|
||||
test("prepends 'enum' to type name for Enum Types", () => {
|
||||
const testType = new GraphQLEnumType({
|
||||
name: "TestType",
|
||||
values: {}
|
||||
values: {},
|
||||
})
|
||||
|
||||
const wrapper = factory({
|
||||
gqlType: testType
|
||||
gqlType: testType,
|
||||
})
|
||||
|
||||
expect(wrapper.find(".type-title").text().startsWith("enum")).toEqual(true)
|
||||
@@ -116,34 +122,36 @@ describe("type", () => {
|
||||
test("'interfaces' computed property returns all the related interfaces", () => {
|
||||
const testInterfaceA = new GraphQLInterfaceType({
|
||||
name: "TestInterfaceA",
|
||||
fields: {}
|
||||
fields: {},
|
||||
})
|
||||
const testInterfaceB = new GraphQLInterfaceType({
|
||||
name: "TestInterfaceB",
|
||||
fields: {}
|
||||
fields: {},
|
||||
})
|
||||
|
||||
const type = new GraphQLObjectType({
|
||||
name: "TestType",
|
||||
interfaces: [testInterfaceA, testInterfaceB],
|
||||
fields: {}
|
||||
fields: {},
|
||||
})
|
||||
|
||||
const wrapper = factory({
|
||||
gqlType: type
|
||||
gqlType: type,
|
||||
})
|
||||
|
||||
expect(wrapper.vm.interfaces).toEqual(expect.arrayContaining([testInterfaceA, testInterfaceB]))
|
||||
expect(wrapper.vm.interfaces).toEqual(
|
||||
expect.arrayContaining([testInterfaceA, testInterfaceB])
|
||||
)
|
||||
})
|
||||
|
||||
test("'interfaces' computed property returns an empty array if there are no interfaces", () => {
|
||||
const type = new GraphQLObjectType({
|
||||
name: "TestType",
|
||||
fields: {}
|
||||
fields: {},
|
||||
})
|
||||
|
||||
const wrapper = factory({
|
||||
gqlType: type
|
||||
gqlType: type,
|
||||
})
|
||||
|
||||
expect(wrapper.vm.interfaces).toEqual([])
|
||||
@@ -152,11 +160,11 @@ describe("type", () => {
|
||||
test("'interfaces' computed property returns an empty array if the type is an enum", () => {
|
||||
const type = new GraphQLEnumType({
|
||||
name: "TestType",
|
||||
values: {}
|
||||
values: {},
|
||||
})
|
||||
|
||||
const wrapper = factory({
|
||||
gqlType: type
|
||||
gqlType: type,
|
||||
})
|
||||
|
||||
expect(wrapper.vm.interfaces).toEqual([])
|
||||
@@ -165,24 +173,24 @@ describe("type", () => {
|
||||
test("'children' computed property returns all the types implementing an interface", () => {
|
||||
const testInterface = new GraphQLInterfaceType({
|
||||
name: "TestInterface",
|
||||
fields: {}
|
||||
fields: {},
|
||||
})
|
||||
|
||||
const typeA = new GraphQLObjectType({
|
||||
name: "TypeA",
|
||||
interfaces: [testInterface],
|
||||
fields: {}
|
||||
fields: {},
|
||||
})
|
||||
|
||||
const typeB = new GraphQLObjectType({
|
||||
name: "TypeB",
|
||||
interfaces: [testInterface],
|
||||
fields: {}
|
||||
fields: {},
|
||||
})
|
||||
|
||||
const wrapper = factory({
|
||||
gqlType: testInterface,
|
||||
gqlTypes: [testInterface, typeA, typeB]
|
||||
gqlTypes: [testInterface, typeA, typeB],
|
||||
})
|
||||
|
||||
expect(wrapper.vm.children).toEqual(expect.arrayContaining([typeA, typeB]))
|
||||
@@ -191,22 +199,22 @@ describe("type", () => {
|
||||
test("'children' computed property returns an empty array if there are no types implementing the interface", () => {
|
||||
const testInterface = new GraphQLInterfaceType({
|
||||
name: "TestInterface",
|
||||
fields: {}
|
||||
fields: {},
|
||||
})
|
||||
|
||||
const typeA = new GraphQLObjectType({
|
||||
name: "TypeA",
|
||||
fields: {}
|
||||
fields: {},
|
||||
})
|
||||
|
||||
const typeB = new GraphQLObjectType({
|
||||
name: "TypeB",
|
||||
fields: {}
|
||||
fields: {},
|
||||
})
|
||||
|
||||
const wrapper = factory({
|
||||
gqlType: testInterface,
|
||||
gqlTypes: [testInterface, typeA, typeB]
|
||||
gqlTypes: [testInterface, typeA, typeB],
|
||||
})
|
||||
|
||||
expect(wrapper.vm.children).toEqual([])
|
||||
@@ -215,20 +223,19 @@ describe("type", () => {
|
||||
test("'children' computed property returns an empty array if the type is an enum", () => {
|
||||
const testInterface = new GraphQLInterfaceType({
|
||||
name: "TestInterface",
|
||||
fields: {}
|
||||
fields: {},
|
||||
})
|
||||
|
||||
const testType = new GraphQLEnumType({
|
||||
name: "TestEnum",
|
||||
values: {}
|
||||
values: {},
|
||||
})
|
||||
|
||||
const wrapper = factory({
|
||||
gqlType: testType,
|
||||
gqlTypes: [testInterface, testType]
|
||||
gqlTypes: [testInterface, testType],
|
||||
})
|
||||
|
||||
expect(wrapper.vm.children).toEqual([])
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import typelink from "../TypeLink.vue"
|
||||
import { shallowMount } from "@vue/test-utils"
|
||||
import {GraphQLInt} from "graphql"
|
||||
import { GraphQLInt } from "graphql"
|
||||
import typelink from "../TypeLink.vue"
|
||||
|
||||
const factory = (props) =>
|
||||
shallowMount(typelink, {
|
||||
@@ -39,7 +39,7 @@ describe("typelink", () => {
|
||||
|
||||
const wrapper = factory({
|
||||
gqlType: GraphQLInt,
|
||||
jumpTypeCallback: callback
|
||||
jumpTypeCallback: callback,
|
||||
})
|
||||
|
||||
await wrapper.trigger("click")
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import GraphqlCard from "../graphql/Card"
|
||||
import { mount } from "@vue/test-utils"
|
||||
import GraphqlCard from "../graphql/Card"
|
||||
|
||||
const factory = (props) => {
|
||||
return mount(GraphqlCard, {
|
||||
@@ -35,20 +35,20 @@ describe("GraphqlCard", () => {
|
||||
const wrapper = factory({
|
||||
entry: {
|
||||
type: "graphql",
|
||||
url: url,
|
||||
query: query,
|
||||
url,
|
||||
query,
|
||||
star: false,
|
||||
},
|
||||
})
|
||||
expect(wrapper).toBeTruthy()
|
||||
})
|
||||
|
||||
test("toggle-star emitted on clicking on star button", async () => {
|
||||
test("toggle-star emitted on clicking on star button", () => {
|
||||
const wrapper = factory({
|
||||
entry: {
|
||||
type: "graphql",
|
||||
url: url,
|
||||
query: query,
|
||||
url,
|
||||
query,
|
||||
star: true,
|
||||
},
|
||||
})
|
||||
@@ -61,8 +61,8 @@ describe("GraphqlCard", () => {
|
||||
const wrapper = factory({
|
||||
entry: {
|
||||
type: "graphql",
|
||||
url: url,
|
||||
query: query,
|
||||
url,
|
||||
query,
|
||||
star: true,
|
||||
},
|
||||
})
|
||||
@@ -85,12 +85,14 @@ describe("GraphqlCard", () => {
|
||||
const wrapper = factory({
|
||||
entry: {
|
||||
type: "graphql",
|
||||
url: url,
|
||||
query: query,
|
||||
url,
|
||||
query,
|
||||
star: true,
|
||||
},
|
||||
})
|
||||
await wrapper.find("button[data-testid='restore_history_entry']").trigger("click")
|
||||
await wrapper
|
||||
.find("button[data-testid='restore_history_entry']")
|
||||
.trigger("click")
|
||||
expect(wrapper.emitted("use-entry")).toBeTruthy()
|
||||
})
|
||||
|
||||
@@ -98,12 +100,14 @@ describe("GraphqlCard", () => {
|
||||
const wrapper = factory({
|
||||
entry: {
|
||||
type: "graphql",
|
||||
url: url,
|
||||
query: query,
|
||||
url,
|
||||
query,
|
||||
star: true,
|
||||
},
|
||||
})
|
||||
await wrapper.find("button[data-testid=delete_history_entry]").trigger("click")
|
||||
await wrapper
|
||||
.find("button[data-testid=delete_history_entry]")
|
||||
.trigger("click")
|
||||
expect(wrapper.emitted("delete-entry")).toBeTruthy()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,284 +0,0 @@
|
||||
import History from "../"
|
||||
import { fb } from "~/helpers/fb"
|
||||
import { shallowMount } from "@vue/test-utils"
|
||||
|
||||
const restHistory = [
|
||||
{
|
||||
id: "0",
|
||||
type: "rest",
|
||||
},
|
||||
{
|
||||
id: "1",
|
||||
type: "rest",
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
type: "rest",
|
||||
},
|
||||
]
|
||||
|
||||
const graphqlHistory = [
|
||||
{
|
||||
id: "0",
|
||||
type: "graphql",
|
||||
},
|
||||
{
|
||||
id: "1",
|
||||
type: "graphql",
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
type: "graphql",
|
||||
},
|
||||
]
|
||||
|
||||
var localStorageMock = (function () {
|
||||
var store = {
|
||||
history: JSON.stringify(restHistory),
|
||||
graphqlHistory: JSON.stringify(graphqlHistory),
|
||||
}
|
||||
return {
|
||||
getItem: function (key) {
|
||||
return store[key]
|
||||
},
|
||||
setItem: jest.fn(),
|
||||
clear: jest.fn(),
|
||||
removeItem: jest.fn(),
|
||||
}
|
||||
})()
|
||||
Object.defineProperty(window, "localStorage", { value: localStorageMock })
|
||||
|
||||
jest.mock("~/helpers/fb", () => ({
|
||||
__esModule: true,
|
||||
|
||||
fb: {
|
||||
currentUser: null,
|
||||
currentHistory: restHistory,
|
||||
currentGraphqlHistory: graphqlHistory,
|
||||
clearHistory: jest.fn(),
|
||||
clearGraphqlHistory: jest.fn(),
|
||||
deleteHistory: jest.fn(),
|
||||
deleteGraphqlHistory: jest.fn(),
|
||||
},
|
||||
}))
|
||||
|
||||
const factory = (props) => {
|
||||
return shallowMount(History, {
|
||||
propsData: props,
|
||||
stubs: {
|
||||
"v-popover": {
|
||||
template: "<div><slot /><slot name='popover' :is-open=true /></div>",
|
||||
},
|
||||
HistoryRestCard: {
|
||||
template: "<div data-testid='rest_card' />",
|
||||
},
|
||||
HistoryGraphqlCard: {
|
||||
template: "<div data-testid='graphql_card' />",
|
||||
},
|
||||
AppSection: {
|
||||
template: "<div><slot /></div>",
|
||||
},
|
||||
},
|
||||
mocks: {
|
||||
$t: (text) => text,
|
||||
$toast: {
|
||||
error() {},
|
||||
},
|
||||
},
|
||||
directives: {
|
||||
tooltip() {
|
||||
/* stub */
|
||||
},
|
||||
closePopover() {
|
||||
/* stub */
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
fb.clearHistory.mockClear()
|
||||
fb.clearGraphqlHistory.mockClear()
|
||||
fb.deleteHistory.mockClear()
|
||||
fb.deleteGraphqlHistory.mockClear()
|
||||
window.localStorage.setItem.mockClear()
|
||||
})
|
||||
|
||||
describe("Mount History", () => {
|
||||
test("Mounts rest history without login", async () => {
|
||||
const wrapper = factory({
|
||||
page: "rest",
|
||||
})
|
||||
expect(wrapper).toBeTruthy()
|
||||
})
|
||||
test("Mounts rest history with login", async () => {
|
||||
fb.currentUser = "user"
|
||||
const wrapper = factory({
|
||||
page: "rest",
|
||||
})
|
||||
expect(wrapper).toBeTruthy()
|
||||
})
|
||||
|
||||
test("Mounts graphql history without login", async () => {
|
||||
const wrapper = factory({
|
||||
page: "rest",
|
||||
})
|
||||
expect(wrapper).toBeTruthy()
|
||||
})
|
||||
test("Mounts graphql history with login", async () => {
|
||||
fb.currentUser = "user"
|
||||
const wrapper = factory({
|
||||
page: "rest",
|
||||
})
|
||||
expect(wrapper).toBeTruthy()
|
||||
})
|
||||
})
|
||||
|
||||
describe("Clear History", () => {
|
||||
test("Clear rest history without login", async () => {
|
||||
fb.currentUser = null
|
||||
const wrapper = factory({
|
||||
page: "rest",
|
||||
})
|
||||
expect(wrapper.vm.filteredHistory).toStrictEqual(restHistory)
|
||||
await wrapper.find("button[data-testid='clear_history']").trigger("click")
|
||||
await wrapper.find("button[data-testid='confirm_clear_history']").trigger("click")
|
||||
expect(fb.clearHistory).not.toHaveBeenCalled()
|
||||
expect(window.localStorage.setItem).toHaveBeenCalledWith("history", JSON.stringify([]))
|
||||
})
|
||||
test("Clear rest history with login", async () => {
|
||||
fb.currentUser = "user"
|
||||
const wrapper = factory({
|
||||
page: "rest",
|
||||
})
|
||||
expect(wrapper.vm.filteredHistory).toStrictEqual(restHistory)
|
||||
await wrapper.find("button[data-testid='clear_history']").trigger("click")
|
||||
await wrapper.find("button[data-testid='confirm_clear_history']").trigger("click")
|
||||
expect(fb.clearHistory).toHaveBeenCalledTimes(1)
|
||||
expect(window.localStorage.setItem).toHaveBeenCalledWith("history", JSON.stringify([]))
|
||||
})
|
||||
test("Dont confirm Clear rest history", async () => {
|
||||
fb.currentUser = "user"
|
||||
const wrapper = factory({
|
||||
page: "rest",
|
||||
})
|
||||
expect(wrapper.vm.filteredHistory).toStrictEqual(restHistory)
|
||||
await wrapper.find("button[data-testid='clear_history']").trigger("click")
|
||||
await wrapper.find("button[data-testid='reject_clear_history']").trigger("click")
|
||||
expect(fb.clearHistory).not.toHaveBeenCalled()
|
||||
expect(window.localStorage.setItem).not.toHaveBeenCalledWith("history", JSON.stringify([]))
|
||||
})
|
||||
|
||||
test("Clear graphql history without login", async () => {
|
||||
fb.currentUser = null
|
||||
const wrapper = factory({
|
||||
page: "graphql",
|
||||
})
|
||||
expect(wrapper.vm.filteredHistory).toStrictEqual(graphqlHistory)
|
||||
await wrapper.find("button[data-testid='clear_history']").trigger("click")
|
||||
await wrapper.find("button[data-testid='confirm_clear_history']").trigger("click")
|
||||
expect(fb.clearGraphqlHistory).not.toHaveBeenCalled()
|
||||
expect(window.localStorage.setItem).toHaveBeenCalledWith("graphqlHistory", JSON.stringify([]))
|
||||
})
|
||||
test("Clear graphql history with login", async () => {
|
||||
fb.currentUser = "user"
|
||||
const wrapper = factory({
|
||||
page: "graphql",
|
||||
})
|
||||
expect(wrapper.vm.filteredHistory).toStrictEqual(graphqlHistory)
|
||||
await wrapper.find("button[data-testid='clear_history']").trigger("click")
|
||||
await wrapper.find("button[data-testid='confirm_clear_history']").trigger("click")
|
||||
expect(fb.clearGraphqlHistory).toHaveBeenCalledTimes(1)
|
||||
expect(window.localStorage.setItem).toHaveBeenCalledWith("graphqlHistory", JSON.stringify([]))
|
||||
})
|
||||
test("Dont confirm Clear graphql history", async () => {
|
||||
fb.currentUser = "user"
|
||||
const wrapper = factory({
|
||||
page: "graphql",
|
||||
})
|
||||
expect(wrapper.vm.filteredHistory).toStrictEqual(graphqlHistory)
|
||||
await wrapper.find("button[data-testid='clear_history']").trigger("click")
|
||||
await wrapper.find("button[data-testid='reject_clear_history']").trigger("click")
|
||||
expect(window.localStorage.setItem).not.toHaveBeenCalledWith(
|
||||
"graphqlHistory",
|
||||
JSON.stringify([])
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("Use History", () => {
|
||||
test("use rest history", async () => {
|
||||
fb.currentUser = "user"
|
||||
const wrapper = factory({
|
||||
page: "rest",
|
||||
})
|
||||
expect(wrapper.findAll("div[data-testid='rest_card']").length).toEqual(restHistory.length)
|
||||
var index = restHistory.length - 1
|
||||
wrapper.findAll("div[data-testid='rest_card']").at(index).vm.$emit("use-entry")
|
||||
expect(wrapper.emitted("useHistory")).toBeTruthy()
|
||||
expect(wrapper.emitted("useHistory")[0]).toStrictEqual([restHistory[index]])
|
||||
})
|
||||
|
||||
test("use graphql history", async () => {
|
||||
fb.currentUser = "user"
|
||||
const wrapper = factory({
|
||||
page: "graphql",
|
||||
})
|
||||
expect(wrapper.findAll("div[data-testid='graphql_card']").length).toEqual(graphqlHistory.length)
|
||||
var index = restHistory.length - 1
|
||||
wrapper.findAll("div[data-testid='graphql_card']").at(index).vm.$emit("use-entry")
|
||||
expect(wrapper.emitted("useHistory")).toBeTruthy()
|
||||
expect(wrapper.emitted("useHistory")[0]).toStrictEqual([graphqlHistory[index]])
|
||||
})
|
||||
})
|
||||
|
||||
describe("Delete History", () => {
|
||||
test("delete rest history with login", async () => {
|
||||
fb.currentUser = "user"
|
||||
const wrapper = factory({
|
||||
page: "rest",
|
||||
})
|
||||
expect(wrapper.findAll("div[data-testid='rest_card']").length).toEqual(restHistory.length)
|
||||
var index = 1
|
||||
wrapper.findAll("div[data-testid='rest_card']").at(index).vm.$emit("delete-entry")
|
||||
expect(fb.deleteHistory).toBeCalledWith(restHistory[index])
|
||||
})
|
||||
|
||||
test("delete rest history without login", async () => {
|
||||
fb.currentUser = null
|
||||
const wrapper = factory({
|
||||
page: "rest",
|
||||
})
|
||||
expect(wrapper.findAll("div[data-testid='rest_card']").length).toEqual(restHistory.length)
|
||||
var index = 1
|
||||
wrapper.findAll("div[data-testid='rest_card']").at(index).vm.$emit("delete-entry")
|
||||
expect(window.localStorage.setItem).toBeCalledWith(
|
||||
"history",
|
||||
JSON.stringify(restHistory.filter((entry) => entry.id != index))
|
||||
)
|
||||
})
|
||||
test("delete graphql history with login", async () => {
|
||||
fb.currentUser = "user"
|
||||
const wrapper = factory({
|
||||
page: "graphql",
|
||||
})
|
||||
expect(wrapper.findAll("div[data-testid='graphql_card']").length).toEqual(graphqlHistory.length)
|
||||
var index = 1
|
||||
wrapper.findAll("div[data-testid='graphql_card']").at(index).vm.$emit("delete-entry")
|
||||
expect(fb.deleteGraphqlHistory).toBeCalledWith(graphqlHistory[index])
|
||||
})
|
||||
|
||||
test("delete graphql history without login", async () => {
|
||||
fb.currentUser = null
|
||||
const wrapper = factory({
|
||||
page: "graphql",
|
||||
})
|
||||
expect(wrapper.findAll("div[data-testid='graphql_card']").length).toEqual(graphqlHistory.length)
|
||||
var index = 1
|
||||
wrapper.findAll("div[data-testid='graphql_card']").at(index).vm.$emit("delete-entry")
|
||||
expect(window.localStorage.setItem).toBeCalledWith(
|
||||
"graphqlHistory",
|
||||
JSON.stringify(graphqlHistory.filter((entry) => entry.id != index))
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -1,5 +1,5 @@
|
||||
import RestCard from "../rest/Card"
|
||||
import { mount } from "@vue/test-utils"
|
||||
import RestCard from "../rest/Card"
|
||||
|
||||
const factory = (props) => {
|
||||
return mount(RestCard, {
|
||||
@@ -26,7 +26,7 @@ const factory = (props) => {
|
||||
const url = "https://dummydata.com/get"
|
||||
const entry = {
|
||||
type: "rest",
|
||||
url: url,
|
||||
url,
|
||||
method: "GET",
|
||||
status: 200,
|
||||
star: false,
|
||||
@@ -37,7 +37,7 @@ describe("RestCard", () => {
|
||||
expect(wrapper).toBeTruthy()
|
||||
})
|
||||
|
||||
test("toggle-star emitted on clicking on star button", async () => {
|
||||
test("toggle-star emitted on clicking on star button", () => {
|
||||
const wrapper = factory({ entry })
|
||||
|
||||
wrapper.find("button[data-testid='star_button']").trigger("click")
|
||||
@@ -46,13 +46,17 @@ describe("RestCard", () => {
|
||||
|
||||
test("use-entry emit on clicking the restore button", async () => {
|
||||
const wrapper = factory({ entry })
|
||||
await wrapper.find("button[data-testid='restore_history_entry']").trigger("click")
|
||||
await wrapper
|
||||
.find("button[data-testid='restore_history_entry']")
|
||||
.trigger("click")
|
||||
expect(wrapper.emitted("use-entry")).toBeTruthy()
|
||||
})
|
||||
|
||||
test("delete-entry emit on clicking the delete button", async () => {
|
||||
const wrapper = factory({ entry })
|
||||
await wrapper.find("button[data-testid=delete_history_entry]").trigger("click")
|
||||
await wrapper
|
||||
.find("button[data-testid=delete_history_entry]")
|
||||
.trigger("click")
|
||||
expect(wrapper.emitted("delete-entry")).toBeTruthy()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -13,42 +13,46 @@
|
||||
/>
|
||||
</li>
|
||||
<button
|
||||
v-tooltip="{
|
||||
content: !entry.star ? $t('add_star') : $t('remove_star'),
|
||||
}"
|
||||
data-testid="star_button"
|
||||
class="icon"
|
||||
:class="{ stared: entry.star }"
|
||||
@click="$emit('toggle-star')"
|
||||
v-tooltip="{
|
||||
content: !entry.star ? $t('add_star') : $t('remove_star'),
|
||||
}"
|
||||
>
|
||||
<i class="material-icons">
|
||||
{{ entry.star ? "star" : "star_border" }}
|
||||
</i>
|
||||
</button>
|
||||
<button
|
||||
data-testid="query_expand"
|
||||
class="icon"
|
||||
@click="expand = !expand"
|
||||
v-tooltip="{
|
||||
content: expand ? $t('hide_more') : $t('show_more'),
|
||||
}"
|
||||
data-testid="query_expand"
|
||||
class="icon"
|
||||
@click="expand = !expand"
|
||||
>
|
||||
<i class="material-icons">
|
||||
{{ expand ? "unfold_less" : "unfold_more" }}
|
||||
</i>
|
||||
</button>
|
||||
<v-popover>
|
||||
<button data-testid="options" class="tooltip-target icon" v-tooltip="$t('options')">
|
||||
<button
|
||||
v-tooltip="$t('options')"
|
||||
data-testid="options"
|
||||
class="tooltip-target icon"
|
||||
>
|
||||
<i class="material-icons">more_vert</i>
|
||||
</button>
|
||||
<template slot="popover">
|
||||
<div>
|
||||
<button
|
||||
v-close-popover
|
||||
data-testid="restore_history_entry"
|
||||
class="icon"
|
||||
@click="$emit('use-entry')"
|
||||
:aria-label="$t('restore')"
|
||||
v-close-popover
|
||||
@click="$emit('use-entry')"
|
||||
>
|
||||
<i class="material-icons">restore</i>
|
||||
<span>{{ $t("restore") }}</span>
|
||||
@@ -56,11 +60,11 @@
|
||||
</div>
|
||||
<div>
|
||||
<button
|
||||
v-close-popover
|
||||
data-testid="delete_history_entry"
|
||||
class="icon"
|
||||
@click="$emit('delete-entry')"
|
||||
:aria-label="$t('delete')"
|
||||
v-close-popover
|
||||
@click="$emit('delete-entry')"
|
||||
>
|
||||
<i class="material-icons">delete</i>
|
||||
<span>{{ $t("delete") }}</span>
|
||||
@@ -86,11 +90,11 @@
|
||||
<div v-if="showMore" class="show-on-large-screen">
|
||||
<li>
|
||||
<input
|
||||
v-tooltip="entry.date"
|
||||
:aria-label="$t('time')"
|
||||
type="text"
|
||||
readonly
|
||||
:value="entry.time"
|
||||
v-tooltip="entry.date"
|
||||
class="pt-0 mt-0 text-sm bg-transparent text-fgLightColor"
|
||||
/>
|
||||
</li>
|
||||
@@ -119,24 +123,10 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.stared {
|
||||
color: #f8e81c !important;
|
||||
}
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: all 0.2s;
|
||||
}
|
||||
.fade-enter,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
entry: Object,
|
||||
entry: { type: Object, default: () => {} },
|
||||
showMore: Boolean,
|
||||
},
|
||||
data() {
|
||||
@@ -153,3 +143,17 @@ export default {
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.stared {
|
||||
color: #f8e81c !important;
|
||||
}
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: all 0.2s;
|
||||
}
|
||||
.fade-enter,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<template>
|
||||
<AppSection icon="history" :label="$t('history')" ref="history" no-legend>
|
||||
<AppSection ref="history" icon="history" :label="$t('history')" no-legend>
|
||||
<div class="show-on-large-screen">
|
||||
<input
|
||||
v-model="filterText"
|
||||
aria-label="Search"
|
||||
type="search"
|
||||
:placeholder="$t('search')"
|
||||
v-model="filterText"
|
||||
class="rounded-t-lg"
|
||||
/>
|
||||
</div>
|
||||
@@ -16,9 +16,9 @@
|
||||
<ul v-for="(entry, index) in filteredHistory" :key="`entry-${index}`">
|
||||
<HistoryRestCard
|
||||
v-if="page == 'rest'"
|
||||
:entry="entry"
|
||||
:id="index"
|
||||
:showMore="showMore"
|
||||
:entry="entry"
|
||||
:show-more="showMore"
|
||||
@toggle-star="toggleStar(entry)"
|
||||
@delete-entry="deleteHistory(entry)"
|
||||
@use-entry="useHistory(entry)"
|
||||
@@ -26,21 +26,24 @@
|
||||
<HistoryGraphqlCard
|
||||
v-if="page == 'graphql'"
|
||||
:entry="entry"
|
||||
:showMore="showMore"
|
||||
:show-more="showMore"
|
||||
@toggle-star="toggleStar(entry)"
|
||||
@delete-entry="deleteHistory(entry)"
|
||||
@use-entry="useHistory(entry)"
|
||||
/>
|
||||
</ul>
|
||||
</div>
|
||||
<p :class="{ hidden: filteredHistory.length != 0 || history.length === 0 }" class="info">
|
||||
<p
|
||||
:class="{ hidden: filteredHistory.length != 0 || history.length === 0 }"
|
||||
class="info"
|
||||
>
|
||||
{{ $t("nothing_found") }} "{{ filterText }}"
|
||||
</p>
|
||||
<p v-if="history.length === 0" class="info">
|
||||
<i class="material-icons">schedule</i> {{ $t("history_empty") }}
|
||||
</p>
|
||||
<div v-if="history.length !== 0" class="rounded-b-lg bg-bgDarkColor">
|
||||
<div class="row-wrapper" v-if="!isClearingHistory">
|
||||
<div v-if="!isClearingHistory" class="row-wrapper">
|
||||
<button
|
||||
data-testid="clear_history"
|
||||
class="icon"
|
||||
@@ -60,22 +63,24 @@
|
||||
</i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="row-wrapper" v-else>
|
||||
<p class="info"><i class="material-icons">help_outline</i> {{ $t("are_you_sure") }}</p>
|
||||
<div v-else class="row-wrapper">
|
||||
<p class="info">
|
||||
<i class="material-icons">help_outline</i> {{ $t("are_you_sure") }}
|
||||
</p>
|
||||
<div>
|
||||
<button
|
||||
v-tooltip="$t('yes')"
|
||||
data-testid="confirm_clear_history"
|
||||
class="icon"
|
||||
@click="clearHistory"
|
||||
v-tooltip="$t('yes')"
|
||||
>
|
||||
<i class="material-icons">done</i>
|
||||
</button>
|
||||
<button
|
||||
v-tooltip="$t('no')"
|
||||
data-testid="reject_clear_history"
|
||||
class="icon"
|
||||
@click="disableHistoryClearing"
|
||||
v-tooltip="$t('no')"
|
||||
>
|
||||
<i class="material-icons">close</i>
|
||||
</button>
|
||||
@@ -85,6 +90,89 @@
|
||||
</AppSection>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
restHistory$,
|
||||
graphqlHistory$,
|
||||
clearRESTHistory,
|
||||
clearGraphqlHistory,
|
||||
toggleGraphqlHistoryEntryStar,
|
||||
toggleRESTHistoryEntryStar,
|
||||
deleteGraphqlHistoryEntry,
|
||||
deleteRESTHistoryEntry,
|
||||
} from "~/newstore/history"
|
||||
|
||||
export default {
|
||||
props: {
|
||||
page: { type: String, default: null },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
filterText: "",
|
||||
showFilter: false,
|
||||
isClearingHistory: false,
|
||||
showMore: false,
|
||||
}
|
||||
},
|
||||
subscriptions() {
|
||||
return {
|
||||
history: this.page === "rest" ? restHistory$ : graphqlHistory$,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
filteredHistory() {
|
||||
const filteringHistory = this.history
|
||||
|
||||
return filteringHistory.filter((entry) => {
|
||||
const filterText = this.filterText.toLowerCase()
|
||||
return Object.keys(entry).some((key) => {
|
||||
let value = entry[key]
|
||||
value = typeof value !== "string" ? value.toString() : value
|
||||
return value.toLowerCase().includes(filterText)
|
||||
})
|
||||
})
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
clearHistory() {
|
||||
if (this.page === "rest") clearRESTHistory()
|
||||
else clearGraphqlHistory()
|
||||
|
||||
this.isClearingHistory = false
|
||||
|
||||
this.$toast.error(this.$t("history_deleted"), {
|
||||
icon: "delete",
|
||||
})
|
||||
},
|
||||
useHistory(entry) {
|
||||
this.$emit("useHistory", entry)
|
||||
},
|
||||
deleteHistory(entry) {
|
||||
if (this.page === "rest") deleteRESTHistoryEntry(entry)
|
||||
else deleteGraphqlHistoryEntry(entry)
|
||||
|
||||
this.$toast.error(this.$t("deleted"), {
|
||||
icon: "delete",
|
||||
})
|
||||
},
|
||||
enableHistoryClearing() {
|
||||
if (!this.history || !this.history.length) return
|
||||
this.isClearingHistory = true
|
||||
},
|
||||
disableHistoryClearing() {
|
||||
this.isClearingHistory = false
|
||||
},
|
||||
toggleCollapse() {
|
||||
this.showMore = !this.showMore
|
||||
},
|
||||
toggleStar(entry) {
|
||||
if (this.page === "rest") toggleRESTHistoryEntryStar(entry)
|
||||
else toggleGraphqlHistoryEntryStar(entry)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.virtual-list {
|
||||
max-height: calc(100vh - 270px);
|
||||
@@ -105,106 +193,3 @@ ol {
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import { fb } from "~/helpers/fb"
|
||||
|
||||
const updateOnLocalStorage = (propertyName, property) =>
|
||||
window.localStorage.setItem(propertyName, JSON.stringify(property))
|
||||
|
||||
export default {
|
||||
props: {
|
||||
page: String,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
history:
|
||||
fb.currentUser !== null
|
||||
? fb.currentHistory
|
||||
: JSON.parse(
|
||||
window.localStorage.getItem(this.page == "rest" ? "history" : "graphqlHistory")
|
||||
) || [],
|
||||
filterText: "",
|
||||
showFilter: false,
|
||||
isClearingHistory: false,
|
||||
showMore: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
filteredHistory() {
|
||||
this.history =
|
||||
fb.currentUser !== null
|
||||
? this.page == "rest"
|
||||
? fb.currentHistory
|
||||
: fb.currentGraphqlHistory
|
||||
: JSON.parse(
|
||||
window.localStorage.getItem(this.page == "rest" ? "history" : "graphqlHistory")
|
||||
) || []
|
||||
return this.history.filter((entry) => {
|
||||
const filterText = this.filterText.toLowerCase()
|
||||
return Object.keys(entry).some((key) => {
|
||||
let value = entry[key]
|
||||
value = typeof value !== "string" ? value.toString() : value
|
||||
return value.toLowerCase().includes(filterText)
|
||||
})
|
||||
})
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async clearHistory() {
|
||||
if (fb.currentUser !== null) {
|
||||
this.page == "rest" ? await fb.clearHistory() : await fb.clearGraphqlHistory()
|
||||
}
|
||||
this.history = []
|
||||
this.filterText = ""
|
||||
this.disableHistoryClearing()
|
||||
updateOnLocalStorage(this.page == "rest" ? "history" : "graphqlHistory", this.history)
|
||||
this.$toast.error(this.$t("history_deleted"), {
|
||||
icon: "delete",
|
||||
})
|
||||
},
|
||||
useHistory(entry) {
|
||||
this.$emit("useHistory", entry)
|
||||
},
|
||||
async deleteHistory(entry) {
|
||||
if (this.history.length === 0) {
|
||||
this.filterText = ""
|
||||
}
|
||||
if (fb.currentUser !== null) {
|
||||
await (this.page == "rest" ? fb.deleteHistory(entry) : fb.deleteGraphqlHistory(entry))
|
||||
this.history = fb.currentHistory
|
||||
updateOnLocalStorage(this.page == "rest" ? "history" : "graphqlHistory", this.history)
|
||||
} else {
|
||||
this.history.splice(this.history.indexOf(entry), 1)
|
||||
updateOnLocalStorage(this.page == "rest" ? "history" : "graphqlHistory", this.history)
|
||||
}
|
||||
this.$toast.error(this.$t("deleted"), {
|
||||
icon: "delete",
|
||||
})
|
||||
},
|
||||
addEntry(entry) {
|
||||
this.history.push(entry)
|
||||
updateOnLocalStorage(this.page == "rest" ? "history" : "graphqlHistory", this.history)
|
||||
},
|
||||
enableHistoryClearing() {
|
||||
if (!this.history || !this.history.length) return
|
||||
this.isClearingHistory = true
|
||||
},
|
||||
disableHistoryClearing() {
|
||||
this.isClearingHistory = false
|
||||
},
|
||||
toggleCollapse() {
|
||||
this.showMore = !this.showMore
|
||||
},
|
||||
async toggleStar(entry) {
|
||||
if (fb.currentUser !== null) {
|
||||
this.page == "rest"
|
||||
? await fb.toggleStar(entry, !entry.star)
|
||||
: await fb.toggleGraphqlHistoryStar(entry, !entry.star)
|
||||
}
|
||||
entry.star = !entry.star
|
||||
updateOnLocalStorage(this.page == "rest" ? "history" : "graphqlHistory", this.history)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -20,13 +20,13 @@
|
||||
</li>
|
||||
<span>
|
||||
<button
|
||||
v-tooltip="{
|
||||
content: !entry.star ? $t('add_star') : $t('remove_star'),
|
||||
}"
|
||||
data-testid="star_button"
|
||||
class="icon"
|
||||
:class="{ stared: entry.star }"
|
||||
@click="$emit('toggle-star')"
|
||||
v-tooltip="{
|
||||
content: !entry.star ? $t('add_star') : $t('remove_star'),
|
||||
}"
|
||||
>
|
||||
<i class="material-icons">
|
||||
{{ entry.star ? "star" : "star_border" }}
|
||||
@@ -48,17 +48,17 @@
|
||||
</button>
|
||||
</li> -->
|
||||
<v-popover>
|
||||
<button class="tooltip-target icon" v-tooltip="$t('options')">
|
||||
<button v-tooltip="$t('options')" class="tooltip-target icon">
|
||||
<i class="material-icons">more_vert</i>
|
||||
</button>
|
||||
<template slot="popover">
|
||||
<div>
|
||||
<button
|
||||
v-close-popover
|
||||
data-testid="restore_history_entry"
|
||||
class="icon"
|
||||
@click="$emit('use-entry')"
|
||||
:aria-label="$t('edit')"
|
||||
v-close-popover
|
||||
@click="$emit('use-entry')"
|
||||
>
|
||||
<i class="material-icons">restore</i>
|
||||
<span>{{ $t("restore") }}</span>
|
||||
@@ -66,11 +66,11 @@
|
||||
</div>
|
||||
<div>
|
||||
<button
|
||||
v-close-popover
|
||||
data-testid="delete_history_entry"
|
||||
class="icon"
|
||||
@click="$emit('delete-entry')"
|
||||
:aria-label="$t('delete')"
|
||||
v-close-popover
|
||||
@click="$emit('delete-entry')"
|
||||
>
|
||||
<i class="material-icons">delete</i>
|
||||
<span>{{ $t("delete") }}</span>
|
||||
@@ -95,11 +95,11 @@
|
||||
<div v-if="showMore" class="show-on-large-screen">
|
||||
<li>
|
||||
<input
|
||||
v-tooltip="entry.date"
|
||||
:aria-label="$t('time')"
|
||||
type="text"
|
||||
readonly
|
||||
:value="entry.time"
|
||||
v-tooltip="entry.date"
|
||||
class="pt-0 mt-0 text-sm bg-transparent text-fgLightColor"
|
||||
/>
|
||||
</li>
|
||||
@@ -128,26 +128,12 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.stared {
|
||||
color: #f8e81c !important;
|
||||
}
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: all 0.2s;
|
||||
}
|
||||
.fade-enter,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import findStatusGroup from "~/helpers/findStatusGroup"
|
||||
|
||||
export default {
|
||||
props: {
|
||||
entry: Object,
|
||||
entry: { type: Object, default: () => {} },
|
||||
showMore: Boolean,
|
||||
},
|
||||
data() {
|
||||
@@ -167,3 +153,17 @@ export default {
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.stared {
|
||||
color: #f8e81c !important;
|
||||
}
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: all 0.2s;
|
||||
}
|
||||
.fade-enter,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -6,9 +6,9 @@
|
||||
<label for="reqParamList">{{ $t("request_body") }}</label>
|
||||
<div>
|
||||
<button
|
||||
v-tooltip.bottom="$t('clear')"
|
||||
class="icon"
|
||||
@click="clearContent('bodyParams', $event)"
|
||||
v-tooltip.bottom="$t('clear')"
|
||||
>
|
||||
<i class="material-icons">clear_all</i>
|
||||
</button>
|
||||
@@ -19,7 +19,14 @@
|
||||
<ul
|
||||
v-for="(param, index) in bodyParams"
|
||||
:key="index"
|
||||
class="border-b border-dashed divide-y md:divide-x border-brdColor divide-dashed divide-brdColor md:divide-y-0"
|
||||
class="
|
||||
border-b border-dashed
|
||||
divide-y
|
||||
md:divide-x
|
||||
border-brdColor
|
||||
divide-dashed divide-brdColor
|
||||
md:divide-y-0
|
||||
"
|
||||
:class="{ 'border-t': index == 0 }"
|
||||
>
|
||||
<li>
|
||||
@@ -27,9 +34,9 @@
|
||||
:placeholder="`key ${index + 1}`"
|
||||
:name="`bparam ${index}`"
|
||||
:value="param.key"
|
||||
autofocus
|
||||
@change="updateBodyParams($event, index, `setKeyBodyParams`)"
|
||||
@keyup.prevent="setRouteQueryState"
|
||||
autofocus
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
@@ -59,8 +66,6 @@
|
||||
<div>
|
||||
<li>
|
||||
<button
|
||||
class="icon"
|
||||
@click="toggleActive(index,param)"
|
||||
v-tooltip.bottom="{
|
||||
content: param.hasOwnProperty('active')
|
||||
? param.active
|
||||
@@ -68,6 +73,8 @@
|
||||
: $t('turn_on')
|
||||
: $t('turn_off'),
|
||||
}"
|
||||
class="icon"
|
||||
@click="toggleActive(index, param)"
|
||||
>
|
||||
<i class="material-icons">
|
||||
{{
|
||||
@@ -84,7 +91,10 @@
|
||||
<div v-if="contentType === 'multipart/form-data'">
|
||||
<li>
|
||||
<label for="attachment" class="p-0">
|
||||
<button class="w-full icon" @click="$refs.attachment[index].click()">
|
||||
<button
|
||||
class="w-full icon"
|
||||
@click="$refs.attachment[index].click()"
|
||||
>
|
||||
<i class="material-icons">attach_file</i>
|
||||
</button>
|
||||
</label>
|
||||
@@ -92,17 +102,17 @@
|
||||
ref="attachment"
|
||||
name="attachment"
|
||||
type="file"
|
||||
@change="setRequestAttachment($event, index)"
|
||||
multiple
|
||||
@change="setRequestAttachment($event, index)"
|
||||
/>
|
||||
</li>
|
||||
</div>
|
||||
<div>
|
||||
<li>
|
||||
<button
|
||||
v-tooltip.bottom="$t('delete')"
|
||||
class="icon"
|
||||
@click="removeRequestBodyParam(index)"
|
||||
v-tooltip.bottom="$t('delete')"
|
||||
>
|
||||
<i class="material-icons">delete</i>
|
||||
</button>
|
||||
@@ -111,7 +121,7 @@
|
||||
</ul>
|
||||
<ul>
|
||||
<li>
|
||||
<button class="icon" @click="addRequestBodyParam" name="addrequest">
|
||||
<button class="icon" name="addrequest" @click="addRequestBodyParam">
|
||||
<i class="material-icons">add</i>
|
||||
<span>{{ $t("add_new") }}</span>
|
||||
</button>
|
||||
@@ -120,26 +130,16 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.file-chips-container {
|
||||
@apply flex;
|
||||
@apply flex-1;
|
||||
@apply whitespace-nowrap;
|
||||
@apply overflow-auto;
|
||||
@apply bg-bgDarkColor;
|
||||
|
||||
.file-chips-wrapper {
|
||||
@apply flex;
|
||||
@apply w-0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
bodyParams: { type: Array, default: () => [] },
|
||||
},
|
||||
computed: {
|
||||
contentType() {
|
||||
return this.$store.state.request.contentType
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
clearContent(bodyParams, $event) {
|
||||
this.$emit("clear-content", bodyParams, $event)
|
||||
@@ -148,8 +148,13 @@ export default {
|
||||
this.$emit("set-route-query-state")
|
||||
},
|
||||
removeRequestBodyParam(index) {
|
||||
const paramArr = this.$store.state.request.bodyParams
|
||||
.filter((item, itemIndex) => itemIndex !== index && (item.hasOwnProperty("active") ? item.active == true : true))
|
||||
const paramArr = this.$store.state.request.bodyParams.filter(
|
||||
(item, itemIndex) =>
|
||||
itemIndex !== index &&
|
||||
(Object.prototype.hasOwnProperty.call(item, "active")
|
||||
? item.active === true
|
||||
: true)
|
||||
)
|
||||
this.setRawParams(paramArr)
|
||||
this.$emit("remove-request-body-param", index)
|
||||
},
|
||||
@@ -174,49 +179,70 @@ export default {
|
||||
fileIndex,
|
||||
})
|
||||
},
|
||||
updateBodyParams(event, index, type){
|
||||
updateBodyParams(event, index, type) {
|
||||
this.$store.commit(type, {
|
||||
index,
|
||||
value: event.target.value,
|
||||
})
|
||||
let paramArr = this.$store.state.request.bodyParams
|
||||
.filter((item) => (item.hasOwnProperty("active") ? item.active == true : true))
|
||||
|
||||
this.setRawParams(paramArr)
|
||||
const paramArr = this.$store.state.request.bodyParams.filter((item) =>
|
||||
Object.prototype.hasOwnProperty.call(item, "active")
|
||||
? item.active === true
|
||||
: true
|
||||
)
|
||||
|
||||
this.setRawParams(paramArr)
|
||||
},
|
||||
toggleActive(index, param){
|
||||
let paramArr = this.$store.state.request.bodyParams
|
||||
.filter((item, itemIndex) => {
|
||||
if(index === itemIndex){
|
||||
return !param.active
|
||||
toggleActive(index, param) {
|
||||
const paramArr = this.$store.state.request.bodyParams.filter(
|
||||
(item, itemIndex) => {
|
||||
if (index === itemIndex) {
|
||||
return !param.active
|
||||
} else {
|
||||
return item.hasOwnProperty("active") ? item.active == true : true
|
||||
return Object.prototype.hasOwnProperty.call(item, "active")
|
||||
? item.active === true
|
||||
: true
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
)
|
||||
|
||||
this.setRawParams(paramArr)
|
||||
|
||||
this.$store.commit('setActiveBodyParams', {
|
||||
this.$store.commit("setActiveBodyParams", {
|
||||
index,
|
||||
value: param.hasOwnProperty('active') ? !param.active : false,
|
||||
value: Object.prototype.hasOwnProperty.call(param, "active")
|
||||
? !param.active
|
||||
: false,
|
||||
})
|
||||
},
|
||||
setRawParams(filteredParamArr){
|
||||
setRawParams(filteredParamArr) {
|
||||
let rawParams = {}
|
||||
filteredParamArr.forEach(_param=>{
|
||||
rawParams={
|
||||
filteredParamArr.forEach((_param) => {
|
||||
rawParams = {
|
||||
...rawParams,
|
||||
[_param.key]:_param.value
|
||||
[_param.key]: _param.value,
|
||||
}
|
||||
})
|
||||
const rawParamsStr = JSON.stringify(rawParams,null,2)
|
||||
this.$store.commit("setState", { value: rawParamsStr, attribute: "rawParams" })
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
contentType() {
|
||||
return this.$store.state.request.contentType
|
||||
const rawParamsStr = JSON.stringify(rawParams, null, 2)
|
||||
this.$store.commit("setState", {
|
||||
value: rawParamsStr,
|
||||
attribute: "rawParams",
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.file-chips-container {
|
||||
@apply flex;
|
||||
@apply flex-1;
|
||||
@apply whitespace-nowrap;
|
||||
@apply overflow-auto;
|
||||
@apply bg-bgDarkColor;
|
||||
|
||||
.file-chips-wrapper {
|
||||
@apply flex;
|
||||
@apply w-0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -11,10 +11,12 @@
|
||||
</div>
|
||||
</div>
|
||||
<div slot="body" class="flex flex-col">
|
||||
<label for="requestType">{{ $t("request_type") }}</label>
|
||||
<label for="requestType">{{ $t("choose_language") }}</label>
|
||||
<span class="select-wrapper">
|
||||
<v-popover>
|
||||
<pre v-if="requestType">{{ codegens.find((x) => x.id === requestType).name }}</pre>
|
||||
<pre v-if="requestType">{{
|
||||
codegens.find((x) => x.id === requestType).name
|
||||
}}</pre>
|
||||
<input
|
||||
v-else
|
||||
id="requestType"
|
||||
@@ -26,7 +28,11 @@
|
||||
/>
|
||||
<template slot="popover">
|
||||
<div v-for="gen in codegens" :key="gen.id">
|
||||
<button class="icon" @click="requestType = gen.id" v-close-popover>
|
||||
<button
|
||||
v-close-popover
|
||||
class="icon"
|
||||
@click="requestType = gen.id"
|
||||
>
|
||||
{{ gen.name }}
|
||||
</button>
|
||||
</div>
|
||||
@@ -37,24 +43,31 @@
|
||||
<label for="generatedCode">{{ $t("generated_code") }}</label>
|
||||
<div>
|
||||
<button
|
||||
class="icon"
|
||||
@click="copyRequestCode"
|
||||
ref="copyRequestCode"
|
||||
v-tooltip="$t('copy_code')"
|
||||
class="icon"
|
||||
@click="copyRequestCode"
|
||||
>
|
||||
<i class="material-icons">content_copy</i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<textarea
|
||||
id="generatedCode"
|
||||
<SmartAceEditor
|
||||
v-if="requestType"
|
||||
ref="generatedCode"
|
||||
name="generatedCode"
|
||||
rows="8"
|
||||
v-model="requestCode"
|
||||
readonly
|
||||
class="rounded-b-lg"
|
||||
></textarea>
|
||||
:value="requestCode"
|
||||
:lang="codegens.find((x) => x.id === requestType).language"
|
||||
:options="{
|
||||
maxLines: '10',
|
||||
minLines: '10',
|
||||
fontSize: '16px',
|
||||
autoScrollEditorIntoView: true,
|
||||
readOnly: true,
|
||||
showPrintMargin: false,
|
||||
useWorker: false,
|
||||
}"
|
||||
styles="rounded-b-lg"
|
||||
/>
|
||||
</div>
|
||||
</SmartModal>
|
||||
</template>
|
||||
@@ -65,8 +78,8 @@ import { codegens } from "~/helpers/codegen/codegen"
|
||||
export default {
|
||||
props: {
|
||||
show: Boolean,
|
||||
requestCode: String,
|
||||
requestTypeProp: { type: String, default: "" },
|
||||
requestCode: { type: String, default: null },
|
||||
requestTypeProp: { type: String, default: "curl" },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -97,9 +110,13 @@ export default {
|
||||
this.$toast.success(this.$t("copied_to_clipboard"), {
|
||||
icon: "done",
|
||||
})
|
||||
this.$refs.generatedCode.select()
|
||||
this.$refs.generatedCode.editor.selectAll()
|
||||
this.$refs.generatedCode.editor.focus()
|
||||
document.execCommand("copy")
|
||||
setTimeout(() => (this.$refs.copyRequestCode.innerHTML = this.copyButton), 1000)
|
||||
setTimeout(
|
||||
() => (this.$refs.copyRequestCode.innerHTML = this.copyButton),
|
||||
1000
|
||||
)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
<template>
|
||||
<AppSection label="Headers" ref="headers" no-legend>
|
||||
<AppSection ref="headers" label="Headers" no-legend>
|
||||
<ul v-if="headers.length !== 0">
|
||||
<li>
|
||||
<div class="row-wrapper">
|
||||
<label for="headerList">{{ $t("header_list") }}</label>
|
||||
<div>
|
||||
<button
|
||||
v-tooltip.bottom="$t('clear')"
|
||||
class="icon"
|
||||
@click="clearContent('headers', $event)"
|
||||
v-tooltip.bottom="$t('clear')"
|
||||
>
|
||||
<i class="material-icons">clear_all</i>
|
||||
</button>
|
||||
@@ -19,7 +19,14 @@
|
||||
<ul
|
||||
v-for="(header, index) in headers"
|
||||
:key="`${header.value}_${index}`"
|
||||
class="border-b border-dashed divide-y md:divide-x border-brdColor divide-dashed divide-brdColor md:divide-y-0"
|
||||
class="
|
||||
border-b border-dashed
|
||||
divide-y
|
||||
md:divide-x
|
||||
border-brdColor
|
||||
divide-dashed divide-brdColor
|
||||
md:divide-y-0
|
||||
"
|
||||
:class="{ 'border-t': index == 0 }"
|
||||
>
|
||||
<li>
|
||||
@@ -28,6 +35,7 @@
|
||||
:source="commonHeaders"
|
||||
:spellcheck="false"
|
||||
:value="header.key"
|
||||
autofocus
|
||||
@input="
|
||||
$store.commit('setKeyHeader', {
|
||||
index,
|
||||
@@ -35,7 +43,6 @@
|
||||
})
|
||||
"
|
||||
@keyup.prevent="setRouteQueryState"
|
||||
autofocus
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
@@ -55,13 +62,6 @@
|
||||
<div>
|
||||
<li>
|
||||
<button
|
||||
class="icon"
|
||||
@click="
|
||||
$store.commit('setActiveHeader', {
|
||||
index,
|
||||
value: header.hasOwnProperty('active') ? !header.active : false,
|
||||
})
|
||||
"
|
||||
v-tooltip.bottom="{
|
||||
content: header.hasOwnProperty('active')
|
||||
? header.active
|
||||
@@ -69,6 +69,13 @@
|
||||
: $t('turn_on')
|
||||
: $t('turn_off'),
|
||||
}"
|
||||
class="icon"
|
||||
@click="
|
||||
$store.commit('setActiveHeader', {
|
||||
index,
|
||||
value: header.hasOwnProperty('active') ? !header.active : false,
|
||||
})
|
||||
"
|
||||
>
|
||||
<i class="material-icons">
|
||||
{{
|
||||
@@ -84,7 +91,11 @@
|
||||
</div>
|
||||
<div>
|
||||
<li>
|
||||
<button class="icon" @click="removeRequestHeader(index)" v-tooltip.bottom="$t('delete')">
|
||||
<button
|
||||
v-tooltip.bottom="$t('delete')"
|
||||
class="icon"
|
||||
@click="removeRequestHeader(index)"
|
||||
>
|
||||
<i class="material-icons">delete</i>
|
||||
</button>
|
||||
</li>
|
||||
|
||||
@@ -11,7 +11,12 @@
|
||||
</div>
|
||||
</div>
|
||||
<div slot="body" class="flex flex-col">
|
||||
<textarea id="import-curl" autofocus rows="8" :placeholder="$t('enter_curl')"></textarea>
|
||||
<textarea
|
||||
id="import-curl"
|
||||
autofocus
|
||||
rows="8"
|
||||
:placeholder="$t('enter_curl')"
|
||||
></textarea>
|
||||
</div>
|
||||
<div slot="footer">
|
||||
<div class="row-wrapper">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<AppSection :label="$t('notes')" ref="sync" no-legend>
|
||||
<AppSection ref="sync" :label="$t('notes')" no-legend>
|
||||
<div v-if="fb.currentUser">
|
||||
<FirebaseInputform />
|
||||
<FirebaseFeeds />
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
<template>
|
||||
<AppSection label="Parameters" ref="parameters" no-legend>
|
||||
<AppSection ref="parameters" label="Parameters" no-legend>
|
||||
<ul v-if="params.length !== 0">
|
||||
<li>
|
||||
<div class="row-wrapper">
|
||||
<label for="paramList">{{ $t("parameter_list") }}</label>
|
||||
<div>
|
||||
<button
|
||||
v-tooltip.bottom="$t('clear')"
|
||||
class="icon"
|
||||
@click="clearContent('parameters', $event)"
|
||||
v-tooltip.bottom="$t('clear')"
|
||||
>
|
||||
<i class="material-icons">clear_all</i>
|
||||
</button>
|
||||
@@ -19,7 +19,14 @@
|
||||
<ul
|
||||
v-for="(param, index) in params"
|
||||
:key="index"
|
||||
class="border-b border-dashed divide-y md:divide-x border-brdColor divide-dashed divide-brdColor md:divide-y-0"
|
||||
class="
|
||||
border-b border-dashed
|
||||
divide-y
|
||||
md:divide-x
|
||||
border-brdColor
|
||||
divide-dashed divide-brdColor
|
||||
md:divide-y-0
|
||||
"
|
||||
:class="{ 'border-t': index == 0 }"
|
||||
>
|
||||
<li>
|
||||
@@ -27,13 +34,13 @@
|
||||
:placeholder="$t('parameter_count', { count: index + 1 })"
|
||||
:name="'param' + index"
|
||||
:value="param.key"
|
||||
autofocus
|
||||
@change="
|
||||
$store.commit('setKeyParams', {
|
||||
index,
|
||||
value: $event.target.value,
|
||||
})
|
||||
"
|
||||
autofocus
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
@@ -72,13 +79,6 @@
|
||||
<div>
|
||||
<li>
|
||||
<button
|
||||
class="icon"
|
||||
@click="
|
||||
$store.commit('setActiveParams', {
|
||||
index,
|
||||
value: param.hasOwnProperty('active') ? !param.active : false,
|
||||
})
|
||||
"
|
||||
v-tooltip.bottom="{
|
||||
content: param.hasOwnProperty('active')
|
||||
? param.active
|
||||
@@ -86,6 +86,13 @@
|
||||
: $t('turn_on')
|
||||
: $t('turn_off'),
|
||||
}"
|
||||
class="icon"
|
||||
@click="
|
||||
$store.commit('setActiveParams', {
|
||||
index,
|
||||
value: param.hasOwnProperty('active') ? !param.active : false,
|
||||
})
|
||||
"
|
||||
>
|
||||
<i class="material-icons">
|
||||
{{
|
||||
@@ -101,7 +108,11 @@
|
||||
</div>
|
||||
<div>
|
||||
<li>
|
||||
<button class="icon" @click="removeRequestParam(index)" v-tooltip.bottom="$t('delete')">
|
||||
<button
|
||||
v-tooltip.bottom="$t('delete')"
|
||||
class="icon"
|
||||
@click="removeRequestParam(index)"
|
||||
>
|
||||
<i class="material-icons">delete</i>
|
||||
</button>
|
||||
</li>
|
||||
|
||||
@@ -6,24 +6,33 @@
|
||||
<label for="rawBody">{{ $t("raw_request_body") }}</label>
|
||||
<div>
|
||||
<button
|
||||
class="icon"
|
||||
ref="prettifyRequest"
|
||||
@click="prettifyRequestBody"
|
||||
v-tooltip="$t('prettify_body')"
|
||||
v-if="rawInput && contentType.endsWith('json')"
|
||||
ref="prettifyRequest"
|
||||
v-tooltip="$t('prettify_body')"
|
||||
class="icon"
|
||||
@click="prettifyRequestBody"
|
||||
>
|
||||
<i class="material-icons">photo_filter</i>
|
||||
</button>
|
||||
<label for="payload" class="p-0">
|
||||
<button class="icon" @click="$refs.payload.click()" v-tooltip="$t('import_json')">
|
||||
<button
|
||||
v-tooltip="$t('import_json')"
|
||||
class="icon"
|
||||
@click="$refs.payload.click()"
|
||||
>
|
||||
<i class="material-icons">post_add</i>
|
||||
</button>
|
||||
</label>
|
||||
<input ref="payload" name="payload" type="file" @change="uploadPayload" />
|
||||
<input
|
||||
ref="payload"
|
||||
name="payload"
|
||||
type="file"
|
||||
@change="uploadPayload"
|
||||
/>
|
||||
<button
|
||||
v-tooltip.bottom="$t('clear')"
|
||||
class="icon"
|
||||
@click="clearContent('rawParams', $event)"
|
||||
v-tooltip.bottom="$t('clear')"
|
||||
>
|
||||
<i class="material-icons">clear_all</i>
|
||||
</button>
|
||||
@@ -53,8 +62,8 @@ import { getEditorLangForMimeType } from "~/helpers/editorutils"
|
||||
|
||||
export default {
|
||||
props: {
|
||||
rawParams: { type: String, default: "{}" },
|
||||
contentType: { type: String, default: "" },
|
||||
rawParams: { type: String, default: null },
|
||||
contentType: { type: String, default: null },
|
||||
rawInput: { type: Boolean, default: false },
|
||||
},
|
||||
data() {
|
||||
@@ -102,7 +111,7 @@ export default {
|
||||
try {
|
||||
const jsonObj = JSON.parse(this.rawParamsBody)
|
||||
this.rawParamsBody = JSON.stringify(jsonObj, null, 2)
|
||||
let oldIcon = this.$refs.prettifyRequest.innerHTML
|
||||
const oldIcon = this.$refs.prettifyRequest.innerHTML
|
||||
this.$refs.prettifyRequest.innerHTML = this.doneButton
|
||||
setTimeout(() => (this.$refs.prettifyRequest.innerHTML = oldIcon), 1000)
|
||||
} catch (e) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<AppSection id="response" :label="$t('response')" ref="response" no-legend>
|
||||
<AppSection id="response" ref="response" :label="$t('response')" no-legend>
|
||||
<HttpResponseMeta :response="response" :active="active" />
|
||||
<div v-if="response.body && response.body !== $t('loading')">
|
||||
<LensesResponseBodyRenderer :response="response" />
|
||||
@@ -12,7 +12,7 @@ export default {
|
||||
props: {
|
||||
response: {
|
||||
type: Object,
|
||||
default: {},
|
||||
default: () => {},
|
||||
},
|
||||
active: {
|
||||
type: Boolean,
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
<div class="flex flex-col">
|
||||
<div class="flex items-center justify-between">
|
||||
<label>{{ $t("response") }}</label>
|
||||
<label v-if="active"><i class="animate-spin material-icons">refresh</i></label>
|
||||
<label v-if="active"
|
||||
><i class="animate-spin material-icons">refresh</i></label
|
||||
>
|
||||
<label v-else :class="statusCategory ? statusCategory.className : ''">
|
||||
<i class="material-icons">fiber_manual_record</i>
|
||||
</label>
|
||||
@@ -31,7 +33,7 @@ export default {
|
||||
props: {
|
||||
response: {
|
||||
type: Object,
|
||||
default: {},
|
||||
default: () => {},
|
||||
},
|
||||
active: {
|
||||
type: Boolean,
|
||||
|
||||
@@ -15,9 +15,9 @@
|
||||
<label>{{ $t("token_list") }}</label>
|
||||
<div v-if="tokens.length != 0">
|
||||
<button
|
||||
v-tooltip.bottom="$t('clear')"
|
||||
class="icon"
|
||||
@click="clearContent('tokens', $event)"
|
||||
v-tooltip.bottom="$t('clear')"
|
||||
>
|
||||
<i class="material-icons">clear_all</i>
|
||||
</button>
|
||||
@@ -42,15 +42,19 @@
|
||||
<div class="row-wrapper">
|
||||
<li>
|
||||
<button
|
||||
v-tooltip.bottom="$t('use_token')"
|
||||
class="icon"
|
||||
@click="useOAuthToken(token.value)"
|
||||
v-tooltip.bottom="$t('use_token')"
|
||||
>
|
||||
<i class="material-icons">input</i>
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button class="icon" @click="removeOAuthToken(index)" v-tooltip.bottom="$t('delete')">
|
||||
<button
|
||||
v-tooltip.bottom="$t('delete')"
|
||||
class="icon"
|
||||
@click="removeOAuthToken(index)"
|
||||
>
|
||||
<i class="material-icons">delete</i>
|
||||
</button>
|
||||
</li>
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
<template>
|
||||
<div>
|
||||
<p v-for="(value, key) in headers" :key="key">
|
||||
<input :value="`${key} → ${value}`" :name="key" class="bg-transparent" readonly />
|
||||
<input
|
||||
:value="`${key} → ${value}`"
|
||||
:name="key"
|
||||
class="bg-transparent"
|
||||
readonly
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
@@ -9,7 +14,7 @@
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
headers: {},
|
||||
headers: { type: Object, default: () => {} },
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
<SmartTabs styles="m-4">
|
||||
<SmartTab
|
||||
v-for="(lens, index) in validLenses"
|
||||
:key="lens.lensName"
|
||||
:id="lens.lensName"
|
||||
:key="lens.lensName"
|
||||
:label="lens.lensName"
|
||||
:selected="index === 0"
|
||||
>
|
||||
@@ -28,7 +28,7 @@ export default {
|
||||
...getLensRenderers(),
|
||||
},
|
||||
props: {
|
||||
response: {},
|
||||
response: { type: Object, default: () => {} },
|
||||
},
|
||||
computed: {
|
||||
validLenses() {
|
||||
|
||||
@@ -4,13 +4,15 @@
|
||||
<label for="body">{{ $t("response_body") }}</label>
|
||||
<div>
|
||||
<button
|
||||
v-if="response.body"
|
||||
ref="ToggleExpandResponse"
|
||||
v-tooltip="{
|
||||
content: !expandResponse
|
||||
? $t('expand_response')
|
||||
: $t('collapse_response'),
|
||||
}"
|
||||
class="icon"
|
||||
@click="ToggleExpandResponse"
|
||||
ref="ToggleExpandResponse"
|
||||
v-if="response.body"
|
||||
v-tooltip="{
|
||||
content: !expandResponse ? $t('expand_response') : $t('collapse_response'),
|
||||
}"
|
||||
>
|
||||
<i class="material-icons">
|
||||
{{ !expandResponse ? "unfold_more" : "unfold_less" }}
|
||||
@@ -18,31 +20,31 @@
|
||||
</button>
|
||||
<button
|
||||
v-if="response.body"
|
||||
class="icon"
|
||||
@click.prevent="togglePreview"
|
||||
v-tooltip="{
|
||||
content: previewEnabled ? $t('hide_preview') : $t('preview_html'),
|
||||
}"
|
||||
class="icon"
|
||||
@click.prevent="togglePreview"
|
||||
>
|
||||
<i class="material-icons">
|
||||
{{ !previewEnabled ? "visibility" : "visibility_off" }}
|
||||
</i>
|
||||
</button>
|
||||
<button
|
||||
v-if="response.body"
|
||||
ref="downloadResponse"
|
||||
v-tooltip="$t('download_file')"
|
||||
class="icon"
|
||||
@click="downloadResponse"
|
||||
ref="downloadResponse"
|
||||
v-if="response.body"
|
||||
v-tooltip="$t('download_file')"
|
||||
>
|
||||
<i class="material-icons">save_alt</i>
|
||||
</button>
|
||||
<button
|
||||
v-if="response.body"
|
||||
ref="copyResponse"
|
||||
v-tooltip="$t('copy_response')"
|
||||
class="icon"
|
||||
@click="copyResponse"
|
||||
ref="copyResponse"
|
||||
v-if="response.body"
|
||||
v-tooltip="$t('copy_response')"
|
||||
>
|
||||
<i class="material-icons">content_copy</i>
|
||||
</button>
|
||||
@@ -64,9 +66,9 @@
|
||||
styles="rounded-b-lg"
|
||||
/>
|
||||
<iframe
|
||||
ref="previewFrame"
|
||||
:class="{ hidden: !previewEnabled }"
|
||||
class="covers-response"
|
||||
ref="previewFrame"
|
||||
src="about:blank"
|
||||
></iframe>
|
||||
</div>
|
||||
@@ -79,7 +81,7 @@ import TextContentRendererMixin from "./mixins/TextContentRendererMixin"
|
||||
export default {
|
||||
mixins: [TextContentRendererMixin],
|
||||
props: {
|
||||
response: {},
|
||||
response: { type: Object, default: () => {} },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -94,7 +96,8 @@ export default {
|
||||
methods: {
|
||||
ToggleExpandResponse() {
|
||||
this.expandResponse = !this.expandResponse
|
||||
this.responseBodyMaxLines = this.responseBodyMaxLines == Infinity ? 16 : Infinity
|
||||
this.responseBodyMaxLines =
|
||||
this.responseBodyMaxLines === Infinity ? 16 : Infinity
|
||||
},
|
||||
downloadResponse() {
|
||||
const dataToWrite = this.responseBodyText
|
||||
@@ -128,19 +131,30 @@ export default {
|
||||
aux.select()
|
||||
document.execCommand("copy")
|
||||
document.body.removeChild(aux)
|
||||
setTimeout(() => (this.$refs.copyResponse.innerHTML = this.copyButton), 1000)
|
||||
setTimeout(
|
||||
() => (this.$refs.copyResponse.innerHTML = this.copyButton),
|
||||
1000
|
||||
)
|
||||
},
|
||||
togglePreview() {
|
||||
this.previewEnabled = !this.previewEnabled
|
||||
if (this.previewEnabled) {
|
||||
if (this.$refs.previewFrame.getAttribute("data-previewing-url") === this.url) return
|
||||
if (
|
||||
this.$refs.previewFrame.getAttribute("data-previewing-url") ===
|
||||
this.url
|
||||
)
|
||||
return
|
||||
// Use DOMParser to parse document HTML.
|
||||
const previewDocument = new DOMParser().parseFromString(this.responseBodyText, "text/html")
|
||||
const previewDocument = new DOMParser().parseFromString(
|
||||
this.responseBodyText,
|
||||
"text/html"
|
||||
)
|
||||
// Inject <base href="..."> tag to head, to fix relative CSS/HTML paths.
|
||||
previewDocument.head.innerHTML =
|
||||
`<base href="${this.url}">` + previewDocument.head.innerHTML
|
||||
// Finally, set the iframe source to the resulting HTML.
|
||||
this.$refs.previewFrame.srcdoc = previewDocument.documentElement.outerHTML
|
||||
this.$refs.previewFrame.srcdoc =
|
||||
previewDocument.documentElement.outerHTML
|
||||
this.$refs.previewFrame.setAttribute("data-previewing-url", this.url)
|
||||
}
|
||||
},
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user