zudoku 0.1.1-dev.31 → 0.1.1-dev.33

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. package/dist/app/App.js +8 -1
  2. package/dist/app/App.js.map +1 -1
  3. package/dist/config/config.d.ts +7 -0
  4. package/dist/lib/components/Header.js +1 -1
  5. package/dist/lib/components/Header.js.map +1 -1
  6. package/dist/lib/components/Layout.js +1 -2
  7. package/dist/lib/components/Layout.js.map +1 -1
  8. package/dist/lib/components/SyntaxHighlight.d.ts +1 -0
  9. package/dist/lib/components/SyntaxHighlight.js +1 -1
  10. package/dist/lib/components/SyntaxHighlight.js.map +1 -1
  11. package/dist/lib/components/context/DevPortalProvider.js +1 -1
  12. package/dist/lib/components/context/DevPortalProvider.js.map +1 -1
  13. package/dist/lib/components/navigation/SideNavigation.js +1 -1
  14. package/dist/lib/components/navigation/SideNavigation.js.map +1 -1
  15. package/dist/lib/components/navigation/SideNavigationCategory.js +1 -1
  16. package/dist/lib/components/navigation/SideNavigationCategory.js.map +1 -1
  17. package/dist/lib/components/navigation/SideNavigationItem.d.ts +2 -1
  18. package/dist/lib/components/navigation/SideNavigationItem.js +11 -4
  19. package/dist/lib/components/navigation/SideNavigationItem.js.map +1 -1
  20. package/dist/lib/components/navigation/SideNavigationWrapper.d.ts +3 -0
  21. package/dist/lib/components/navigation/SideNavigationWrapper.js +2 -3
  22. package/dist/lib/components/navigation/SideNavigationWrapper.js.map +1 -1
  23. package/dist/lib/core/DevPortalContext.d.ts +1 -0
  24. package/dist/lib/core/DevPortalContext.js +2 -2
  25. package/dist/lib/core/DevPortalContext.js.map +1 -1
  26. package/dist/lib/core/plugins.d.ts +1 -1
  27. package/dist/lib/core/plugins.js +1 -1
  28. package/dist/lib/core/plugins.js.map +1 -1
  29. package/dist/lib/plugins/api-key/CreateApiKeys.d.ts +5 -0
  30. package/dist/lib/plugins/api-key/CreateApiKeys.js +37 -0
  31. package/dist/lib/plugins/api-key/CreateApiKeys.js.map +1 -0
  32. package/dist/lib/plugins/api-key/SettingsApiKeys.d.ts +3 -2
  33. package/dist/lib/plugins/api-key/SettingsApiKeys.js +35 -4
  34. package/dist/lib/plugins/api-key/SettingsApiKeys.js.map +1 -1
  35. package/dist/lib/plugins/api-key/index.d.ts +19 -21
  36. package/dist/lib/plugins/api-key/index.js +57 -36
  37. package/dist/lib/plugins/api-key/index.js.map +1 -1
  38. package/dist/lib/plugins/index.js +0 -1
  39. package/dist/lib/plugins/index.js.map +1 -1
  40. package/dist/lib/plugins/openapi/MakeRequest.js +27 -12
  41. package/dist/lib/plugins/openapi/MakeRequest.js.map +1 -1
  42. package/dist/lib/plugins/openapi/OperationListItem.js +24 -1
  43. package/dist/lib/plugins/openapi/OperationListItem.js.map +1 -1
  44. package/dist/lib/plugins/openapi/RequestBodySidecarBox.js +1 -1
  45. package/dist/lib/plugins/openapi/RequestBodySidecarBox.js.map +1 -1
  46. package/dist/lib/plugins/openapi/ResponsesSidecarBox.js +2 -2
  47. package/dist/lib/plugins/openapi/ResponsesSidecarBox.js.map +1 -1
  48. package/dist/lib/plugins/openapi/Sidecar.js +13 -13
  49. package/dist/lib/plugins/openapi/Sidecar.js.map +1 -1
  50. package/dist/lib/plugins/openapi/graphql/gql.d.ts +2 -2
  51. package/dist/lib/plugins/openapi/graphql/gql.js +1 -1
  52. package/dist/lib/plugins/openapi/graphql/gql.js.map +1 -1
  53. package/dist/lib/plugins/openapi/graphql/graphql.d.ts +1 -0
  54. package/dist/lib/plugins/openapi/graphql/graphql.js +4 -0
  55. package/dist/lib/plugins/openapi/graphql/graphql.js.map +1 -1
  56. package/dist/lib/plugins/openapi/index.js +2 -0
  57. package/dist/lib/plugins/openapi/index.js.map +1 -1
  58. package/dist/lib/plugins/openapi/playground/Headers.js +5 -16
  59. package/dist/lib/plugins/openapi/playground/Headers.js.map +1 -1
  60. package/dist/lib/plugins/openapi/playground/InlineInput.d.ts +1 -0
  61. package/dist/lib/plugins/openapi/playground/InlineInput.js +1 -1
  62. package/dist/lib/plugins/openapi/playground/{UrlParts.d.ts → PathParams.d.ts} +2 -2
  63. package/dist/lib/plugins/openapi/playground/{UrlParts.js → PathParams.js} +4 -4
  64. package/dist/lib/plugins/openapi/playground/PathParams.js.map +1 -0
  65. package/dist/lib/plugins/openapi/playground/Playground.d.ts +8 -4
  66. package/dist/lib/plugins/openapi/playground/Playground.js +49 -46
  67. package/dist/lib/plugins/openapi/playground/Playground.js.map +1 -1
  68. package/dist/lib/plugins/openapi/playground/QueryParams.d.ts +4 -6
  69. package/dist/lib/plugins/openapi/playground/QueryParams.js +27 -22
  70. package/dist/lib/plugins/openapi/playground/QueryParams.js.map +1 -1
  71. package/dist/lib/plugins/openapi/playground/UrlDisplay.d.ts +4 -0
  72. package/dist/lib/plugins/openapi/playground/UrlDisplay.js +22 -0
  73. package/dist/lib/plugins/openapi/playground/UrlDisplay.js.map +1 -0
  74. package/dist/lib/plugins/openapi/playground/createUrl.d.ts +2 -0
  75. package/dist/lib/plugins/openapi/playground/createUrl.js +15 -0
  76. package/dist/lib/plugins/openapi/playground/createUrl.js.map +1 -0
  77. package/dist/lib/plugins/openapi/util/generateSchemaExample.d.ts +1 -1
  78. package/dist/lib/plugins/openapi/util/generateSchemaExample.js +19 -9
  79. package/dist/lib/plugins/openapi/util/generateSchemaExample.js.map +1 -1
  80. package/dist/lib/plugins/redirect/index.d.ts +1 -2
  81. package/dist/lib/util/MdxComponents.js +1 -1
  82. package/dist/lib/util/MdxComponents.js.map +1 -1
  83. package/dist/lib/util/createVariantComponent.d.ts +4 -3
  84. package/dist/lib/util/createVariantComponent.js +9 -5
  85. package/dist/lib/util/createVariantComponent.js.map +1 -1
  86. package/dist/vite/plugin-api-keys.d.ts +4 -0
  87. package/dist/vite/plugin-api-keys.js +33 -0
  88. package/dist/vite/plugin-api-keys.js.map +1 -0
  89. package/dist/vite/plugin-mdx.js +1 -1
  90. package/dist/vite/plugin-mdx.js.map +1 -1
  91. package/dist/vite/plugin-redirect.d.ts +4 -0
  92. package/dist/vite/plugin-redirect.js +32 -0
  93. package/dist/vite/plugin-redirect.js.map +1 -0
  94. package/dist/vite/plugin.js +4 -0
  95. package/dist/vite/plugin.js.map +1 -1
  96. package/lib/{urql-B7mLfVog.js → urql-DMlBWUKL.js} +301 -321
  97. package/lib/{DevPortal-Dh66z5c3.js → util-BJVAslZ-.js} +3666 -4913
  98. package/lib/zudoku.components.js +1310 -4
  99. package/lib/zudoku.openapi-worker.js +1 -1
  100. package/lib/zudoku.plugins.js +12876 -12545
  101. package/package.json +1 -1
  102. package/src/app/App.tsx +8 -1
  103. package/src/lib/components/Header.tsx +2 -2
  104. package/src/lib/components/Layout.tsx +10 -6
  105. package/src/lib/components/SyntaxHighlight.tsx +6 -3
  106. package/src/lib/components/context/DevPortalProvider.ts +1 -1
  107. package/src/lib/components/navigation/SideNavigation.tsx +4 -1
  108. package/src/lib/components/navigation/SideNavigationCategory.tsx +3 -1
  109. package/src/lib/components/navigation/SideNavigationItem.tsx +15 -5
  110. package/src/lib/components/navigation/SideNavigationWrapper.tsx +14 -11
  111. package/src/lib/core/DevPortalContext.ts +3 -2
  112. package/src/lib/core/plugins.ts +1 -1
  113. package/src/lib/plugins/api-key/CreateApiKeys.tsx +84 -0
  114. package/src/lib/plugins/api-key/SettingsApiKeys.tsx +111 -11
  115. package/src/lib/plugins/api-key/index.tsx +80 -77
  116. package/src/lib/plugins/index.ts +0 -1
  117. package/src/lib/plugins/openapi/MakeRequest.tsx +38 -29
  118. package/src/lib/plugins/openapi/OperationListItem.tsx +137 -1
  119. package/src/lib/plugins/openapi/RequestBodySidecarBox.tsx +1 -1
  120. package/src/lib/plugins/openapi/ResponsesSidecarBox.tsx +1 -2
  121. package/src/lib/plugins/openapi/Sidecar.tsx +20 -20
  122. package/src/lib/plugins/openapi/graphql/gql.ts +3 -3
  123. package/src/lib/plugins/openapi/graphql/graphql.ts +5 -0
  124. package/src/lib/plugins/openapi/index.tsx +2 -0
  125. package/src/lib/plugins/openapi/playground/Headers.tsx +14 -20
  126. package/src/lib/plugins/openapi/playground/InlineInput.tsx +1 -1
  127. package/src/lib/plugins/openapi/playground/{UrlParts.tsx → PathParams.tsx} +22 -26
  128. package/src/lib/plugins/openapi/playground/Playground.tsx +205 -155
  129. package/src/lib/plugins/openapi/playground/QueryParams.tsx +89 -57
  130. package/src/lib/plugins/openapi/playground/UrlDisplay.tsx +32 -0
  131. package/src/lib/plugins/openapi/playground/createUrl.ts +22 -0
  132. package/src/lib/plugins/openapi/util/generateSchemaExample.ts +24 -9
  133. package/src/lib/plugins/redirect/index.tsx +1 -1
  134. package/src/lib/util/MdxComponents.tsx +1 -0
  135. package/src/lib/util/createVariantComponent.tsx +11 -8
  136. package/dist/lib/plugins/openapi/playground/UrlParts.js.map +0 -1
  137. package/src/lib/plugins/openapi/queries.graphql +0 -6
