diff --git a/commit/api/bruno.py b/commit/api/bruno.py index 904c0e7..4f5e258 100644 --- a/commit/api/bruno.py +++ b/commit/api/bruno.py @@ -1,7 +1,7 @@ import frappe @frappe.whitelist() -def generate_bruno_file(data): +def generate_bruno_file(data, return_type='download'): request_data = frappe.parse_json(data) """ Generates .bru file content for a single request based on the provided request data. @@ -35,32 +35,35 @@ def format_name(name): bru_files = {} - for request_type in request_types: - seq = 1 - request_type_upper = request_type.upper() - request_type_lower = request_type.lower() - url = f"{base_url_template}/{api_path}" - - query_string = '&'.join([f'{k}={v}' for k, v in params.items() if v]) - full_url = f'{url}?{query_string}' if query_string else url - - bru_content = [] + request_type = request_types[0] + seq = 1 + request_type_upper = request_type.upper() + request_type_lower = request_type.lower() + url = f"{base_url_template}/{api_path}" + + query_string = '&'.join([f'{k}={v}' for k, v in params.items() if v]) + full_url = f'{url}?{query_string}' if query_string else url - # Meta section - bru_content.append(f'meta {{\n name: {name}\n type: http\n seq: {seq}\n}}\n') + bru_content = [] - # Request section - bru_content.append(f'{request_type_lower} {{\n url: {full_url}\n body: none\n auth: none\n}}\n') + # Meta section + bru_content.append(f'meta {{\n name: {name}\n type: http\n seq: {seq}\n}}\n') - # Params section - if params: - bru_content.append(f'params:query {{\n') - for k, v in params.items(): - if v: - bru_content.append(f' {k}: {v}\n') - bru_content.append('}\n') + # Request section + bru_content.append(f'{request_type_lower} {{\n url: {full_url}\n body: none\n auth: none\n}}\n') - bru_files[request_type_upper] = '\n'.join(bru_content) + # Params section + if params: + bru_content.append(f'params:query {{\n') + for k, v in params.items(): + if v: + bru_content.append(f' {k}: {v}\n') + bru_content.append('}\n') + + bru_files[request_type_upper] = '\n'.join(bru_content) + if return_type == 'download': frappe.local.response.filename = f'{name} {request_type}.bru' if len(request_types) > 1 else f'{name}.bru' frappe.local.response.filecontent = bru_files[request_type_upper] - frappe.local.response.type = 'download' \ No newline at end of file + frappe.local.response.type = 'download' + else: + return bru_files[request_type_upper] \ No newline at end of file diff --git a/commit/commit/code_analysis/schema_builder.py b/commit/commit/code_analysis/schema_builder.py index 9c0928e..b2ef681 100644 --- a/commit/commit/code_analysis/schema_builder.py +++ b/commit/commit/code_analysis/schema_builder.py @@ -33,6 +33,7 @@ def get_schema_from_doctypes_json(doctypes_json: dict): 'name': field.get('label', fieldname), 'id': fieldname, 'format': fieldtype, + 'is_custom_field': field.get('is_custom_field') or False, } columns.append(column) @@ -50,6 +51,7 @@ def get_schema_from_doctypes_json(doctypes_json: dict): 'name': doctype_name, 'id': doctype_name, 'module': doctype_json.get('module'), + 'istable': doctype_json.get('istable'), 'columns': columns, } tables.append(table) diff --git a/dashboard/package.json b/dashboard/package.json index c127a0f..86e0a58 100644 --- a/dashboard/package.json +++ b/dashboard/package.json @@ -42,6 +42,7 @@ "cmdk": "^0.2.0", "downshift": "^9.0.6", "frappe-react-sdk": "^1.7.0", + "html-to-image": "^1.11.11", "install": "^0.13.0", "jotai": "^2.8.3", "lodash": "^4.17.21", @@ -54,7 +55,7 @@ "react-icons": "^5.2.1", "react-markdown": "^9.0.1", "react-router-dom": "^6.14.1", - "reactflow": "^11.7.4", + "reactflow": "^11.11.4", "rehype-raw": "^7.0.0", "remark-gfm": "^3.0.1", "shadcn-ui": "^0.8.0", diff --git a/dashboard/src/components/common/CopyToClipboard/CopyToClipboard.tsx b/dashboard/src/components/common/CopyToClipboard/CopyToClipboard.tsx index 41e315f..0df317e 100644 --- a/dashboard/src/components/common/CopyToClipboard/CopyToClipboard.tsx +++ b/dashboard/src/components/common/CopyToClipboard/CopyToClipboard.tsx @@ -1,9 +1,9 @@ -import { Button } from "@/components/ui/button" +import { Button, ButtonProps } from "@/components/ui/button" import { useToast } from "@/components/ui/use-toast" import { CheckIcon, CopyIcon } from "@radix-ui/react-icons" import { useEffect, useState } from "react" -interface CopyButtonProps extends React.HTMLAttributes { +interface CopyButtonProps extends ButtonProps { value: string } diff --git a/dashboard/src/components/features/api_viewer/APIDetails.tsx b/dashboard/src/components/features/api_viewer/APIDetails.tsx index 7f5829b..d312227 100644 --- a/dashboard/src/components/features/api_viewer/APIDetails.tsx +++ b/dashboard/src/components/features/api_viewer/APIDetails.tsx @@ -2,12 +2,14 @@ import CopyButton from "@/components/common/CopyToClipboard/CopyToClipboard" import { ErrorBanner } from "@/components/common/ErrorBanner/ErrorBanner" import { FullPageLoader } from "@/components/common/FullPageLoader/FullPageLoader" import { Tabs } from "@/components/common/Tabs" +import { Button } from "@/components/ui/button" import { Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table" 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 { MdOutlineFileDownload } from "react-icons/md" import Markdown from "react-markdown" export const APIDetails = ({ project_branch, endpointData, selectedEndpoint, setSelectedEndpoint, viewerType }: { project_branch: string, endpointData: APIData[], selectedEndpoint: string, setSelectedEndpoint: React.Dispatch>, viewerType: string }) => { @@ -17,10 +19,17 @@ export const APIDetails = ({ project_branch, endpointData, selectedEndpoint, set }, [endpointData, selectedEndpoint]) const tabs = [ - { name: 'Parameters', content: }, - { name: 'Code', content: }, - { name: 'Documentation', content: } + { + name: 'Parameters', content: + }, + { + name: 'Code', content: + }, + { + name: 'Bruno', content: + }, ] + data?.documentation && tabs.push({ name: 'Documentation', content: }) const requestTypeBgColor = (requestType: string) => { switch (requestType) { @@ -37,13 +46,6 @@ export const APIDetails = ({ project_branch, endpointData, selectedEndpoint, set } } - const rest = useMemo(() => { - if (data) { - const { allow_guest, xss_safe, documentation, block_end, block_start, index, ...rest } = data - return rest - } - }, [data]) - const requestTypeBorderColor = (requestType: string) => { switch (requestType) { case 'GET': @@ -64,11 +66,6 @@ export const APIDetails = ({ project_branch, endpointData, selectedEndpoint, set

API Details

-
}
- +
                     {isLoading && }
@@ -194,4 +191,62 @@ export const Documentation = ({ documentation }: { documentation: string }) => {
     return (
         {documentation}
     )
+}
+
+export const Bruno = ({ doc }: { doc: APIData }) => {
+
+    const rest = useMemo(() => {
+        if (doc) {
+            const { allow_guest, xss_safe, documentation, block_end, block_start, index, ...rest } = doc
+            return rest
+        }
+    }, [doc])
+
+    const { data, error, isLoading } = useFrappeGetCall('commit.api.bruno.generate_bruno_file', {
+        data: JSON.stringify(rest),
+        type: 'copy'
+    }, rest ? undefined : null, {
+        revalidateOnFocus: false,
+        revalidateIfStale: false,
+    })
+
+    const copyValue = () => {
+        const content = JSON.parse(JSON.stringify(data ?? '') ?? '[]')
+        return content
+
+    }
+
+    return (
+        
+ {error && } + {isLoading &&
+ +
} + +
+
+ + +
+
+
+                    {isLoading && }
+                    {data && 
{data}
} +
+
+
+ +
bruno is a Fast and Git-Friendly Opensource API client.
+ +
+
+ ) } \ No newline at end of file diff --git a/dashboard/src/components/features/meta_apps/YourAppAPIExplorer.tsx b/dashboard/src/components/features/meta_apps/YourAppAPIExplorer.tsx index fcca4b4..10db415 100644 --- a/dashboard/src/components/features/meta_apps/YourAppAPIExplorer.tsx +++ b/dashboard/src/components/features/meta_apps/YourAppAPIExplorer.tsx @@ -5,10 +5,9 @@ import { AppsData } from "./YourApps" import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog" import { Button } from "@/components/ui/button" import { AiOutlineApi } from "react-icons/ai" -import { useCallback, useMemo, useState } from "react" +import { useCallback, useState } from "react" import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group" import { Label } from "@/components/ui/label" -import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" export const YourAppAPIExplorer = () => { @@ -55,11 +54,11 @@ export const ViewAPIExplorerContent = ({ data }: { data: AppsData[] }) => { }, [branch, navigate]) return ( - + Select Apps - Select the apps to view ERD + Select the apps to view API's
    @@ -85,9 +84,6 @@ export interface ViewAPIExplorerProps { } export const ViewAPIExplorerCard = ({ app }: ViewAPIExplorerProps) => { - const appNameInitials = useMemo(() => { - return app.app_name.split('_').map((word) => word[0]).join('').toUpperCase() - }, [app]) return (
  • @@ -95,10 +91,6 @@ export const ViewAPIExplorerCard = ({ app }: ViewAPIExplorerProps) => {
    diff --git a/dashboard/src/components/features/meta_apps/YourApps.tsx b/dashboard/src/components/features/meta_apps/YourApps.tsx index 401e4e7..6e663ef 100644 --- a/dashboard/src/components/features/meta_apps/YourApps.tsx +++ b/dashboard/src/components/features/meta_apps/YourApps.tsx @@ -1,14 +1,13 @@ import { FullPageLoader } from "@/components/common/FullPageLoader/FullPageLoader" import { Avatar, AvatarFallback } from "@/components/ui/avatar" import { Button } from "@/components/ui/button" -import { Card, CardContent, CardDescription, CardFooter, CardTitle } from "@/components/ui/card" +import { Card, CardContent, CardDescription, CardTitle } from "@/components/ui/card" import { AvatarImage } from "@radix-ui/react-avatar" import { useFrappeGetCall } from "frappe-react-sdk" import { useMemo } from "react" import { BsDatabase } from "react-icons/bs" import { useNavigate } from "react-router-dom" import { YourAppAPIExplorer } from "./YourAppAPIExplorer" -import { Badge } from "@/components/ui/badge" export interface AppsData { app_name: string @@ -38,12 +37,13 @@ export const YourApps = () => { if (data && data.message) { return ( -
    +
    -
    +
    {data.message.map((app: AppsData) => { return })} @@ -70,20 +70,26 @@ const AppsCard = ({ app }: { app: AppsData }) => { }, [app]) return ( - + - - - {appNameInitials} +
    + + + {appNameInitials} - {app.app_name} +
    +
    +
    + {app.app_name} +
    + {app.app_publisher} +
    +
    {app.app_description} +
    - - {app.app_publisher} -
    ) } \ No newline at end of file diff --git a/dashboard/src/components/features/projects/APIExplorer.tsx b/dashboard/src/components/features/projects/APIExplorer.tsx index 8b394a4..31661a3 100644 --- a/dashboard/src/components/features/projects/APIExplorer.tsx +++ b/dashboard/src/components/features/projects/APIExplorer.tsx @@ -4,9 +4,8 @@ import { useFrappeGetCall } from "frappe-react-sdk" import { ProjectData, ProjectWithBranch } from "./Projects" import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog" import { AiOutlineApi } from "react-icons/ai" -import { useCallback, useMemo, useState } from "react" +import { useCallback, useState } from "react" import { useNavigate } from "react-router-dom" -import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group" import { CommitProjectBranch } from "@/types/commit/CommitProjectBranch" @@ -56,11 +55,11 @@ export const ViewAPIExplorerContent = ({ data }: { data: ProjectData[] }) => { }, [branch, navigate]) return ( - + Select Apps - Select the apps to view ERD + Select the apps to view API's
      @@ -93,19 +92,12 @@ export const ViewAPIExplorerCard = ({ project }: ViewERDProjectCardProps) => { const [branch, setBranch] = useState(project.branches[0]?.name) - const appNameInitials = useMemo(() => { - return project.display_name.split('_').map((word) => word[0]).join('').toUpperCase() - }, [project]) return (
    • diff --git a/dashboard/src/components/features/projects/Branch/ManageBranchItem.tsx b/dashboard/src/components/features/projects/Branch/ManageBranchItem.tsx index ff6662d..5f013e4 100644 --- a/dashboard/src/components/features/projects/Branch/ManageBranchItem.tsx +++ b/dashboard/src/components/features/projects/Branch/ManageBranchItem.tsx @@ -92,7 +92,7 @@ const ManageBranchItem = ({ branch, mutate }: { branch: CommitProjectBranch, mut Fetch latest code - +
      -
      +
      {data.message.map((org: ProjectData) => { const orgName = org.organization_name; return org.projects.map((project) => ( diff --git a/dashboard/src/components/features/projects/Projects/ProjectCard.tsx b/dashboard/src/components/features/projects/Projects/ProjectCard.tsx index 3531529..c12a214 100644 --- a/dashboard/src/components/features/projects/Projects/ProjectCard.tsx +++ b/dashboard/src/components/features/projects/Projects/ProjectCard.tsx @@ -3,7 +3,6 @@ import { Card, CardContent, CardDescription, - CardFooter, CardTitle, } from "@/components/ui/card"; import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; @@ -19,7 +18,6 @@ import { Dialog } from "@radix-ui/react-dialog"; import ManageBranchModal from "../Branch/ManageBranchModal"; import { ProjectWithBranch, ProjectData } from "../Projects"; import DeleteProjectModal from "./DeleteProjectModal"; -import { Badge } from "@/components/ui/badge"; export interface ProjectCardProps { project: ProjectWithBranch @@ -42,21 +40,26 @@ const ProjectCard = ({ project, mutate, orgName }: ProjectCardProps) => { const [openDeleteDialogModal, setOpenDeleteDialogModal] = useState(false) return ( - +
      - - - + + + {appNameInitials}
      -
      +
      - {project.display_name} +
      + {project.display_name} +
      + {orgName} +
      +
      {isCreateAccess && @@ -83,8 +86,6 @@ const ProjectCard = ({ project, mutate, orgName }: ProjectCardProps) => { {project.description}
      - -
      @@ -95,10 +96,6 @@ const ProjectCard = ({ project, mutate, orgName }: ProjectCardProps) => { - - - {orgName} - ); } diff --git a/dashboard/src/components/features/projects/ViewERDAppDialog.tsx b/dashboard/src/components/features/projects/ViewERDAppDialog.tsx index d98b5e9..3f16b7d 100644 --- a/dashboard/src/components/features/projects/ViewERDAppDialog.tsx +++ b/dashboard/src/components/features/projects/ViewERDAppDialog.tsx @@ -1,10 +1,9 @@ import { Button } from "@/components/ui/button" import { DialogHeader, DialogFooter, DialogContent, DialogDescription, DialogTitle } from "@/components/ui/dialog" -import { useState, useMemo } from "react" +import { useState } from "react" import { useNavigate } from "react-router-dom" import { ProjectData, ProjectWithBranch } from "./Projects" import { Checkbox } from "@/components/ui/checkbox" -import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" import { CommitProjectBranch } from "@/types/commit/CommitProjectBranch" @@ -15,6 +14,7 @@ export const ViewERDDialogContent = ({ data }: { data: ProjectData[] }) => { const navigate = useNavigate() const onViewERD = () => { + window.sessionStorage.removeItem('ERDDoctypes') navigate('/project-erd', { state: { apps: apps @@ -23,7 +23,7 @@ export const ViewERDDialogContent = ({ data }: { data: ProjectData[] }) => { } return ( - + Select Apps @@ -57,9 +57,6 @@ export const ViewERDProjectCard = ({ project, apps, setApps }: ViewERDProjectCar const [branch, setBranch] = useState(project.branches[0]?.name) - const appNameInitials = useMemo(() => { - return project.display_name.split('_').map((word) => word[0]).join('').toUpperCase() - }, [project]) return (
    • @@ -76,10 +73,6 @@ export const ViewERDProjectCard = ({ project, apps, setApps }: ViewERDProjectCar } }} /> - - - {appNameInitials} -

      {project.display_name}

      diff --git a/dashboard/src/pages/features/erd/ERDForDoctypes.tsx b/dashboard/src/pages/features/erd/ERDForDoctypes.tsx index 8fd45c3..558e351 100644 --- a/dashboard/src/pages/features/erd/ERDForDoctypes.tsx +++ b/dashboard/src/pages/features/erd/ERDForDoctypes.tsx @@ -14,9 +14,10 @@ export interface Props { project_branch: string[] doctypes: { doctype: string, project_branch: string }[] setDocTypes: React.Dispatch> + flowRef: React.MutableRefObject } -export const ERDForDoctypes = ({ project_branch, doctypes, setDocTypes }: Props) => { +export const ERDForDoctypes = ({ project_branch, doctypes, setDocTypes, flowRef }: Props) => { const [data, setData] = useState(null) const { call, error, loading } = useFrappePostCall<{ message: SchemaData }>('commit.api.erd_viewer.get_erd_schema_for_doctypes') @@ -40,7 +41,7 @@ export const ERDForDoctypes = ({ project_branch, doctypes, setDocTypes }: Props) } if (data) { - return + return } return null diff --git a/dashboard/src/pages/features/erd/ERDViewer.tsx b/dashboard/src/pages/features/erd/ERDViewer.tsx index 37bd067..51303dc 100644 --- a/dashboard/src/pages/features/erd/ERDViewer.tsx +++ b/dashboard/src/pages/features/erd/ERDViewer.tsx @@ -5,7 +5,7 @@ import { AppModuleData } from "@/types/CommitProjectBranch" import { Dialog, Transition } from "@headlessui/react" import { XMarkIcon } from "@heroicons/react/20/solid" import { useFrappeGetCall } from "frappe-react-sdk" -import { Fragment, useEffect, useMemo, useState } from "react" +import { Fragment, useEffect, useMemo, useRef, useState } from "react" import { useLocation } from "react-router-dom" import { Header } from "@/components/common/Header" import { Input } from "@/components/ui/input" @@ -14,6 +14,8 @@ import { ERDForDoctypes } from "./ERDForDoctypes" import { Dialog as Dialog2 } from "@/components/ui/dialog" import { Popover, PopoverTrigger } from "@/components/ui/popover" import { DoctypeListPopover, ViewERDAppList } from "./meta/ERDDoctypeAndAppModal" +import { BsDownload } from "react-icons/bs" +import { toPng } from 'html-to-image' export const ERDViewer = () => { @@ -26,22 +28,53 @@ export const ERDViewer = () => { const [selectedApps, setSelectedApps] = useState(apps) const [erdDoctypes, setERDDocTypes] = useState<{ doctype: string, project_branch: string }[]>([]) + + useEffect(() => { + const doctypes = JSON.parse(window.sessionStorage.getItem('ERDDoctypes') ?? '[]') + const filteredDoctypes = doctypes.filter((d: { doctype: string, project_branch: string }) => selectedApps.includes(d.project_branch)) ?? [] + setERDDocTypes(filteredDoctypes) + if (filteredDoctypes.length) { + setOpen(false) + } + + }, []) + + const flowRef = useRef(null) + return (
      - @@ -66,6 +99,7 @@ export const ModuleDoctypeListDrawer = ({ open, setOpen, apps, setSelectedApps, const onGenerateERD = () => { setERDDocTypes(doctype) + window.sessionStorage.setItem('ERDDoctypes', JSON.stringify(doctype)) setOpen(false) } diff --git a/dashboard/src/pages/features/erd/Graph.tsx b/dashboard/src/pages/features/erd/Graph.tsx index 9322228..d6cfc92 100644 --- a/dashboard/src/pages/features/erd/Graph.tsx +++ b/dashboard/src/pages/features/erd/Graph.tsx @@ -16,20 +16,18 @@ import ReactFlow, { useEdgesState, useNodesState } from 'reactflow' -// import { uniqBy } from 'lodash' import 'reactflow/dist/style.css' import { PostgresTable, PostgresRelationship, TableNodeData } from '@/types/Table' import { CgMaximizeAlt } from 'react-icons/cg' import { TbArrowsMinimize } from 'react-icons/tb' import { TableNode } from './TableNode' import { TableDrawer } from '../TableDrawer/TableDrawer' -// import { set } from 'lodash' // ReactFlow is scaling everything by the factor of 2 export const NODE_WIDTH = 320 export const NODE_ROW_HEIGHT = 40 -export const Graph = ({ tables, relationships, project_branch, setDoctypes, doctypes }: { +export const Graph = ({ tables, relationships, project_branch, setDoctypes, doctypes, flowRef }: { tables: PostgresTable[] relationships: PostgresRelationship[] project_branch: string[] @@ -41,10 +39,11 @@ export const Graph = ({ tables, relationships, project_branch, setDoctypes, doct doctype: string; project_branch: string; }[] + flowRef: React.MutableRefObject }) => { return ( - + ) } @@ -63,6 +62,7 @@ function getGraphDataFromTables(tables: PostgresTable[], relationships: Postgres id: column.id, name: column.name, format: column.format, + is_custom_field: column.is_custom_field, } }) @@ -72,6 +72,7 @@ function getGraphDataFromTables(tables: PostgresTable[], relationships: Postgres data: { name: table.name, isForeign: false, + istable: table.istable, columns, }, position: { x: 0, y: 0 }, @@ -183,7 +184,8 @@ const TablesGraph: FC<{ doctype: string; project_branch: string; }[] -}> = ({ tables, relationships, setDoctypes, doctypes }) => { + flowRef: React.MutableRefObject +}> = ({ tables, relationships, setDoctypes, doctypes, flowRef }) => { const [nodes, setNodes, onNodesChange] = useNodesState([]); const [edges, setEdges, onEdgesChange] = useEdgesState([]); const [fullscreenOn, setFullScreen] = useState(false); @@ -293,9 +295,11 @@ const TablesGraph: FC<{ ); setDoctypes((doctypes) => { - return doctypes.filter((doctype) => { + const doc = doctypes.filter((doctype) => { return !nodes.includes(doctype.doctype); - }); + }) + window.sessionStorage.setItem('ERDDoctypes', JSON.stringify(doc)) + return doc }) }, [setNodes, setEdges, setDoctypes] @@ -305,14 +309,10 @@ const TablesGraph: FC<{ const { nodes, edges } = getGraphDataFromTables(tables, relationships) setNodes(nodes) setEdges(edges) - // reactFlowInstance.setNodes(nodes) - // reactFlowInstance.setEdges(edges) setTimeout(() => reactFlowInstance.fitView({})) // it needs to happen during next event tick }, [tables, relationships, setNodes, setEdges, reactFlowInstance]) - - return ( <>
      @@ -321,6 +321,7 @@ const TablesGraph: FC<{ style={{ backgroundColor: '#F7FAFC', }} + ref={flowRef} nodes={nodes} edges={edges} onNodesChange={onNodesChange} @@ -340,8 +341,6 @@ const TablesGraph: FC<{ deletable: false, style: { stroke: '#0ea5e9', - strokeWidth: 2, - // color: '#082f49' }, }} nodeTypes={nodeTypes} @@ -356,15 +355,20 @@ const TablesGraph: FC<{ {fullscreenOn && } - {/* */} - {/* - */} +
      +
      +
      +
      Table
      +
      +
      +
      +
      Child Table
      +
      +
      +
      +
      Custom Field
      +
      +
      setSelectedDoctype(null)} doctype={selectedDoctype ?? ''} project_branch={doctypes.find(d => d.doctype === selectedDoctype)?.project_branch} key={selectedDoctype} /> diff --git a/dashboard/src/pages/features/erd/TableNode.tsx b/dashboard/src/pages/features/erd/TableNode.tsx index 0a19c6a..2e369fb 100644 --- a/dashboard/src/pages/features/erd/TableNode.tsx +++ b/dashboard/src/pages/features/erd/TableNode.tsx @@ -3,6 +3,7 @@ import { NodeProps, Handle, useReactFlow } from "reactflow"; import { NODE_WIDTH } from "./Graph"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"; import { XMarkIcon } from '@heroicons/react/24/outline' +import { cn } from "@/lib/utils"; export const TableNode = ({ data, targetPosition, sourcePosition }: NodeProps) => { @@ -20,24 +21,25 @@ export const TableNode = ({ data, targetPosition, sourcePosition }: NodeProps
      -
      + {data.istable ?
      {data.name}
      + :
      + {data.name} +
      } {data.columns.map((column) => (
      {column.name} @@ -64,7 +66,7 @@ export const TableNode = ({ data, targetPosition, sourcePosition }: NodeProps - diff --git a/dashboard/src/pages/features/erd/meta/CreateERDForMeta.tsx b/dashboard/src/pages/features/erd/meta/CreateERDForMeta.tsx index c049ef9..f3f9e21 100644 --- a/dashboard/src/pages/features/erd/meta/CreateERDForMeta.tsx +++ b/dashboard/src/pages/features/erd/meta/CreateERDForMeta.tsx @@ -1,7 +1,7 @@ import { Header } from "@/components/common/Header" import { Button } from "@/components/ui/button" import { Dialog, Transition } from "@headlessui/react" -import { Fragment, useEffect, useState } from "react" +import { Fragment, useEffect, useRef, useState } from "react" import { XMarkIcon } from "@heroicons/react/20/solid" import { ErrorBanner } from "@/components/common/ErrorBanner/ErrorBanner" import { FullPageLoader } from "@/components/common/FullPageLoader/FullPageLoader" @@ -13,28 +13,58 @@ import { useDebounce } from "@/hooks/useDebounce" import { ERDForMetaDoctypes } from "./ERDForMetaDoctype" import { Popover, PopoverTrigger } from "@/components/ui/popover" import { DoctypeListPopoverForMeta } from "./ERDDoctypeAndAppModal" +import { BsDownload } from "react-icons/bs" +import { toPng } from 'html-to-image' export const CreateERD = () => { const [open, setOpen] = useState(true) const [erdDoctypes, setERDDocTypes] = useState([]) + useEffect(() => { + const doctypes = JSON.parse(window.sessionStorage.getItem('ERDMetaDoctypes') ?? '[]') + if (doctypes.length) { + setERDDocTypes(doctypes) + setOpen(false) + } + + }, []) + + const flowRef = useRef(null) + return (
      - @@ -55,6 +85,7 @@ export const ModuleDoctypeListDrawer = ({ open, setOpen, erdDoctypes, setERDDocT const onGenerateERD = () => { setERDDocTypes(doctype) + window.sessionStorage.setItem('ERDMetaDoctypes', JSON.stringify(doctype)) setOpen(false) } diff --git a/dashboard/src/pages/features/erd/meta/ERDDoctypeAndAppModal.tsx b/dashboard/src/pages/features/erd/meta/ERDDoctypeAndAppModal.tsx index 50d2d50..1423384 100644 --- a/dashboard/src/pages/features/erd/meta/ERDDoctypeAndAppModal.tsx +++ b/dashboard/src/pages/features/erd/meta/ERDDoctypeAndAppModal.tsx @@ -76,7 +76,7 @@ export const ViewERDAppList = ({ apps, setApps, onClose }: { apps: string[], set onClose() } return ( - + Select Apps diff --git a/dashboard/src/pages/features/erd/meta/ERDForMetaDoctype.tsx b/dashboard/src/pages/features/erd/meta/ERDForMetaDoctype.tsx index 8dcf468..b604dd0 100644 --- a/dashboard/src/pages/features/erd/meta/ERDForMetaDoctype.tsx +++ b/dashboard/src/pages/features/erd/meta/ERDForMetaDoctype.tsx @@ -13,9 +13,10 @@ export interface SchemaData { export interface Props { doctypes: string[] setDocTypes: React.Dispatch> + flowRef: React.MutableRefObject } -export const ERDForMetaDoctypes = ({ doctypes, setDocTypes }: Props) => { +export const ERDForMetaDoctypes = ({ doctypes, setDocTypes, flowRef }: Props) => { const [data, setData] = useState(null) const { call, error, loading } = useFrappePostCall<{ message: SchemaData }>('commit.api.erd_viewer.get_meta_erd_schema_for_doctypes') @@ -38,7 +39,7 @@ export const ERDForMetaDoctypes = ({ doctypes, setDocTypes }: Props) => { } if (data) { - return + return } return null diff --git a/dashboard/src/pages/features/erd/meta/MetaGraph.tsx b/dashboard/src/pages/features/erd/meta/MetaGraph.tsx index 14f1bb6..5d0a8e4 100644 --- a/dashboard/src/pages/features/erd/meta/MetaGraph.tsx +++ b/dashboard/src/pages/features/erd/meta/MetaGraph.tsx @@ -1,7 +1,6 @@ import dagre from '@dagrejs/dagre' import { FC, useCallback, useEffect, useMemo, useState } from 'react' import '../../../../styles/flow.css' - import ReactFlow, { Background, Controls, @@ -17,29 +16,29 @@ import ReactFlow, { useEdgesState, useNodesState } from 'reactflow' -// import { uniqBy } from 'lodash' import 'reactflow/dist/style.css' import { PostgresTable, PostgresRelationship, TableNodeData } from '@/types/Table' import { CgMaximizeAlt } from 'react-icons/cg' import { TbArrowsMinimize } from 'react-icons/tb' import { TableNode } from '../TableNode' import { MetaTableDrawer } from '../../TableDrawer/MetaTableDrawer' - -// import { set } from 'lodash' +import { Button } from '@/components/ui/button' +import { DownloadIcon } from '@radix-ui/react-icons' // ReactFlow is scaling everything by the factor of 2 export const NODE_WIDTH = 320 export const NODE_ROW_HEIGHT = 40 -export const MetaGraph = ({ tables, relationships, setDoctypes, doctypes }: { +export const MetaGraph = ({ tables, relationships, setDoctypes, doctypes, flowRef }: { tables: PostgresTable[] relationships: PostgresRelationship[] setDoctypes: React.Dispatch> doctypes: string[] + flowRef: React.MutableRefObject }) => { return ( - + ) } @@ -58,6 +57,7 @@ function getGraphDataFromTables(tables: PostgresTable[], relationships: Postgres id: column.id, name: column.name, format: column.format, + is_custom_field: column.is_custom_field, } }) @@ -67,6 +67,7 @@ function getGraphDataFromTables(tables: PostgresTable[], relationships: Postgres data: { name: table.name, isForeign: false, + istable: table.istable, columns, }, position: { x: 0, y: 0 }, @@ -172,7 +173,8 @@ const TablesGraph: FC<{ string[] >> doctypes: string[] -}> = ({ tables, relationships, setDoctypes }) => { + flowRef: React.MutableRefObject +}> = ({ tables, relationships, setDoctypes, flowRef }) => { const [nodes, setNodes, onNodesChange] = useNodesState([]); const [edges, setEdges, onEdgesChange] = useEdgesState([]); const [fullscreenOn, setFullScreen] = useState(false); @@ -227,7 +229,6 @@ const TablesGraph: FC<{ ...ed.style, stroke: '#042f2e', } - // setHighlightEdgeClassName(ed); } return ed; @@ -282,9 +283,11 @@ const TablesGraph: FC<{ ); setDoctypes((doctypes) => { - return doctypes.filter((doctype) => { + const doc = doctypes.filter((doctype) => { return !nodes.includes(doctype); - }); + }) + window.sessionStorage.setItem('ERDMetaDoctypes', JSON.stringify(doc)) + return doc }) }, [setNodes, setEdges, setDoctypes] @@ -294,22 +297,18 @@ const TablesGraph: FC<{ const { nodes, edges } = getGraphDataFromTables(tables, relationships) setNodes(nodes) setEdges(edges) - // reactFlowInstance.setNodes(nodes) - // reactFlowInstance.setEdges(edges) setTimeout(() => reactFlowInstance.fitView({})) // it needs to happen during next event tick }, [tables, relationships, setNodes, setEdges, reactFlowInstance]) - - return ( <>
      - {/* */} } - {/* */} - {/* - */} +
      +
      +
      +
      Table
      +
      +
      +
      +
      Child Table
      +
      +
      +
      +
      Custom Field
      +
      +
      +
      + +
      setSelectedDoctype(null)} doctype={selectedDoctype ?? ''} key={selectedDoctype} /> diff --git a/dashboard/src/types/Table.ts b/dashboard/src/types/Table.ts index 3dac91b..83844c7 100644 --- a/dashboard/src/types/Table.ts +++ b/dashboard/src/types/Table.ts @@ -32,6 +32,7 @@ export type PostgresColumn = { read_only?: number, hidden?: number, default?: string, + is_custom_field: number, // default_value: string, // is_unique: boolean, // is_nullable: boolean, diff --git a/dashboard/yarn.lock b/dashboard/yarn.lock index d91ef42..28e53fa 100644 --- a/dashboard/yarn.lock +++ b/dashboard/yarn.lock @@ -1330,28 +1330,28 @@ dependencies: "@babel/runtime" "^7.13.10" -"@reactflow/background@11.3.13": - version "11.3.13" - resolved "https://registry.yarnpkg.com/@reactflow/background/-/background-11.3.13.tgz#a29bcdce01b5e881a330067bfd08c58c12fc7266" - integrity sha512-hkvpVEhgvfTDyCvdlitw4ioKCYLaaiRXnuEG+1QM3Np+7N1DiWF1XOv5I8AFyNoJL07yXEkbECUTsHvkBvcG5A== +"@reactflow/background@11.3.14": + version "11.3.14" + resolved "https://registry.yarnpkg.com/@reactflow/background/-/background-11.3.14.tgz#778ca30174f3de77fc321459ab3789e66e71a699" + integrity sha512-Gewd7blEVT5Lh6jqrvOgd4G6Qk17eGKQfsDXgyRSqM+CTwDqRldG2LsWN4sNeno6sbqVIC2fZ+rAUBFA9ZEUDA== dependencies: - "@reactflow/core" "11.11.3" + "@reactflow/core" "11.11.4" classcat "^5.0.3" zustand "^4.4.1" -"@reactflow/controls@11.2.13": - version "11.2.13" - resolved "https://registry.yarnpkg.com/@reactflow/controls/-/controls-11.2.13.tgz#a05d86b82fc49e8ed0ca35d04c838a45f90f4f15" - integrity sha512-3xgEg6ALIVkAQCS4NiBjb7ad8Cb3D8CtA7Vvl4Hf5Ar2PIVs6FOaeft9s2iDZGtsWP35ECDYId1rIFVhQL8r+A== +"@reactflow/controls@11.2.14": + version "11.2.14" + resolved "https://registry.yarnpkg.com/@reactflow/controls/-/controls-11.2.14.tgz#508ed2c40d23341b3b0919dd11e76fd49cf850c7" + integrity sha512-MiJp5VldFD7FrqaBNIrQ85dxChrG6ivuZ+dcFhPQUwOK3HfYgX2RHdBua+gx+40p5Vw5It3dVNp/my4Z3jF0dw== dependencies: - "@reactflow/core" "11.11.3" + "@reactflow/core" "11.11.4" classcat "^5.0.3" zustand "^4.4.1" -"@reactflow/core@11.11.3": - version "11.11.3" - resolved "https://registry.yarnpkg.com/@reactflow/core/-/core-11.11.3.tgz#2cdc0c684931918125d505bec3cb94f115b87747" - integrity sha512-+adHdUa7fJSEM93fWfjQwyWXeI92a1eLKwWbIstoCakHpL8UjzwhEh6sn+mN2h/59MlVI7Ehr1iGTt3MsfcIFA== +"@reactflow/core@11.11.4": + version "11.11.4" + resolved "https://registry.yarnpkg.com/@reactflow/core/-/core-11.11.4.tgz#89bd86d1862aa1416f3f49926cede7e8c2aab6a7" + integrity sha512-H4vODklsjAq3AMq6Np4LE12i1I4Ta9PrDHuBR9GmL8uzTt2l2jh4CiQbEMpvMDcp7xi4be0hgXj+Ysodde/i7Q== dependencies: "@types/d3" "^7.4.0" "@types/d3-drag" "^3.0.1" @@ -1363,12 +1363,12 @@ d3-zoom "^3.0.0" zustand "^4.4.1" -"@reactflow/minimap@11.7.13": - version "11.7.13" - resolved "https://registry.yarnpkg.com/@reactflow/minimap/-/minimap-11.7.13.tgz#99396175065a1e2d058b8639883d13c71777764f" - integrity sha512-m2MvdiGSyOu44LEcERDEl1Aj6x//UQRWo3HEAejNU4HQTlJnYrSN8tgrYF8TxC1+c/9UdyzQY5VYgrTwW4QWdg== +"@reactflow/minimap@11.7.14": + version "11.7.14" + resolved "https://registry.yarnpkg.com/@reactflow/minimap/-/minimap-11.7.14.tgz#298d7a63cb1da06b2518c99744f716560c88ca73" + integrity sha512-mpwLKKrEAofgFJdkhwR5UQ1JYWlcAAL/ZU/bctBkuNTT1yqV+y0buoNVImsRehVYhJwffSWeSHaBR5/GJjlCSQ== dependencies: - "@reactflow/core" "11.11.3" + "@reactflow/core" "11.11.4" "@types/d3-selection" "^3.0.3" "@types/d3-zoom" "^3.0.1" classcat "^5.0.3" @@ -1376,23 +1376,23 @@ d3-zoom "^3.0.0" zustand "^4.4.1" -"@reactflow/node-resizer@2.2.13": - version "2.2.13" - resolved "https://registry.yarnpkg.com/@reactflow/node-resizer/-/node-resizer-2.2.13.tgz#83faf6e2977f40b528bf100c0da634e08f8fb51a" - integrity sha512-X7ceQ2s3jFLgbkg03n2RYr4hm3jTVrzkW2W/8ANv/SZfuVmF8XJxlERuD8Eka5voKqLda0ywIZGAbw9GoHLfUQ== +"@reactflow/node-resizer@2.2.14": + version "2.2.14" + resolved "https://registry.yarnpkg.com/@reactflow/node-resizer/-/node-resizer-2.2.14.tgz#1810c0ce51aeb936f179466a6660d1e02c7a77a8" + integrity sha512-fwqnks83jUlYr6OHcdFEedumWKChTHRGw/kbCxj0oqBd+ekfs+SIp4ddyNU0pdx96JIm5iNFS0oNrmEiJbbSaA== dependencies: - "@reactflow/core" "11.11.3" + "@reactflow/core" "11.11.4" classcat "^5.0.4" d3-drag "^3.0.0" d3-selection "^3.0.0" zustand "^4.4.1" -"@reactflow/node-toolbar@1.3.13": - version "1.3.13" - resolved "https://registry.yarnpkg.com/@reactflow/node-toolbar/-/node-toolbar-1.3.13.tgz#f15a2e6ed89287a33c4305d3bd7f3991b85db4af" - integrity sha512-aknvNICO10uWdthFSpgD6ctY/CTBeJUMV9co8T9Ilugr08Nb89IQ4uD0dPmr031ewMQxixtYIkw+sSDDzd2aaQ== +"@reactflow/node-toolbar@1.3.14": + version "1.3.14" + resolved "https://registry.yarnpkg.com/@reactflow/node-toolbar/-/node-toolbar-1.3.14.tgz#c6ffc76f82acacdce654f2160dc9852162d6e7c9" + integrity sha512-rbynXQnH/xFNu4P9H+hVqlEUafDCkEoCy0Dg9mG22Sg+rY/0ck6KkrAQrYrTgXusd+cEJOMK0uOOFCK2/5rSGQ== dependencies: - "@reactflow/core" "11.11.3" + "@reactflow/core" "11.11.4" classcat "^5.0.3" zustand "^4.4.1" @@ -2922,6 +2922,11 @@ hastscript@^8.0.0: property-information "^6.0.0" space-separated-tokens "^2.0.0" +html-to-image@^1.11.11: + version "1.11.11" + resolved "https://registry.yarnpkg.com/html-to-image/-/html-to-image-1.11.11.tgz#c0f8a34dc9e4b97b93ff7ea286eb8562642ebbea" + integrity sha512-9gux8QhvjRO/erSnDPv28noDZcPZmYE7e1vFsBLKLlRlKDSqNJYebj6Qz1TGd5lsRV+X+xYyjCKjuZdABinWjA== + html-url-attributes@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/html-url-attributes/-/html-url-attributes-3.0.0.tgz#fc4abf0c3fb437e2329c678b80abb3c62cff6f08" @@ -4513,17 +4518,17 @@ react@^18.2.0: dependencies: loose-envify "^1.1.0" -reactflow@^11.7.4: - version "11.11.3" - resolved "https://registry.yarnpkg.com/reactflow/-/reactflow-11.11.3.tgz#5e8d8b395bd443c6d10d7cef2101866ed185a1e0" - integrity sha512-wusd1Xpn1wgsSEv7UIa4NNraCwH9syBtubBy4xVNXg3b+CDKM+sFaF3hnMx0tr0et4km9urIDdNvwm34QiZong== - dependencies: - "@reactflow/background" "11.3.13" - "@reactflow/controls" "11.2.13" - "@reactflow/core" "11.11.3" - "@reactflow/minimap" "11.7.13" - "@reactflow/node-resizer" "2.2.13" - "@reactflow/node-toolbar" "1.3.13" +reactflow@^11.11.4: + version "11.11.4" + resolved "https://registry.yarnpkg.com/reactflow/-/reactflow-11.11.4.tgz#e3593e313420542caed81aecbd73fb9bc6576653" + integrity sha512-70FOtJkUWH3BAOsN+LU9lCrKoKbtOPnz2uq0CV2PLdNSwxTXOhCbsZr50GmZ+Rtw3jx8Uv7/vBFtCGixLfd4Og== + dependencies: + "@reactflow/background" "11.3.14" + "@reactflow/controls" "11.2.14" + "@reactflow/core" "11.11.4" + "@reactflow/minimap" "11.7.14" + "@reactflow/node-resizer" "2.2.14" + "@reactflow/node-toolbar" "1.3.14" read-cache@^1.0.0: version "1.0.0"