mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-04-22 16:48:30 +02:00
Add body type to request and tab dropdown
This commit is contained in:
@@ -6,9 +6,7 @@
|
|||||||
<script value="tauri-dev" />
|
<script value="tauri-dev" />
|
||||||
</scripts>
|
</scripts>
|
||||||
<node-interpreter value="project" />
|
<node-interpreter value="project" />
|
||||||
<envs>
|
<envs />
|
||||||
<env name="DATABASE_URL" value="sqlite://$USER_HOME$/Library/Application%20Support/co.schier.yaak/db.sqlite" />
|
|
||||||
</envs>
|
|
||||||
<method v="2" />
|
<method v="2" />
|
||||||
</configuration>
|
</configuration>
|
||||||
</component>
|
</component>
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"tauri-dev": "tauri dev",
|
"tauri-dev": "YAAK_ENV=development tauri dev",
|
||||||
"tauri-build": "npm run build:icon && tauri build",
|
"tauri-build": "npm run build:icon && tauri build",
|
||||||
"build": "npm run build:frontend",
|
"build": "npm run build:frontend",
|
||||||
"dev": "vite dev",
|
"dev": "vite dev",
|
||||||
|
|||||||
1
src-tauri/migrations/20230314160400_add-body-type.sql
Normal file
1
src-tauri/migrations/20230314160400_add-body-type.sql
Normal file
@@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE http_requests ADD COLUMN body_type TEXT NULL;
|
||||||
@@ -20,78 +20,6 @@
|
|||||||
},
|
},
|
||||||
"query": "\n DELETE FROM http_responses\n WHERE request_id = ?\n "
|
"query": "\n DELETE FROM http_responses\n WHERE request_id = ?\n "
|
||||||
},
|
},
|
||||||
"3d2a542964d946ff9854d053b1adf04985d97a6de27b713188505c1f99c77707": {
|
|
||||||
"describe": {
|
|
||||||
"columns": [
|
|
||||||
{
|
|
||||||
"name": "id",
|
|
||||||
"ordinal": 0,
|
|
||||||
"type_info": "Text"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "workspace_id",
|
|
||||||
"ordinal": 1,
|
|
||||||
"type_info": "Text"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "created_at",
|
|
||||||
"ordinal": 2,
|
|
||||||
"type_info": "Datetime"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "updated_at",
|
|
||||||
"ordinal": 3,
|
|
||||||
"type_info": "Datetime"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "deleted_at",
|
|
||||||
"ordinal": 4,
|
|
||||||
"type_info": "Datetime"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "name",
|
|
||||||
"ordinal": 5,
|
|
||||||
"type_info": "Text"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "url",
|
|
||||||
"ordinal": 6,
|
|
||||||
"type_info": "Text"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "method",
|
|
||||||
"ordinal": 7,
|
|
||||||
"type_info": "Text"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "body",
|
|
||||||
"ordinal": 8,
|
|
||||||
"type_info": "Text"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "headers!: sqlx::types::Json<Vec<HttpRequestHeader>>",
|
|
||||||
"ordinal": 9,
|
|
||||||
"type_info": "Text"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"nullable": [
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
true,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
true,
|
|
||||||
false
|
|
||||||
],
|
|
||||||
"parameters": {
|
|
||||||
"Right": 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"query": "\n SELECT id, workspace_id, created_at, updated_at, deleted_at, name, url, method, body,\n headers AS \"headers!: sqlx::types::Json<Vec<HttpRequestHeader>>\"\n FROM http_requests\n WHERE workspace_id = ?\n "
|
|
||||||
},
|
|
||||||
"448a1d1f1866ab42c0f81fcf8eb2930bf21dfdd43ca4831bc1a198cf45ac3732": {
|
"448a1d1f1866ab42c0f81fcf8eb2930bf21dfdd43ca4831bc1a198cf45ac3732": {
|
||||||
"describe": {
|
"describe": {
|
||||||
"columns": [],
|
"columns": [],
|
||||||
@@ -102,7 +30,7 @@
|
|||||||
},
|
},
|
||||||
"query": "\n DELETE FROM http_requests\n WHERE id = ?\n "
|
"query": "\n DELETE FROM http_requests\n WHERE id = ?\n "
|
||||||
},
|
},
|
||||||
"7ec60cbc3c9f26e8af86a21ef6b66e564f4fa518925c92308b04f882237a244e": {
|
"795391626a74376de88fb8ff015f99fb10d6d8a5c9a0623dc279e833e8e86a07": {
|
||||||
"describe": {
|
"describe": {
|
||||||
"columns": [
|
"columns": [
|
||||||
{
|
{
|
||||||
@@ -151,9 +79,14 @@
|
|||||||
"type_info": "Text"
|
"type_info": "Text"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "headers!: sqlx::types::Json<Vec<HttpRequestHeader>>",
|
"name": "body_type",
|
||||||
"ordinal": 9,
|
"ordinal": 9,
|
||||||
"type_info": "Text"
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "headers!: sqlx::types::Json<Vec<HttpRequestHeader>>",
|
||||||
|
"ordinal": 10,
|
||||||
|
"type_info": "Text"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"nullable": [
|
"nullable": [
|
||||||
@@ -166,13 +99,14 @@
|
|||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
true,
|
true,
|
||||||
|
true,
|
||||||
false
|
false
|
||||||
],
|
],
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"Right": 1
|
"Right": 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"query": "\n SELECT id, workspace_id, created_at, updated_at, deleted_at, name, url, method, body,\n headers AS \"headers!: sqlx::types::Json<Vec<HttpRequestHeader>>\"\n FROM http_requests\n WHERE id = ?\n ORDER BY created_at DESC\n "
|
"query": "\n SELECT\n id,\n workspace_id,\n created_at,\n updated_at,\n deleted_at,\n name,\n url,\n method,\n body,\n body_type,\n headers AS \"headers!: sqlx::types::Json<Vec<HttpRequestHeader>>\"\n FROM http_requests\n WHERE id = ?\n ORDER BY created_at DESC\n "
|
||||||
},
|
},
|
||||||
"7f623d0e8f1ddad33d356e2d159b776a2bef1a238cb9200d74eb0c5e3983df85": {
|
"7f623d0e8f1ddad33d356e2d159b776a2bef1a238cb9200d74eb0c5e3983df85": {
|
||||||
"describe": {
|
"describe": {
|
||||||
@@ -312,16 +246,6 @@
|
|||||||
},
|
},
|
||||||
"query": "\n SELECT id, created_at, updated_at, deleted_at, name, description\n FROM workspaces\n "
|
"query": "\n SELECT id, created_at, updated_at, deleted_at, name, description\n FROM workspaces\n "
|
||||||
},
|
},
|
||||||
"a097740ea4ab772ec6f9d8a5144d6871e0b172130d5abe4da61e663155d2bf25": {
|
|
||||||
"describe": {
|
|
||||||
"columns": [],
|
|
||||||
"nullable": [],
|
|
||||||
"parameters": {
|
|
||||||
"Right": 7
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"query": "\n INSERT INTO http_requests (id, workspace_id, name, url, method, body, headers, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)\n ON CONFLICT (id) DO UPDATE SET\n updated_at = CURRENT_TIMESTAMP,\n name = excluded.name,\n method = excluded.method,\n headers = excluded.headers,\n body = excluded.body,\n url = excluded.url\n "
|
|
||||||
},
|
|
||||||
"a83698dcf9a815b881097133edb31a34ba25e7c6c114d463c495342a85371639": {
|
"a83698dcf9a815b881097133edb31a34ba25e7c6c114d463c495342a85371639": {
|
||||||
"describe": {
|
"describe": {
|
||||||
"columns": [],
|
"columns": [],
|
||||||
@@ -332,6 +256,84 @@
|
|||||||
},
|
},
|
||||||
"query": "\n UPDATE http_responses SET (elapsed, url, status, status_reason, body, error, headers, updated_at) =\n (?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP) WHERE id = ?;\n "
|
"query": "\n UPDATE http_responses SET (elapsed, url, status, status_reason, body, error, headers, updated_at) =\n (?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP) WHERE id = ?;\n "
|
||||||
},
|
},
|
||||||
|
"d572673b8eadbd5238c7a972b581fa8788f92a44fcc8decd0f1333e662f3374b": {
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"ordinal": 0,
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "workspace_id",
|
||||||
|
"ordinal": 1,
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "created_at",
|
||||||
|
"ordinal": 2,
|
||||||
|
"type_info": "Datetime"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "updated_at",
|
||||||
|
"ordinal": 3,
|
||||||
|
"type_info": "Datetime"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "deleted_at",
|
||||||
|
"ordinal": 4,
|
||||||
|
"type_info": "Datetime"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "name",
|
||||||
|
"ordinal": 5,
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "url",
|
||||||
|
"ordinal": 6,
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "method",
|
||||||
|
"ordinal": 7,
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "body",
|
||||||
|
"ordinal": 8,
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "body_type",
|
||||||
|
"ordinal": 9,
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "headers!: sqlx::types::Json<Vec<HttpRequestHeader>>",
|
||||||
|
"ordinal": 10,
|
||||||
|
"type_info": "Text"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"nullable": [
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
false
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Right": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"query": "\n SELECT\n id,\n workspace_id,\n created_at,\n updated_at,\n deleted_at,\n name,\n url,\n method,\n body,\n body_type,\n headers AS \"headers!: sqlx::types::Json<Vec<HttpRequestHeader>>\"\n FROM http_requests\n WHERE workspace_id = ?\n "
|
||||||
|
},
|
||||||
"e767522f92c8c49cd2e563e58737a05092daf9b1dc763bacc82a5c14d696d78e": {
|
"e767522f92c8c49cd2e563e58737a05092daf9b1dc763bacc82a5c14d696d78e": {
|
||||||
"describe": {
|
"describe": {
|
||||||
"columns": [],
|
"columns": [],
|
||||||
@@ -342,6 +344,16 @@
|
|||||||
},
|
},
|
||||||
"query": "\n INSERT INTO http_responses (id, request_id, workspace_id, elapsed, url, status, status_reason, body, headers)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);\n "
|
"query": "\n INSERT INTO http_responses (id, request_id, workspace_id, elapsed, url, status, status_reason, body, headers)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);\n "
|
||||||
},
|
},
|
||||||
|
"f0bc12d939b0f26774a3e599be7518510d298f327cca8743231242dd6105768c": {
|
||||||
|
"describe": {
|
||||||
|
"columns": [],
|
||||||
|
"nullable": [],
|
||||||
|
"parameters": {
|
||||||
|
"Right": 8
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"query": "\n INSERT INTO http_requests (\n id,\n workspace_id,\n name,\n url,\n method,\n body,\n body_type,\n headers,\n updated_at\n )\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)\n ON CONFLICT (id) DO UPDATE SET\n updated_at = CURRENT_TIMESTAMP,\n name = excluded.name,\n method = excluded.method,\n headers = excluded.headers,\n body = excluded.body,\n body_type = excluded.body_type,\n url = excluded.url\n "
|
||||||
|
},
|
||||||
"f116d8cf9aad828135bb8c3a4c8b8e6b857ae13303989e9133a33b2d1cf20e96": {
|
"f116d8cf9aad828135bb8c3a4c8b8e6b857ae13303989e9133a33b2d1cf20e96": {
|
||||||
"describe": {
|
"describe": {
|
||||||
"columns": [],
|
"columns": [],
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ windows_subsystem = "windows"
|
|||||||
extern crate objc;
|
extern crate objc;
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::env;
|
||||||
|
use std::env::current_dir;
|
||||||
use std::fs::create_dir_all;
|
use std::fs::create_dir_all;
|
||||||
|
|
||||||
use http::header::{ACCEPT, HeaderName, USER_AGENT};
|
use http::header::{ACCEPT, HeaderName, USER_AGENT};
|
||||||
@@ -202,7 +204,7 @@ async fn create_request(
|
|||||||
let pool = &*db_instance.lock().await;
|
let pool = &*db_instance.lock().await;
|
||||||
let headers = Vec::new();
|
let headers = Vec::new();
|
||||||
let created_request =
|
let created_request =
|
||||||
models::upsert_request(None, workspace_id, name, "GET", None, "", headers, pool)
|
models::upsert_request(None, workspace_id, name, "GET", None, None, "", headers, pool)
|
||||||
.await
|
.await
|
||||||
.expect("Failed to create request");
|
.expect("Failed to create request");
|
||||||
|
|
||||||
@@ -237,6 +239,7 @@ async fn update_request(
|
|||||||
request.name.as_str(),
|
request.name.as_str(),
|
||||||
request.method.as_str(),
|
request.method.as_str(),
|
||||||
body,
|
body,
|
||||||
|
request.body_type,
|
||||||
request.url.as_str(),
|
request.url.as_str(),
|
||||||
request.headers.0,
|
request.headers.0,
|
||||||
pool,
|
pool,
|
||||||
@@ -359,7 +362,11 @@ fn main() {
|
|||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
.setup(|app| {
|
.setup(|app| {
|
||||||
let dir = app.path_resolver().app_data_dir().unwrap();
|
let dir = match is_dev() {
|
||||||
|
true => current_dir().unwrap(),
|
||||||
|
false => app.path_resolver().app_data_dir().unwrap(),
|
||||||
|
};
|
||||||
|
|
||||||
create_dir_all(dir.clone()).expect("Problem creating App directory!");
|
create_dir_all(dir.clone()).expect("Problem creating App directory!");
|
||||||
let p = dir.join("db.sqlite");
|
let p = dir.join("db.sqlite");
|
||||||
let p_string = p.to_string_lossy().replace(' ', "%20");
|
let p_string = p.to_string_lossy().replace(' ', "%20");
|
||||||
@@ -431,3 +438,8 @@ fn main() {
|
|||||||
.run(tauri::generate_context!())
|
.run(tauri::generate_context!())
|
||||||
.expect("error while running tauri application");
|
.expect("error while running tauri application");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_dev() -> bool {
|
||||||
|
let env = option_env!("YAAK_ENV");
|
||||||
|
env.unwrap_or("production") == "development"
|
||||||
|
}
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ pub struct HttpRequest {
|
|||||||
pub url: String,
|
pub url: String,
|
||||||
pub method: String,
|
pub method: String,
|
||||||
pub body: Option<String>,
|
pub body: Option<String>,
|
||||||
|
pub body_type: Option<String>,
|
||||||
pub headers: Json<Vec<HttpRequestHeader>>,
|
pub headers: Json<Vec<HttpRequestHeader>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,6 +117,7 @@ pub async fn upsert_request(
|
|||||||
name: &str,
|
name: &str,
|
||||||
method: &str,
|
method: &str,
|
||||||
body: Option<&str>,
|
body: Option<&str>,
|
||||||
|
body_type: Option<String>,
|
||||||
url: &str,
|
url: &str,
|
||||||
headers: Vec<HttpRequestHeader>,
|
headers: Vec<HttpRequestHeader>,
|
||||||
pool: &Pool<Sqlite>,
|
pool: &Pool<Sqlite>,
|
||||||
@@ -131,14 +133,25 @@ pub async fn upsert_request(
|
|||||||
let headers_json = Json(headers);
|
let headers_json = Json(headers);
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
r#"
|
r#"
|
||||||
INSERT INTO http_requests (id, workspace_id, name, url, method, body, headers, updated_at)
|
INSERT INTO http_requests (
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)
|
id,
|
||||||
|
workspace_id,
|
||||||
|
name,
|
||||||
|
url,
|
||||||
|
method,
|
||||||
|
body,
|
||||||
|
body_type,
|
||||||
|
headers,
|
||||||
|
updated_at
|
||||||
|
)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)
|
||||||
ON CONFLICT (id) DO UPDATE SET
|
ON CONFLICT (id) DO UPDATE SET
|
||||||
updated_at = CURRENT_TIMESTAMP,
|
updated_at = CURRENT_TIMESTAMP,
|
||||||
name = excluded.name,
|
name = excluded.name,
|
||||||
method = excluded.method,
|
method = excluded.method,
|
||||||
headers = excluded.headers,
|
headers = excluded.headers,
|
||||||
body = excluded.body,
|
body = excluded.body,
|
||||||
|
body_type = excluded.body_type,
|
||||||
url = excluded.url
|
url = excluded.url
|
||||||
"#,
|
"#,
|
||||||
id,
|
id,
|
||||||
@@ -147,6 +160,7 @@ pub async fn upsert_request(
|
|||||||
url,
|
url,
|
||||||
method,
|
method,
|
||||||
body,
|
body,
|
||||||
|
body_type,
|
||||||
headers_json,
|
headers_json,
|
||||||
)
|
)
|
||||||
.execute(pool)
|
.execute(pool)
|
||||||
@@ -162,7 +176,17 @@ pub async fn find_requests(
|
|||||||
sqlx::query_as!(
|
sqlx::query_as!(
|
||||||
HttpRequest,
|
HttpRequest,
|
||||||
r#"
|
r#"
|
||||||
SELECT id, workspace_id, created_at, updated_at, deleted_at, name, url, method, body,
|
SELECT
|
||||||
|
id,
|
||||||
|
workspace_id,
|
||||||
|
created_at,
|
||||||
|
updated_at,
|
||||||
|
deleted_at,
|
||||||
|
name,
|
||||||
|
url,
|
||||||
|
method,
|
||||||
|
body,
|
||||||
|
body_type,
|
||||||
headers AS "headers!: sqlx::types::Json<Vec<HttpRequestHeader>>"
|
headers AS "headers!: sqlx::types::Json<Vec<HttpRequestHeader>>"
|
||||||
FROM http_requests
|
FROM http_requests
|
||||||
WHERE workspace_id = ?
|
WHERE workspace_id = ?
|
||||||
@@ -177,7 +201,17 @@ pub async fn get_request(id: &str, pool: &Pool<Sqlite>) -> Result<HttpRequest, s
|
|||||||
sqlx::query_as!(
|
sqlx::query_as!(
|
||||||
HttpRequest,
|
HttpRequest,
|
||||||
r#"
|
r#"
|
||||||
SELECT id, workspace_id, created_at, updated_at, deleted_at, name, url, method, body,
|
SELECT
|
||||||
|
id,
|
||||||
|
workspace_id,
|
||||||
|
created_at,
|
||||||
|
updated_at,
|
||||||
|
deleted_at,
|
||||||
|
name,
|
||||||
|
url,
|
||||||
|
method,
|
||||||
|
body,
|
||||||
|
body_type,
|
||||||
headers AS "headers!: sqlx::types::Json<Vec<HttpRequestHeader>>"
|
headers AS "headers!: sqlx::types::Json<Vec<HttpRequestHeader>>"
|
||||||
FROM http_requests
|
FROM http_requests
|
||||||
WHERE id = ?
|
WHERE id = ?
|
||||||
|
|||||||
@@ -34,7 +34,19 @@ export function RequestPane({ fullHeight, className }: Props) {
|
|||||||
</div>
|
</div>
|
||||||
<Tabs
|
<Tabs
|
||||||
tabs={[
|
tabs={[
|
||||||
{ value: 'body', label: 'JSON' },
|
{
|
||||||
|
value: 'body',
|
||||||
|
label: activeRequest.bodyType ?? 'NoBody',
|
||||||
|
options: {
|
||||||
|
onValueChange: (bodyType) => updateRequest.mutate({ bodyType: bodyType.value }),
|
||||||
|
value: activeRequest.bodyType ?? 'nobody',
|
||||||
|
items: [
|
||||||
|
{ label: 'No Body', value: 'nobody' },
|
||||||
|
{ label: 'JSON', value: 'json' },
|
||||||
|
{ label: 'GraphQL', value: 'graphql' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
{ value: 'params', label: 'Params' },
|
{ value: 'params', label: 'Params' },
|
||||||
{ value: 'headers', label: 'Headers' },
|
{ value: 'headers', label: 'Headers' },
|
||||||
{ value: 'auth', label: 'Auth' },
|
{ value: 'auth', label: 'Auth' },
|
||||||
@@ -48,15 +60,29 @@ export function RequestPane({ fullHeight, className }: Props) {
|
|||||||
<HeaderEditor key={activeRequest.id} request={activeRequest} />
|
<HeaderEditor key={activeRequest.id} request={activeRequest} />
|
||||||
</TabContent>
|
</TabContent>
|
||||||
<TabContent value="body">
|
<TabContent value="body">
|
||||||
<Editor
|
{activeRequest.bodyType === 'json' ? (
|
||||||
key={activeRequest.id}
|
<Editor
|
||||||
className="!bg-gray-50"
|
key={activeRequest.id}
|
||||||
heightMode={fullHeight ? 'full' : 'auto'}
|
className="!bg-gray-50"
|
||||||
useTemplating
|
heightMode={fullHeight ? 'full' : 'auto'}
|
||||||
defaultValue={activeRequest.body ?? ''}
|
useTemplating
|
||||||
contentType="application/graphql+json"
|
defaultValue={activeRequest.body ?? ''}
|
||||||
onChange={(body) => updateRequest.mutate({ body })}
|
contentType="application/json"
|
||||||
/>
|
onChange={(body) => updateRequest.mutate({ body })}
|
||||||
|
/>
|
||||||
|
) : activeRequest.bodyType === 'graphql' ? (
|
||||||
|
<Editor
|
||||||
|
key={activeRequest.id}
|
||||||
|
className="!bg-gray-50"
|
||||||
|
heightMode={fullHeight ? 'full' : 'auto'}
|
||||||
|
useTemplating
|
||||||
|
defaultValue={activeRequest.body ?? ''}
|
||||||
|
contentType="application/graphql+json"
|
||||||
|
onChange={(body) => updateRequest.mutate({ body })}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div className="h-full text-gray-400 flex items-center justify-center">No Body</div>
|
||||||
|
)}
|
||||||
</TabContent>
|
</TabContent>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { useDeleteResponse } from '../hooks/useResponseDelete';
|
|||||||
import { useResponses } from '../hooks/useResponses';
|
import { useResponses } from '../hooks/useResponses';
|
||||||
import { tryFormatJson } from '../lib/formatters';
|
import { tryFormatJson } from '../lib/formatters';
|
||||||
import type { HttpResponse } from '../lib/models';
|
import type { HttpResponse } from '../lib/models';
|
||||||
import { Dropdown } from './core/Dropdown';
|
import { Dropdown, DropdownMenuTrigger } from './core/Dropdown';
|
||||||
import { Editor } from './core/Editor';
|
import { Editor } from './core/Editor';
|
||||||
import { Icon } from './core/Icon';
|
import { Icon } from './core/Icon';
|
||||||
import { IconButton } from './core/IconButton';
|
import { IconButton } from './core/IconButton';
|
||||||
@@ -97,7 +97,9 @@ export const ResponsePane = memo(function ResponsePane({ className }: Props) {
|
|||||||
})),
|
})),
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<IconButton icon="clock" className="ml-auto" size="sm" />
|
<DropdownMenuTrigger>
|
||||||
|
<IconButton icon="clock" className="ml-auto" size="sm" />
|
||||||
|
</DropdownMenuTrigger>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
</HStack>
|
</HStack>
|
||||||
</HStack>
|
</HStack>
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ export type ButtonProps = HTMLAttributes<HTMLElement> & {
|
|||||||
justify?: 'start' | 'center';
|
justify?: 'start' | 'center';
|
||||||
type?: 'button' | 'submit';
|
type?: 'button' | 'submit';
|
||||||
forDropdown?: boolean;
|
forDropdown?: boolean;
|
||||||
|
disabled?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
|||||||
@@ -2,18 +2,20 @@ import * as D from '@radix-ui/react-dropdown-menu';
|
|||||||
import { CheckIcon } from '@radix-ui/react-icons';
|
import { CheckIcon } from '@radix-ui/react-icons';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import type { ReactNode, ForwardedRef } from 'react';
|
import type { ForwardedRef, ReactElement, ReactNode } from 'react';
|
||||||
import { forwardRef, useImperativeHandle, useLayoutEffect, useState } from 'react';
|
import { forwardRef, useImperativeHandle, useLayoutEffect, useState } from 'react';
|
||||||
|
|
||||||
interface DropdownMenuRadioProps {
|
export interface DropdownMenuRadioItem {
|
||||||
children: ReactNode;
|
label: string;
|
||||||
onValueChange: ((v: { label: string; value: string }) => void) | null;
|
value: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DropdownMenuRadioProps {
|
||||||
|
children: ReactElement<typeof DropdownMenuTrigger>;
|
||||||
|
onValueChange: ((v: DropdownMenuRadioItem) => void) | null;
|
||||||
value: string;
|
value: string;
|
||||||
label?: string;
|
label?: string;
|
||||||
items: {
|
items: DropdownMenuRadioItem[];
|
||||||
label: string;
|
|
||||||
value: string;
|
|
||||||
}[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function DropdownMenuRadio({
|
export function DropdownMenuRadio({
|
||||||
@@ -32,7 +34,7 @@ export function DropdownMenuRadio({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<D.Root>
|
<D.Root>
|
||||||
<DropdownMenuTrigger>{children}</DropdownMenuTrigger>
|
{children}
|
||||||
<DropdownMenuPortal>
|
<DropdownMenuPortal>
|
||||||
<DropdownMenuContent>
|
<DropdownMenuContent>
|
||||||
{label && <DropdownMenuLabel>{label}</DropdownMenuLabel>}
|
{label && <DropdownMenuLabel>{label}</DropdownMenuLabel>}
|
||||||
@@ -50,7 +52,7 @@ export function DropdownMenuRadio({
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface DropdownProps {
|
export interface DropdownProps {
|
||||||
children: ReactNode;
|
children: ReactElement<typeof DropdownMenuTrigger>;
|
||||||
items: (
|
items: (
|
||||||
| {
|
| {
|
||||||
label: string;
|
label: string;
|
||||||
@@ -65,7 +67,7 @@ export interface DropdownProps {
|
|||||||
export function Dropdown({ children, items }: DropdownProps) {
|
export function Dropdown({ children, items }: DropdownProps) {
|
||||||
return (
|
return (
|
||||||
<D.Root>
|
<D.Root>
|
||||||
<DropdownMenuTrigger>{children}</DropdownMenuTrigger>
|
{children}
|
||||||
<DropdownMenuPortal>
|
<DropdownMenuPortal>
|
||||||
<DropdownMenuContent>
|
<DropdownMenuContent>
|
||||||
{items.map((item, i) => {
|
{items.map((item, i) => {
|
||||||
@@ -268,7 +270,7 @@ type DropdownMenuTriggerProps = D.DropdownMenuTriggerProps & {
|
|||||||
className?: string;
|
className?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
function DropdownMenuTrigger({ children, className, ...props }: DropdownMenuTriggerProps) {
|
export function DropdownMenuTrigger({ children, className, ...props }: DropdownMenuTriggerProps) {
|
||||||
return (
|
return (
|
||||||
<D.Trigger asChild className={classnames(className)} {...props}>
|
<D.Trigger asChild className={classnames(className)} {...props}>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import * as T from '@radix-ui/react-tabs';
|
import * as T from '@radix-ui/react-tabs';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import type { ReactNode } from 'react';
|
import type { ReactElement, ReactNode } from 'react';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { Button } from '../Button';
|
import { Button } from '../Button';
|
||||||
import { ScrollArea } from '../ScrollArea';
|
import type { DropdownMenuRadioItem, DropdownMenuRadioProps } from '../Dropdown';
|
||||||
|
import { DropdownMenuRadio, DropdownMenuTrigger } from '../Dropdown';
|
||||||
|
import { Icon } from '../Icon';
|
||||||
import { HStack } from '../Stacks';
|
import { HStack } from '../Stacks';
|
||||||
|
|
||||||
import './Tabs.css';
|
import './Tabs.css';
|
||||||
@@ -11,7 +13,15 @@ import './Tabs.css';
|
|||||||
interface Props {
|
interface Props {
|
||||||
defaultValue?: string;
|
defaultValue?: string;
|
||||||
label: string;
|
label: string;
|
||||||
tabs: { value: string; label: ReactNode }[];
|
tabs: {
|
||||||
|
value: string;
|
||||||
|
label: string;
|
||||||
|
options?: {
|
||||||
|
onValueChange: DropdownMenuRadioProps['onValueChange'];
|
||||||
|
value: string;
|
||||||
|
items: DropdownMenuRadioItem[];
|
||||||
|
};
|
||||||
|
}[];
|
||||||
tabListClassName?: string;
|
tabListClassName?: string;
|
||||||
className?: string;
|
className?: string;
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
@@ -32,43 +42,72 @@ export function Tabs({ defaultValue, label, children, tabs, className, tabListCl
|
|||||||
'h-auto flex items-center overflow-x-auto mb-1 pb-1',
|
'h-auto flex items-center overflow-x-auto mb-1 pb-1',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{/*<ScrollArea className="w-full pb-2">*/}
|
|
||||||
<HStack space={1}>
|
<HStack space={1}>
|
||||||
{tabs.map((t) => (
|
{tabs.map((t) => {
|
||||||
<TabTrigger key={t.value} value={t.value} active={t.value === value}>
|
const isActive = t.value === value;
|
||||||
{t.label}
|
if (t.options && isActive) {
|
||||||
</TabTrigger>
|
return (
|
||||||
))}
|
<DropdownMenuRadio
|
||||||
|
key={t.value}
|
||||||
|
items={t.options.items}
|
||||||
|
value={t.options.value}
|
||||||
|
onValueChange={t.options.onValueChange}
|
||||||
|
>
|
||||||
|
<DropdownMenuTrigger>
|
||||||
|
<Button
|
||||||
|
color="custom"
|
||||||
|
size="sm"
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
className={classnames(
|
||||||
|
isActive
|
||||||
|
? 'bg-gray-100 text-gray-900'
|
||||||
|
: 'text-gray-600 hover:text-gray-900',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{t.options.items.find((i) => i.value === t.options?.value)?.label ?? ''}
|
||||||
|
<Icon icon="triangleDown" className="-mr-1.5" />
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
</DropdownMenuRadio>
|
||||||
|
);
|
||||||
|
} else if (t.options && !isActive) {
|
||||||
|
return (
|
||||||
|
<T.Trigger asChild key={t.value} value={t.value}>
|
||||||
|
<Button
|
||||||
|
color="custom"
|
||||||
|
size="sm"
|
||||||
|
className={classnames(
|
||||||
|
isActive ? 'bg-gray-100 text-gray-900' : 'text-gray-600 hover:text-gray-900',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{t.options.items.find((i) => i.value === t.options?.value)?.label ?? ''}
|
||||||
|
<Icon icon="triangleDown" className="-mr-1.5" />
|
||||||
|
</Button>
|
||||||
|
</T.Trigger>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<T.Trigger asChild key={t.value} value={t.value}>
|
||||||
|
<Button
|
||||||
|
color="custom"
|
||||||
|
size="sm"
|
||||||
|
className={classnames(
|
||||||
|
isActive ? 'bg-gray-100 text-gray-900' : 'text-gray-600 hover:text-gray-900',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{t.label}
|
||||||
|
</Button>
|
||||||
|
</T.Trigger>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})}
|
||||||
</HStack>
|
</HStack>
|
||||||
{/*</ScrollArea>*/}
|
|
||||||
</T.List>
|
</T.List>
|
||||||
{children}
|
{children}
|
||||||
</T.Root>
|
</T.Root>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TabTriggerProps {
|
|
||||||
value: string;
|
|
||||||
children: ReactNode;
|
|
||||||
active?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function TabTrigger({ value, children, active }: TabTriggerProps) {
|
|
||||||
return (
|
|
||||||
<T.Trigger value={value} asChild>
|
|
||||||
<Button
|
|
||||||
color="custom"
|
|
||||||
size="sm"
|
|
||||||
className={classnames(
|
|
||||||
active ? 'bg-gray-100 text-gray-900' : 'text-gray-600 hover:text-gray-900',
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</Button>
|
|
||||||
</T.Trigger>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
interface TabContentProps {
|
interface TabContentProps {
|
||||||
value: string;
|
value: string;
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ export interface HttpRequest extends BaseModel {
|
|||||||
name: string;
|
name: string;
|
||||||
url: string;
|
url: string;
|
||||||
body: string | null;
|
body: string | null;
|
||||||
|
bodyType: string | null;
|
||||||
method: string;
|
method: string;
|
||||||
headers: HttpHeader[];
|
headers: HttpHeader[];
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user