@@ -1,8 +1,8 @@
1
1
  import { Control, useFieldArray, UseFormRegister } from "react-hook-form";
2
- import { TrashIcon } from "lucide-react";
2
+ import { XIcon } from "lucide-react";
3
3
  import { InlineInput } from "./InlineInput.js";
4
4
  import type { PlaygroundForm, Header } from "./Playground.js";
5
- import { useEffect } from "react";
5
+ import { Button } from "../../../ui/Button.js";
6
6
 
7
7
  export const Headers = ({
8
8
  control,
@@ -17,20 +17,6 @@ export const Headers = ({
17
17
  control,
18
18
  name: "headers",
19
19
  });
20
-
21
- const headersCount = headers.length;
22
- const headersLastName = headers.at(-1)?.name;
23
- const headersValue = headers.at(-1)?.value;
24
-
25
- useEffect(() => {
26
- if (headersCount === 0) {
27
- append({ name: "", value: "" });
28
- }
29
- if (headersLastName !== "" || headersValue !== "") {
30
- append({ name: "", value: "" });
31
- }
32
- }, [append, headersCount, headersLastName, headersValue]);
33
-
34
20
  return (
35
21
  <div className="grid grid-cols-[1fr_1fr_auto]">
36
22
  {fields.map((header, i) => (
@@ -38,29 +24,37 @@ export const Headers = ({
38
24
  key={header.id}
39
25
  className="grid-cols-subgrid col-span-full grid items-center gap-x-2 has-[:focus]:bg-muted hover:bg-muted rounded overflow-hidden group"
40
26
  >
41
- {/*<input type="checkbox" />*/}
42
27
  <InlineInput
43
28
  {...register(`headers.${i}.name`)}
44
29
  placeholder="Name"
45
30
  className="peer"
31
+ autoComplete="off"
46
32
  />
47
33
  <InlineInput
48
34
  placeholder={"Value"}
49
35
  className="peer"
50
36
  {...register(`headers.${i}.value`)}
37
+ autoComplete="off"
51
38
  />
52
39
  <button
53
40
  className="hover:bg-black/5 p-1 rounded mr-2 text-muted-foreground invisible group-hover:visible peer-focus:visible"
54
41
  onClick={() => {
55
42
  remove(i);
56
43
  }}
57
- disabled={fields.length === 1}
44
+ type="button"
58
45
  >
59
- <TrashIcon size={16} />
46
+ <XIcon size={16} />
60
47
  </button>
61
- <div className="col-span-full border-b border-border"></div>
48
+ <div className="col-span-full border-b border-border"></div>
62
49
  </div>
63
50
  ))}
51
+ <Button
52
+ className="col-span-full mt-4"
53
+ onClick={() => append({ name: "", value: "" })}
54
+ type="button"
55
+ >
56
+ Add header
57
+ </Button>
64
58
  </div>
65
59
  );
66
60
  };
@@ -2,5 +2,5 @@ import createVariantComponent from "../../../util/createVariantComponent.js";
2
2
 
3
3
  export const InlineInput = createVariantComponent(
4
4
  "input",
5
- "px-2 bg-transparent h-8 font-mono text-xs m-2",
5
+ "px-2 bg-transparent h-6 font-mono text-xs m-2",
6
6
  );
@@ -26,7 +26,7 @@ const ParameterValue = forwardRef<HTMLInputElement, ParameterValueProps>(
26
26
  },
27
27
  );
28
28
 
29
- export const UrlParts = ({
29
+ export const PathParams = ({
30
30
  control,
31
31
  register,
32
32
  }: {
@@ -35,33 +35,29 @@ export const UrlParts = ({
35
35
  }) => {
36
36
  const { fields } = useFieldArray<PlaygroundForm>({
37
37
  control,
38
- name: "urlParts",
38
+ name: "pathParams",
39
39
  });
40
40
 
41
- return (
42
- <div className="grid grid-cols-[1fr_1fr_auto]">
43
- {fields.map((part, i) => (
44
- <div
45
- key={part.id}
46
- className="grid-cols-subgrid col-span-full grid items-center gap-x-2 has-[:focus]:bg-muted hover:bg-muted rounded overflow-hidden group"
47
- >
48
- <InlineInput
49
- {...register(`urlParts.${i}.name`)}
50
- disabled
51
- placeholder="Name"
52
- className="peer"
53
- />
54
- <ParameterValue
55
- {...register(`urlParts.${i}.value`)}
56
- part={part.name}
57
- {...{ [DATA_ATTR]: part.name }}
58
- placeholder="Value"
59
- className="peer"
60
- />
41
+ return fields.map((part, i) => (
42
+ <div
43
+ key={part.id}
44
+ className="grid-cols-subgrid col-span-full grid items-center gap-x-2 has-[:focus]:bg-muted hover:bg-muted rounded overflow-hidden group"
45
+ >
46
+ <InlineInput
47
+ {...register(`pathParams.${i}.name`)}
48
+ disabled
49
+ placeholder="Name"
50
+ className="peer"
51
+ />
52
+ <ParameterValue
53
+ {...register(`pathParams.${i}.value`)}
54
+ part={part.name}
55
+ {...{ [DATA_ATTR]: part.name }}
56
+ placeholder="Value"
57
+ className="peer"
58
+ />
61
59
 
62
- <div className="col-span-full border-b border-border"></div>
63
- </div>
64
- ))}
60
+ <div className="col-span-full border-b border-border"></div>
65
61
  </div>
66
- );
62
+ ));
67
63
  };
@@ -9,15 +9,18 @@ import {
9
9
  import { Card, CardContent } from "../../../ui/Card.js";
10
10
  import { Button } from "../../../ui/Button.js";
11
11
  import { Tabs, TabsContent, TabsList, TabsTrigger } from "../../../ui/Tabs.js";
12
- import { Fragment } from "react";
12
+ import { Fragment, useMemo } from "react";
13
13
  import { SyntaxHighlight } from "../../../components/SyntaxHighlight.js";
14
14
  import { ColorizedParam } from "../ColorizedParam.js";
15
15
  import { useMutation } from "@tanstack/react-query";
16
- import { useForm } from "react-hook-form";
16
+ import { FormProvider, useForm } from "react-hook-form";
17
17
  import { Headers } from "./Headers.js";
18
18
  import { QueryParams } from "./QueryParams.js";
19
- import { UrlParts } from "./UrlParts.js";
20
- import { LoaderCircle } from "lucide-react";
19
+ import { PathParams } from "./PathParams.js";
20
+ import { CirclePlayIcon, LoaderCircle, MonitorCheckIcon } from "lucide-react";
21
+ import { createUrl } from "./createUrl.js";
22
+ import { UrlDisplay } from "./UrlDisplay.js";
23
+ import { useApiIdentities } from "../../../components/context/DevPortalProvider.js";
21
24
 
22
25
  function mimeTypeToLanguage(mimeType: string) {
23
26
  const mimeTypeMapping = {
@@ -36,6 +39,19 @@ function mimeTypeToLanguage(mimeType: string) {
36
39
  )?.[0][1];
37
40
  }
38
41
 
42
+ const statusCodeMap: Record<number, string> = {
43
+ 200: "OK",
44
+ 201: "Created",
45
+ 202: "Accepted",
46
+ 204: "No Content",
47
+ 400: "Bad Request",
48
+ 401: "Unauthorized",
49
+ 403: "Forbidden",
50
+ 404: "Not Found",
51
+ 405: "Method Not Allowed",
52
+ 500: "Internal Server Error",
53
+ };
54
+
39
55
  export type Header = {
40
56
  name: string;
41
57
  value: string;
@@ -43,8 +59,9 @@ export type Header = {
43
59
  export type QueryParam = {
44
60
  name: string;
45
61
  value: string;
62
+ active?: boolean;
46
63
  };
47
- export type UrlPart = {
64
+ export type PathParam = {
48
65
  name: string;
49
66
  value: string;
50
67
  };
@@ -52,7 +69,7 @@ export type UrlPart = {
52
69
  export type PlaygroundForm = {
53
70
  body: string;
54
71
  queryParams: QueryParam[];
55
- urlParts: UrlPart[];
72
+ pathParams: PathParam[];
56
73
  headers: Header[];
57
74
  };
58
75
 
@@ -60,63 +77,61 @@ const Playground = ({
60
77
  url,
61
78
  host,
62
79
  method,
63
- defaultHeaders,
80
+ headers = [{ name: "", value: "" }],
81
+ queryParams = [],
82
+ pathParams = [],
83
+ hasParams,
64
84
  }: {
65
85
  host: string;
66
86
  url: string;
67
87
  method: string;
68
- defaultHeaders?: Header[];
88
+ headers?: Header[];
89
+ queryParams?: QueryParam[];
90
+ pathParams?: PathParam[];
91
+ hasParams: boolean;
69
92
  }) => {
70
- const { register, control, handleSubmit, watch } = useForm<PlaygroundForm>({
71
- defaultValues: {
72
- body: "",
73
- queryParams: [{ name: "", value: "" }],
74
- headers: defaultHeaders ?? [{ name: "", value: "" }],
75
- urlParts: url
76
- .split("/")
77
- .filter((part) => part.startsWith("{") && part.endsWith("}"))
78
- .map((part) => ({ name: part.slice(1, -1), value: "" })),
79
- },
80
- });
81
-
93
+ const { register, control, handleSubmit, watch, ...form } =
94
+ useForm<PlaygroundForm>({
95
+ defaultValues: {
96
+ body: "",
97
+ queryParams,
98
+ headers,
99
+ pathParams,
100
+ },
101
+ });
82
102
  const formState = watch();
103
+ const identities = useApiIdentities();
83
104
 
84
- const x = useMutation({
105
+ const queryMutation = useMutation({
85
106
  mutationFn: async (data: PlaygroundForm) => {
86
- const fullUrl =
87
- host +
88
- url
89
- .split("/")
90
- .map((v) =>
91
- v.startsWith("{") && v.endsWith("}")
92
- ? data.urlParts.find((part) => part.name === v.slice(1, -1))
93
- ?.value ?? v
94
- : v,
95
- )
96
- .join("/");
97
-
98
- const response = await fetch(fullUrl, {
107
+ const requestUrl = createUrl(host, url, data);
108
+ const start = performance.now();
109
+ const response = await fetch(requestUrl, {
99
110
  method: method.toUpperCase(),
100
111
  headers: Object.fromEntries(
101
- data.urlParts
112
+ data.pathParams
102
113
  .filter((h) => h.name)
103
114
  .map((header) => [header.name, header.value]),
104
115
  ),
105
116
  });
106
117
 
118
+ const body = await response.text();
119
+
107
120
  return {
108
121
  status: response.status,
109
122
  headers: response.headers,
110
- body: await response.text(),
123
+ size: body.length,
124
+ body,
125
+ time: performance.now() - start,
111
126
  };
112
127
  },
113
128
  });
114
129
 
115
- const path = url.split("/").map((part) => (
130
+ const path = url.split("/").map((part, i, arr) => (
116
131
  <Fragment key={part}>
117
132
  {part.startsWith("{") && part.endsWith("}") ? (
118
133
  <ColorizedParam
119
- name={part.slice(1, -1)}
134
+ name={part}
120
135
  backgroundOpacity="0"
121
136
  onClick={() => {
122
137
  console.log("asd");
@@ -124,148 +139,183 @@ const Playground = ({
124
139
  slug={part.slice(1, -1)}
125
140
  >
126
141
  {
127
- formState.urlParts.find((p) => {
128
- console.log(
129
- p.name,
130
- part.slice(1, -1),
131
- p.name === part.slice(1, -1),
132
- p.value,
133
- );
134
- return p.name === part.slice(1, -1);
135
- })?.value
142
+ formState.pathParams.find((p) => p.name === part.slice(1, -1))
143
+ ?.value
136
144
  }
137
145
  </ColorizedParam>
138
146
  ) : (
139
147
  part
140
148
  )}
141
- {"/"}
149
+ {i < arr.length - 1 && "/"}
142
150
  <wbr />
143
151
  </Fragment>
144
152
  ));
145
153
 
146
- const lang = mimeTypeToLanguage(x.data?.headers.get("Content-Type") ?? "");
154
+ const lang = mimeTypeToLanguage(
155
+ queryMutation.data?.headers.get("Content-Type") ?? "",
156
+ );
157
+
158
+ const headerEntries = Array.from(queryMutation.data?.headers.entries() ?? []);
159
+
160
+ const urlQueryParams = formState.queryParams
161
+ .filter((p) => p.active)
162
+ .map((p) => (
163
+ <Fragment key={p.name}>
164
+ {p.name}={p.value}
165
+ <wbr />
166
+ </Fragment>
167
+ ));
147
168
 
148
169
  return (
149
170
  <Dialog>
150
- <DialogTrigger>Open</DialogTrigger>
171
+ <DialogTrigger asChild>
172
+ <CirclePlayIcon
173
+ className="cursor-pointer text-primary hover:text-primary/80"
174
+ size={16}
175
+ />
176
+ </DialogTrigger>
151
177
 
152
- <DialogContent className="max-w-screen-xl w-full h-5/6">
153
- <form
154
- onSubmit={handleSubmit((data) => {
155
- x.mutateAsync(data);
156
- })}
157
- >
158
- <DialogHeader>
159
- <DialogTitle className="mb-4">API Playground</DialogTitle>
160
- <DialogDescription>
161
- <div className="grid grid-cols-2 gap-2">
162
- <Card>
163
- <CardContent className="border-b border-border pt-4">
164
- <div className="font-bold mb-1">URL</div>
165
- <div className="flex gap-2 items-center">
166
- <div className="border rounded border-border flex justify-stretch w-full">
167
- <div className="border-r border-border p-2 bg-muted font-mono">
168
- {method.toUpperCase()}
178
+ <DialogContent className="max-w-screen-xl w-full h-5/6 overflow-auto p-0">
179
+ <FormProvider {...{ register, control, handleSubmit, watch, ...form }}>
180
+ <form
181
+ onSubmit={handleSubmit((data) => {
182
+ queryMutation.mutateAsync(data);
183
+ })}
184
+ >
185
+ <div className="grid grid-cols-2 text-sm h-full">
186
+ <div className="flex flex-col gap-4 p-8 bg-muted/50 after:bg-muted-foreground/20 relative after:absolute after:w-px after:inset-0 after:left-auto">
187
+ <div className="flex gap-2 items-stretch">
188
+ <div className="flex flex-1 items-center w-full border rounded-md border-border">
189
+ <div className="border-r border-border p-2 bg-muted rounded-l-md self-stretch font-semibold font-mono">
190
+ {method.toUpperCase()}
191
+ </div>
192
+ <div className="p-2 font-mono text-xs">
193
+ {path}
194
+ {urlQueryParams.length > 0 ? "?" : ""}
195
+ {urlQueryParams}
196
+ </div>
197
+ </div>
198
+ <Button type="submit" className="h-auto flex gap-1">
199
+ Send
200
+ </Button>
201
+ </div>
202
+ <Tabs defaultValue={hasParams ? "parameters" : "headers"}>
203
+ <TabsList>
204
+ {hasParams && (
205
+ <TabsTrigger value="parameters">Parameters</TabsTrigger>
206
+ )}
207
+ <TabsTrigger value="headers">
208
+ Headers{" "}
209
+ {formState.headers.length > 0 &&
210
+ `(${formState.headers.length})`}
211
+ </TabsTrigger>
212
+ <TabsTrigger
213
+ value="body"
214
+ disabled={["POST", "PUT", "PATCH", "DELETE"].includes(
215
+ method.toUpperCase(),
216
+ )}
217
+ >
218
+ Body
219
+ </TabsTrigger>
220
+ </TabsList>
221
+ <TabsContent value="headers">
222
+ <Headers
223
+ control={control}
224
+ register={register}
225
+ headers={formState.headers}
226
+ />
227
+ </TabsContent>
228
+ <TabsContent value="parameters">
229
+ <div className="grid grid-cols-[min-content_1fr_1fr_auto]">
230
+ {pathParams.length > 0 && (
231
+ <div className="font-semibold my-3 col-span-3">
232
+ Path Parameters
169
233
  </div>
170
- <div className="p-2 whitespace-nowrap overflow-scroll flex-1">
171
- {path}
234
+ )}
235
+ <PathParams control={control} register={register} />
236
+ {queryParams.length > 0 && (
237
+ <span className="font-semibold my-3 col-span-3">
238
+ Query Parameters
239
+ </span>
240
+ )}
241
+ <QueryParams control={control} />
242
+ </div>
243
+ </TabsContent>
244
+ <TabsContent value="body">
245
+ <textarea
246
+ {...register("body")}
247
+ className="border border-border w-full rounded p-2 bg-muted h-40"
248
+ />
249
+ </TabsContent>
250
+ </Tabs>
251
+ </div>
252
+ <div className="flex flex-col gap-4 p-8 bg-muted/70">
253
+ {queryMutation.error ? (
254
+ <div>{queryMutation.error.message}</div>
255
+ ) : queryMutation.data ? (
256
+ <div className="flex flex-col gap-2">
257
+ <div className="flex gap-2">
258
+ <div className="flex text-xs gap-6">
259
+ <div>
260
+ Status: {queryMutation.data.status}{" "}
261
+ {statusCodeMap[queryMutation.data.status] ?? ""}
172
262
  </div>
263
+ <div>Time: {queryMutation.data.time.toFixed(0)}ms</div>
264
+ <div>Size: {queryMutation.data.size} B</div>
173
265
  </div>
174
- <Button type="submit">Send</Button>
175
266
  </div>
176
- </CardContent>
177
- <Tabs defaultValue="parameters">
178
- <CardContent className="border-b border-border py-4">
267
+ {/*<UrlDisplay host={host} path={url} />*/}
268
+ <Tabs defaultValue="response">
179
269
  <TabsList>
180
- <TabsTrigger value="parameters">Parameters</TabsTrigger>
270
+ <TabsTrigger value="response">Response</TabsTrigger>
181
271
  <TabsTrigger value="headers">
182
- Headers ({formState.headers.length - 1})
183
- </TabsTrigger>
184
- <TabsTrigger
185
- value="body"
186
- disabled={["POST", "PUT", "PATCH", "DELETE"].includes(
187
- method.toUpperCase(),
188
- )}
189
- >
190
- Body
272
+ {headerEntries.length
273
+ ? `Headers (${headerEntries.length})`
274
+ : "No headers"}
191
275
  </TabsTrigger>
192
276
  </TabsList>
193
- </CardContent>
194
- <CardContent className="overflow-auto h-full">
195
- <TabsContent value="headers">
196
- <Headers
197
- control={control}
198
- register={register}
199
- headers={formState.headers}
200
- />
201
- </TabsContent>
202
- <TabsContent value="parameters">
203
- <UrlParts control={control} register={register} />
204
- Query Parameters
205
- <QueryParams
206
- control={control}
207
- queryParams={formState.queryParams}
208
- register={register}
209
- />
210
- </TabsContent>
211
- <TabsContent value="auth">
212
- <CardContent>
213
- {url
214
- .split("/")
215
- .map((part, i) =>
216
- part.startsWith("{") && part.endsWith("}")
217
- ? formState.urlParts[i]
218
- : part,
219
- )
220
- .join("/")}
221
- </CardContent>
222
- Change your password here.
277
+
278
+ <TabsContent value="response">
279
+ <Card className="shadow-none p-4">
280
+ <SyntaxHighlight
281
+ language={lang ?? "json"}
282
+ noBackground
283
+ className="overflow-x-auto "
284
+ code={
285
+ queryMutation.data?.body ?? JSON.stringify("")
286
+ }
287
+ />
288
+ </Card>
223
289
  </TabsContent>
224
- <TabsContent value="body">
225
- <textarea
226
- {...register("body")}
227
- className="border border-border w-full rounded p-2 bg-muted h-40"
228
- />
290
+ <TabsContent value="headers">
291
+ <Card className="grid grid-cols-2 w-full gap-2.5 font-mono text-xs shadow-none p-4">
292
+ <div className="font-semibold">Key</div>
293
+ <div className="font-semibold">Value</div>
294
+ {headerEntries.map(([key, value]) => (
295
+ <Fragment key={key}>
296
+ <div>{key}</div>
297
+ <div>{value}</div>
298
+ </Fragment>
299
+ ))}
300
+ </Card>
229
301
  </TabsContent>
230
- </CardContent>
231
- </Tabs>
232
- </Card>
233
- <Card>
234
- <CardContent>
235
- <div className="mt-2 font-mono gap-2 flex">
236
- {method.toUpperCase()} {x.data?.status}{" "}
237
- {url
238
- .split("/")
239
- .map((v) =>
240
- v.startsWith("{") && v.endsWith("}")
241
- ? formState.urlParts.find((part) => part.name === v)
242
- ?.value ?? v
243
- : v,
244
- )
245
- .join("/")}
246
- </div>
247
-
248
- <div className="w-full overflow-auto max-h-80 rounded-xl border border-border p-4 dark:!bg-foreground/10 dark:border-transparent">
249
- {x.isPending && (
250
- <>
251
- <LoaderCircle size={18} className="animate-spins" /> a
252
- request to see the response."
253
- </>
302
+ </Tabs>
303
+ </div>
304
+ ) : (
305
+ <div className="grid place-items-center h-full">
306
+ <span className="text-[16px] font-semibold text-muted-foreground">
307
+ {queryMutation.isPending ? (
308
+ <LoaderCircle size={18} className="animate-spin" />
309
+ ) : (
310
+ "Send a request first to see the response here"
254
311
  )}
255
- <SyntaxHighlight
256
- language={lang ?? "json"}
257
- noBackground
258
- copyable={false}
259
- className="overflow-x-auto "
260
- code={x.data?.body ?? JSON.stringify("")}
261
- />
262
- </div>
263
- </CardContent>
264
- </Card>
312
+ </span>
313
+ </div>
314
+ )}
265
315
  </div>
266
- </DialogDescription>
267
- </DialogHeader>
268
- </form>
316
+ </div>
317
+ </form>
318
+ </FormProvider>
269
319
  </DialogContent>
270
320
  </Dialog>
271
321
  );