diff --git a/commit/api/get_commands.py b/commit/api/get_commands.py
index 218a4ec..b64187a 100644
--- a/commit/api/get_commands.py
+++ b/commit/api/get_commands.py
@@ -56,8 +56,8 @@ def get_site_app_commands(app: str) -> dict:
# Call get_commands if it is a callable
command_list = []
- if hasattr(app_command_module, 'get_commands') and callable(getattr(app_command_module, 'get_commands')):
- commands_from_function = app_command_module.get_commands()
+ if hasattr(app_command_module, 'commands'):
+ commands_from_function = app_command_module.commands
if commands_from_function:
for command_instance in commands_from_function:
help_text = getattr(command_instance, 'help', 'No help text available')
diff --git a/dashboard/src/components/common/Header.tsx b/dashboard/src/components/common/Header.tsx
index e166546..f60b0fc 100644
--- a/dashboard/src/components/common/Header.tsx
+++ b/dashboard/src/components/common/Header.tsx
@@ -1,8 +1,5 @@
import { Link } from 'react-router-dom'
import CommitLogo from '../../assets/commit-logo.png'
-import { Button } from "@/components/ui/button"
-import { GitHubLogoIcon } from '@radix-ui/react-icons'
-
export const Header = ({ text }: { text?: string }) => {
return (
@@ -13,16 +10,6 @@ export const Header = ({ text }: { text?: string }) => {
{text &&
{text}
}
-
-
-
-
)
}
\ No newline at end of file
diff --git a/dashboard/src/components/features/APIClient/APIClientContent.tsx b/dashboard/src/components/features/APIClient/APIClientContent.tsx
new file mode 100644
index 0000000..9024319
--- /dev/null
+++ b/dashboard/src/components/features/APIClient/APIClientContent.tsx
@@ -0,0 +1,285 @@
+import CopyButton from "@/components/common/CopyToClipboard/CopyToClipboard"
+import { Button } from "@/components/ui/button"
+import { DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog"
+import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"
+import { Input } from "@/components/ui/input"
+import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
+import { Argument } from "@/types/APIData"
+import { FrappeConfig, FrappeContext } from "frappe-react-sdk"
+import { useCallback, useContext, useEffect, useState } from "react"
+import { FormProvider, useForm } from "react-hook-form"
+import { FaCaretDown } from "react-icons/fa"
+import { IoAdd } from "react-icons/io5";
+
+
+export interface APIClientContentProps {
+ endpoint: string
+ parameters?: Argument[]
+ open: boolean
+}
+
+export const APIClientContent = ({ endpoint, open, parameters }: APIClientContentProps) => {
+
+ const [requestType, setRequestType] = useState<"GET" | "POST">('GET')
+
+ const methods = useForm()
+
+ const { setValue, reset, register, handleSubmit, watch } = useForm()
+
+ const [paramsType, setParamsType] = useState<'params' | 'form-data'>('params')
+
+ const [response, setResponse] = useState({})
+
+ const data = watch()
+
+ const onParamsTypeChange = useCallback((type: 'params' | 'form-data') => {
+ if (type === 'params') {
+ setParamsType('params')
+ if (parameters) {
+ parameters.forEach((parameter) => {
+ setValue(parameter.argument, '')
+ })
+ } else {
+ reset({})
+
+ }
+ } else {
+ setParamsType('form-data')
+ reset({})
+ }
+ }, [parameters])
+
+ useEffect(() => {
+ if (parameters) {
+ parameters.forEach((parameter) => {
+ setValue(parameter.argument, '')
+ })
+
+ }
+ }, [parameters])
+
+ useEffect(() => {
+ reset({})
+ setRequestType('GET')
+ setParamsType('params')
+ setResponse({})
+ }, [open])
+
+ const { call } = useContext(FrappeContext) as FrappeConfig
+
+ const returnString = (value: any) => {
+ try {
+ // If it's a string but contains JSON-like structure, attempt to parse it
+ const parsedValue = JSON.parse(value);
+
+ if (Array.isArray(parsedValue)) {
+ return JSON.stringify(parsedValue); // Convert array to string
+ } else if (typeof parsedValue === 'object' && parsedValue !== null) {
+ return JSON.stringify(parsedValue); // Convert object to string
+ }
+ } catch (error) {
+ // If JSON.parse fails, it's not a JSON string, continue to the next checks
+ }
+
+ // Check if it's already a string
+ if (typeof value === 'string') {
+ return value; // Return the string as it is
+ }
+
+ // Check if it's an array
+ if (Array.isArray(value)) {
+ return JSON.stringify(value); // Convert array to string
+ }
+
+ // Check if it's an object
+ if (typeof value === 'object' && value !== null) {
+ return JSON.stringify(value); // Convert object to string
+ }
+
+ // If it's not string, array, or object, return the string representation of the value
+ return String(value);
+ }
+
+ const onSubmit = (data: any) => {
+ const filteredParameterValues = Object.keys(data).reduce((acc, key) => {
+ if (data[key] !== '') {
+ // check if value is string if it not if it is list or object then convert it to string using JSON.stringify
+ acc[key] = returnString(data[key])
+ }
+ return acc
+ }, {} as Record)
+
+ const paramsData = handleFormData(filteredParameterValues)
+ console.log('paramsData', paramsData, filteredParameterValues)
+
+ if (requestType === 'GET') {
+ // call get api with endpoint and parameterValues
+ call.get(endpoint, paramsData).then((response) => {
+ setResponse(response)
+ }).catch((error) => {
+ setResponse(error)
+ })
+ } else {
+ // call post api with endpoint and parameterValues
+ call.post(endpoint, paramsData).then((response) => {
+ setResponse(response)
+ }).catch((error) => {
+ setResponse(error)
+ })
+ }
+ }
+
+ const handleFormData = (data: any) => {
+ if (paramsType === 'form-data') {
+ const obj = {}
+ Object.keys(data)?.filter((key) => key.includes('key')).forEach((key) => {
+ // @ts-ignore
+ obj[data[key]] = data[key.replace('key', 'value')]
+ })
+ return obj
+ }
+ else {
+ return Object.keys(data)?.filter((key) => !(key.includes('key') || key.includes('value'))).reduce((acc, key) => {
+ acc[key] = data[key]
+ return acc
+ }, {} as Record)
+ }
+ }
+
+ return (
+
+
+
+ API Request
+
+ Easily test and interact with your API endpoints.
+
+
+
+
+
+
+
+
+
+ setRequestType('GET')}>
+ GET
+
+ setRequestType('POST')}>
+ POST
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ onParamsTypeChange('params')}>
+ Params
+
+ onParamsTypeChange('form-data')}>
+ Form Data
+
+
+
+
+
+
+ {paramsType === 'form-data' &&
+
+
}
+
+
+
+
+
+
+
+
+ {
+ JSON.stringify(response, null, 2)
+ }
+
+
+
+
+
+ Note: Please use double quotes (") for parameters instead of single quotes (').
+
+
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/dashboard/src/components/features/api_viewer/APIDetails.tsx b/dashboard/src/components/features/api_viewer/APIDetails.tsx
index c8f32e3..cf7651f 100644
--- a/dashboard/src/components/features/api_viewer/APIDetails.tsx
+++ b/dashboard/src/components/features/api_viewer/APIDetails.tsx
@@ -8,8 +8,13 @@ import { web_url } from "@/config/socket"
import { APIData, Argument } from "@/types/APIData"
import { XMarkIcon } from "@heroicons/react/24/outline"
import { useFrappeGetCall } from "frappe-react-sdk"
-import { useMemo } from "react"
+import { useMemo, useState } from "react"
import { MdOutlineFileDownload } from "react-icons/md"
+import Markdown from "react-markdown"
+import { AiOutlineThunderbolt } from "react-icons/ai"
+import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"
+import { Dialog } from "@/components/ui/dialog"
+import { APIClientContent } from "../APIClient/APIClientContent"
import { APIDocumentationOfSiteApp, Documentation } from "../documentation/APIDocumentation"
export const APIDetails = ({ project_branch, endpointData, selectedEndpoint, setSelectedEndpoint, viewerType }: { project_branch: string, endpointData: APIData[], selectedEndpoint: string, setSelectedEndpoint: React.Dispatch>, viewerType: string }) => {
@@ -48,11 +53,13 @@ export const APIDetails = ({ project_branch, endpointData, selectedEndpoint, set
}
}
+ const [apiOpen, setApiOpen] = useState(false)
+
return (
-
API Details
+
{data?.name}
{data?.allow_guest || data?.xss_safe ?
{data?.allow_guest &&
Allow Guest
@@ -77,15 +84,32 @@ export const APIDetails = ({ project_branch, endpointData, selectedEndpoint, set
-
-
- Name :
- {data?.name}
-
-
+
- Endpoint :
-
-
- {data?.api_path}
-
+
+
+
-
+ {data?.api_path}
+
+
+
+
+ {viewerType === 'app' &&
+
+
+
+
+
+ Click to make an API call to this endpoint
+
+
+ }
+
@@ -131,6 +155,9 @@ export const APIDetails = ({ project_branch, endpointData, selectedEndpoint, set
}
+
)
}
@@ -188,7 +215,7 @@ export const CodeSnippet = ({ apiData, project_branch, file_path, viewerType }:
{isLoading &&
}
-
+
@@ -231,7 +258,7 @@ export const Bruno = ({ doc }: { doc: APIData }) => {
{isLoading &&
}
-
+
-
+
+
+
+
+ Click to view available bench commands in this app
+
+
+
diff --git a/dashboard/src/components/features/commands/CommandsContent.tsx b/dashboard/src/components/features/commands/CommandsContent.tsx
index 195111c..eb1bebd 100644
--- a/dashboard/src/components/features/commands/CommandsContent.tsx
+++ b/dashboard/src/components/features/commands/CommandsContent.tsx
@@ -85,7 +85,7 @@ const CommandComponent = ({ name, help }: CommandResponse) => {
return (
-
+
{`bench ${name}`}
{
const location = useLocation()
- const { apps } = location.state as { apps: string[] }
+ const { apps } = location.state as { apps: string[] } || {}
- const [selectedApps, setSelectedApps] = useState(apps)
+ const [selectedApps, setSelectedApps] = useState(apps ?? [])
const [erdDoctypes, setERDDocTypes] = useState<{ doctype: string, project_branch: string }[]>([])
@@ -39,6 +39,12 @@ export const ERDViewer = () => {
}, [])
+ useEffect(() => {
+ if (!apps) {
+ setOpen(true)
+ }
+ }, [apps])
+
const flowRef = useRef(null)
return (
@@ -113,6 +119,12 @@ export const ModuleDoctypeListDrawer = ({ open, setOpen, apps, setSelectedApps,
setOpenDialog(false)
}
+ useEffect(() => {
+ if (apps.length === 0) {
+ setOpenDialog(true)
+ }
+ }, [])
+
return (
<>
@@ -145,7 +157,7 @@ export const ModuleDoctypeListDrawer = ({ open, setOpen, apps, setSelectedApps,
: null}
- {apps.length ? : null}
+
diff --git a/dashboard/src/pages/overview/Overview.tsx b/dashboard/src/pages/overview/Overview.tsx
index af8c453..64090d7 100644
--- a/dashboard/src/pages/overview/Overview.tsx
+++ b/dashboard/src/pages/overview/Overview.tsx
@@ -9,9 +9,9 @@ export const Overview = () => {
const areAppsAvailable = isSystemAppAvailable()
return (
-
+
- {areAppsAvailable ?
+ {areAppsAvailable ?
Projects
Site Apps