Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve auto-layout #332

Merged
merged 8 commits into from
Nov 17, 2024
Merged

Improve auto-layout #332

merged 8 commits into from
Nov 17, 2024

Conversation

loicknuchel
Copy link
Contributor

@loicknuchel loicknuchel commented Nov 15, 2024

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced a new CSS class for checkmark icons in lists.
    • Added support for custom list styles in Tailwind CSS.
    • Expanded icon set with a new "bars-arrow-down" icon.
    • Enhanced layout management with new auto-layout methods and diagram structures.
    • Implemented new functions for handling diagram nodes and edges.
    • Added new functionality for handling auto-layout configurations in the application.
  • Bug Fixes

    • Improved error handling and command processing in various modules.
  • Documentation

    • Updated documentation to reflect new features and changes in functionality.
  • Refactor

    • Streamlined command handling processes across multiple modules.
    • Enhanced internal logic for managing table visibility and layout.
  • Style

    • Improved UI elements for better user experience, including dropdowns for layout options.
  • Tests

    • Added tests for new functionalities and ensured existing tests are updated accordingly.

Copy link

coderabbitai bot commented Nov 15, 2024

Walkthrough

The changes in this pull request introduce several enhancements across multiple files, focusing on layout management, new UI components, and data handling improvements. Notable additions include a new CSS class for checkmarks, updates to Tailwind CSS configurations, and the introduction of new functions for icon rendering and data encoding/decoding. Additionally, the application now supports various auto-layout methods for visualizing graph structures, with corresponding updates in both Elm and TypeScript files to facilitate these functionalities.

Changes

File Path Change Summary
backend/assets/css/app.css Added new CSS class .list-image-checkmark for a checkmark icon.
backend/assets/tailwind.config.js Introduced listStyleImage property for custom list styles.
backend/lib/azimutt_web/components/icon.ex Added bars_arrow_down icon rendering function.
backend/lib/azimutt_web/templates/website/index.html.heex Modified layout and content for promotional sections and added a "Personas" section.
frontend/elm.json Removed several dependencies from direct and indirect categories.
frontend/package.json Added new dependencies for Cytoscape and type definitions.
frontend/src/Components/Slices/PlanDialog.elm Added small HTML element for additional information in modal.
frontend/src/Conf.elm Updated manyTablesLimit from 50 to 100 and fewTablesLimit from 10 to 30.
frontend/src/Libs/List.elm Introduced indexBy function for creating dictionaries from lists.
frontend/src/Libs/Models/Area.elm Added encode and decode functions for the Area type.
frontend/src/Libs/Set.elm Renamed parameter in toggle function from list to set.
frontend/src/Models/Area.elm Expanded exported functions and types related to canvas and grid operations.
frontend/src/Models/AutoLayout.elm Introduced new module for auto-layout methods and diagram structures.
frontend/src/Models/Organization.elm Updated canShowTables function to include a new parameter LayoutName.
frontend/src/Models/Project/TableRow.elm Added toString and fromString functions for Id conversions.
frontend/src/PagesComponents/Organization_/Project_/Models.elm Updated Msg type to include ArrangeTables constructor with AutoLayoutMethod.
frontend/src/PagesComponents/Organization_/Project_/Models/Erd.elm Added viewport function for computing viewports based on ErdProps.
frontend/src/PagesComponents/Organization_/Project_/Models/ErdTable.elm Introduced rank function to compute table importance.
frontend/src/PagesComponents/Organization_/Project_/Updates.elm Enhanced update and handleJsMessage functions with new parameters and logic.
frontend/src/PagesComponents/Organization_/Project_/Updates/Canvas.elm Modified layout management functions to focus on areas.
frontend/src/PagesComponents/Organization_/Project_/Updates/Hotkey.elm Updated handleHotkey function to return Extra Msg instead of Cmd Msg.
frontend/src/PagesComponents/Organization_/Project_/Updates/Table.elm Modified showTables and showAllTables functions for layout management.
frontend/src/PagesComponents/Organization_/Project_/Views/Commands.elm Replaced button with dropdown for arranging tables.
frontend/src/PagesComponents/Organization_/Project_/Views/Erd.elm Updated UI components and logic for displaying tables.
frontend/src/PagesComponents/Organization_/Project_/Views/Navbar/Search.elm Updated visibility checks and search result handling using Set.
frontend/ts-src/index.ts Introduced new methods for handling graph layout using Cytoscape.
frontend/ts-src/services/elm.ts Added callback for handling GetAutoLayout messages.
frontend/ts-src/types/basics.ts Defined new types for diagram representation and layout methods.
frontend/ts-src/types/ports.ts Added types and schemas for auto layout messaging.
libs/models/src/common.ts Introduced new Area constant and type definition.

Poem

In the garden of code, new blooms arise,
With checkmarks and layouts, a sweet surprise.
From icons that dance to tables that gleam,
Our app's now a canvas, a vibrant dream!
So hop with delight, let the changes unfold,
For every new feature is a story retold! 🐰✨


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@loicknuchel loicknuchel marked this pull request as ready for review November 17, 2024 20:24
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Outside diff range and nitpick comments (43)
frontend/src/Libs/Set.elm (1)

7-12: Consider adding documentation and simplifying the implementation.

While the implementation is correct, consider these improvements:

  1. Add documentation to describe the function's purpose
  2. Simplify the pipe operator usage

Here's a suggested improvement:

+{-| Toggle an item in a set. If the item exists, it will be removed.
+If it doesn't exist, it will be added.
+-}
 toggle : comparable -> Set comparable -> Set comparable
 toggle item set =
-    if set |> Set.member item then
-        set |> Set.remove item
+    if Set.member item set then
+        Set.remove item set
     else
-        set |> Set.insert item
+        Set.insert item set
frontend/ts-src/types/basics.ts (3)

43-44: Consider adding documentation for layout methods.

While most layout methods are self-explanatory, some like 'avsdf' and 'fcose' might benefit from documentation explaining their purpose, use cases, and behavior. This would help developers choose the appropriate layout method for their needs.

Consider adding JSDoc comments like this:

/**
 * Available auto-layout methods for arranging nodes in a diagram.
 * - random: Randomly positions nodes
 * - grid: Arranges nodes in a grid pattern
 * - circle: Places nodes in a circular layout
 * - avsdf: Average-link Small-Degree-First layout (optimized for small-degree graphs)
 * - breadthfirst: Arranges nodes in a tree-like structure
 * - cose: Compound Spring Embedder layout
 * - dagre: Hierarchical layout for directed graphs
 * - fcose: Fast Compound Spring Embedder layout
 */
export const AutoLayoutMethod = z.enum(['random', 'grid', 'circle', 'avsdf', 'breadthfirst', 'cose', 'dagre', 'fcose'])

45-46: Consider strengthening id validation.

While the current schema validates that id is a string, consider adding more specific validation rules to ensure consistent id formats and prevent potential issues.

Consider this enhancement:

-export const DiagramNode = z.object({id: z.string(), size: Size, position: Position}).strict()
+export const DiagramNode = z.object({
+    id: z.string().min(1).regex(/^[a-zA-Z0-9_-]+$/),
+    size: Size,
+    position: Position
+}).strict()

47-48: Consider enhancing the edge schema for better graph representation.

The current edge schema might be too simplistic for a robust graph visualization. Consider adding support for:

  1. Edge labels or descriptions
  2. Edge styling (e.g., line style, color)
  3. Edge direction validation
  4. Reference integrity validation

Here's a suggested enhancement:

export const DiagramEdge = z.object({
    src: z.string(),
    ref: z.string(),
    label: z.string().optional(),
    style: z.object({
        lineStyle: z.enum(['solid', 'dashed', 'dotted']).optional(),
        color: z.string().optional(),
    }).optional(),
}).refine(
    (data) => data.src !== data.ref,
    "Self-referential edges are not allowed"
).strict()

Additionally, consider implementing a custom validation function to ensure that src and ref point to existing node IDs.

frontend/src/Models/AutoLayout.elm (2)

9-22: Add documentation for layout methods and default choice.

While the type definition is clear, adding documentation would help users understand:

  1. The characteristics of each layout method
  2. When to use which method
  3. Why Cose is the default choice

Add documentation like this:

+{-| Available auto-layout methods for arranging diagram elements.
    - Random: Positions nodes randomly
    - Grid: Arranges nodes in a grid pattern
    - Circle: Places nodes in a circular layout
    - Avsdf: Uses AVSDF (Automatic Variable Step Force Directed) algorithm
    - BreadthFirst: Arranges nodes in a tree-like structure level by level
    - Dagre: Optimized for directed acyclic graphs
    - Cose: Compound Spring Embedder algorithm
    - FCose: Fast Compound Spring Embedder algorithm
-}
 type AutoLayoutMethod
     = Random
     | Grid
     | Circle
     | Avsdf
     | BreadthFirst
     | Dagre
     | Cose
     | FCose

+{-| Default layout method.
    Cose is chosen as default because it provides a good balance between
    performance and aesthetically pleasing results for most graph structures.
-}
 default : AutoLayoutMethod

25-30: Improve type aliases with documentation and clearer field names.

Consider making the following improvements:

  1. Add documentation for both type aliases
  2. Use more descriptive field names in DiagramEdge

Apply these changes:

+{-| Represents a node in the diagram with its unique identifier,
    dimensions, and current position.
-}
 type alias DiagramNode =
     { id : String
     , size : Size
     , position : Position
     }

+{-| Represents an edge connecting two nodes in the diagram.
-}
 type alias DiagramEdge =
-    { src : String
-    , ref : String
+    { sourceId : String
+    , targetId : String
     }
libs/models/src/common.ts (2)

36-37: LGTM! Consider adding documentation.

The new Area type is well-structured and follows the established patterns. Consider adding a JSDoc comment to document its purpose and usage in the auto-layout context.

+/**
+ * Represents a rectangular area in 2D space, used for layout calculations.
+ * @property position - The top-left corner coordinates
+ * @property size - The width and height dimensions
+ */
 export const Area = z.object({position: Position, size: Size}).strict()
 export type Area = z.infer<typeof Area>

36-37: Consider adding validation for negative values.

Since this type is used for layout purposes, you might want to prevent negative values for position and size components.

-export const Area = z.object({position: Position, size: Size}).strict()
+export const Area = z.object({
+  position: z.object({
+    left: Px.nonnegative(),
+    top: Px.nonnegative()
+  }).strict(),
+  size: z.object({
+    width: Px.positive(),
+    height: Px.positive()
+  }).strict()
+}).strict()
frontend/src/Libs/Models/Area.elm (1)

95-99: Consider adding custom error handling for better debugging

The decoder implementation is correct, but consider adding custom error messages for better debugging experience when JSON decoding fails.

Consider enhancing the decoder with custom error messages:

 decode : Decode.Decoder Area
 decode =
-    Decode.map2 Area
-        (Decode.field "position" Position.decode)
-        (Decode.field "size" Size.decode)
+    Decode.map2 Area
+        (Decode.field "position" Position.decode
+            |> Decode.andThen
+                (\pos ->
+                    if isValidPosition pos then
+                        Decode.succeed pos
+                    else
+                        Decode.fail "Invalid position values in Area"
+                )
+        )
+        (Decode.field "size" Size.decode
+            |> Decode.andThen
+                (\size ->
+                    if isValidSize size then
+                        Decode.succeed size
+                    else
+                        Decode.fail "Invalid size values in Area"
+                )
+        )
backend/assets/css/app.css (1)

31-33: Consider improving the checkmark implementation and code organization.

While the fallback implementation works, there are several improvements that could make it more robust:

  1. The CSS rule is placed in an arbitrary location. Consider moving it to a more appropriate section, such as under "/* This file is for your main application CSS */".

  2. The implementation could be more resilient:

-/* Fallback until ../tailwind.config.js `theme.extend.listStyleImage` works */
-
-.list-image-checkmark { list-style-image: url("/images/icons/checkmark.svg"); }
+/* Custom list styles */
+.list-image-checkmark {
+  /* Fallback until ../tailwind.config.js `theme.extend.listStyleImage` works */
+  list-style-image: url("/images/icons/checkmark.svg");
+  /* Fallback if image fails to load */
+  list-style-type: "✓";
+}
+
+/* Support dark mode */
+@media (prefers-color-scheme: dark) {
+  .list-image-checkmark {
+    /* Consider using a light-colored checkmark SVG for dark mode */
+    list-style-image: url("/images/icons/checkmark-light.svg");
+  }
+}
frontend/src/Models/Organization.elm (1)

31-33: Consider separating layout and table count checks.

The function now handles two distinct responsibilities:

  1. Checking if the layout matches the default layout
  2. Validating table counts against plan limits

Consider splitting these into separate functions for better maintainability and single responsibility principle.

Example refactor:

-canShowTables : LayoutName -> Int -> Int -> { x | organization : Maybe Organization } -> Bool
-canShowTables layout layoutTables newTables projectRef =
-    layout == Conf.constants.defaultLayout || (projectRef.organization |> Maybe.mapOrElse (.plan >> .layoutTables >> Maybe.all (\max -> layoutTables + newTables <= max)) (newTables + layoutTables <= Feature.layoutTables.default))
+isDefaultLayout : LayoutName -> Bool
+isDefaultLayout layout =
+    layout == Conf.constants.defaultLayout
+
+isWithinTableLimits : Int -> Int -> { x | organization : Maybe Organization } -> Bool
+isWithinTableLimits layoutTables newTables projectRef =
+    projectRef.organization 
+        |> Maybe.mapOrElse 
+            (.plan >> .layoutTables >> Maybe.all (\max -> layoutTables + newTables <= max))
+            (newTables + layoutTables <= Feature.layoutTables.default)
+
+canShowTables : LayoutName -> Int -> Int -> { x | organization : Maybe Organization } -> Bool
+canShowTables layout layoutTables newTables projectRef =
+    isDefaultLayout layout || isWithinTableLimits layoutTables newTables projectRef
frontend/src/Models/Area.elm (1)

90-92: Consider adding documentation for the calculation

While the implementation is correct, adding a docstring would help explain how the bottom right position is calculated using the position and size delta.

+{-| Calculates the bottom right position of a CanvasLike area by adding the size delta to the current position.
-}
 bottomRightCanvas : CanvasLike a -> Position.Canvas
 bottomRightCanvas area =
     area.position |> Position.moveCanvas (area.size |> Size.deltaCanvas)
frontend/src/PagesComponents/Organization_/Project_/Models/ErdTable.elm (1)

160-163: Enhance documentation and maintainability of the ranking algorithm.

While the implementation is correct, consider these improvements:

  1. The comment should explain the rationale behind the weighting system.
  2. The magic number 10 should be defined as a named constant.

Consider this refactoring:

+-- Weight for incoming relations in the ranking algorithm.
+-- Higher value means incoming relations have more influence on the layout than the number of columns.
+relationWeight : Int
+relationWeight =
+    10
+
+{-| Compute table importance for layout positioning.
+    The rank is based on:
+    - Number of columns (1 point each)
+    - Number of incoming relations (relationWeight points each)
+    
+    Tables with higher ranks will be positioned more prominently in the layout.
+-}
 rank : ErdTable -> Int
 rank table =
-    -- basic computation if "table importance", using number of incoming relations as the main metric, then number of columns
-    table.columns |> Dict.foldl (\_ c r -> r + 1 + (10 * (c.inRelations |> List.length))) 0
+    table.columns
+        |> Dict.foldl
+            (\_ column acc ->
+                acc
+                    + 1 -- Points for the column itself
+                    + (relationWeight * List.length column.inRelations)
+            )
+            0
frontend/src/Models/Project/TableRow.elm (1)

81-89: Add documentation and consider input validation

While the implementation is correct, consider these improvements:

  1. Add documentation comments to describe:
    • Purpose and usage of each function
    • Expected input/output format
    • Relationship with toHtmlId/fromHtmlId
  2. Consider adding validation for negative IDs if they're not valid in your domain.

Example documentation:

{-| Convert a table row ID to its string representation.
-}
toString : Id -> String

{-| Attempt to parse a string into a table row ID.
Returns Nothing if the string is not a valid integer.
-}
fromString : String -> Maybe Id
frontend/src/PagesComponents/Organization_/Project_/Views/Navbar/Search.elm (3)

88-100: Consider extracting magic numbers and improving clarity

The implementation is good, but could be improved:

  1. Extract 15 as a named constant like maxSuggestedTables
  2. Consider using List.sortBy (.rank >> negate) or adding a comment explaining the rank negation
+ topTablesLimit : Int
+ topTablesLimit =
+     15

  topTables : List ErdTable
  topTables =
-     tables |> Dict.values |> List.sortBy (ErdTable.rank >> negate) |> List.filter (\t -> shown |> Set.member t.id |> not) |> List.take 15
+     tables |> Dict.values |> List.sortBy (.rank >> negate) |> List.filter (\t -> shown |> Set.member t.id |> not) |> List.take topTablesLimit

Line range hint 127-161: Consider reducing duplication in relation visibility checks

The implementation could be more DRY by extracting the common visibility check logic for relations:

+ isTableHidden : TableId -> Bool
+ isTableHidden tableId =
+     shown |> Set.member tableId |> not

  case res of
      FoundRelation relation ->
-         if shown |> Set.member relation.src.table |> not then
+         if isTableHidden relation.src.table then
              viewItem "relation" relation.src.table Icons.columns.foreignKey [ text relation.name ] False

-         else if shown |> Set.member relation.ref.table |> not then
+         else if isTableHidden relation.ref.table then
              viewItem "relation" relation.ref.table Icons.columns.foreignKey [ text relation.name ] False

Line range hint 1-1: Consider adding tests for search functionality

The search implementation is comprehensive but would benefit from unit tests to verify:

  • Ranking algorithm for search results
  • Set-based visibility checks
  • Edge cases in fuzzy matching

Would you like me to help create a test suite for this module?

frontend/src/Libs/List.elm (1)

609-612: Add documentation and test coverage

The implementation is correct and efficient, but would benefit from:

  1. Documentation explaining the behavior with duplicate keys (keeps the last value)
  2. Test cases to verify the behavior with:
    • Empty list
    • List with unique keys
    • List with duplicate keys

Would you like me to help generate the documentation and test cases?

frontend/ts-src/types/ports.ts (1)

168-169: Consider enhancing type safety for auto-layout types.

To improve type safety and maintainability, consider:

  1. Creating a shared interface for diagram data:
interface DiagramData {
  nodes: DiagramNode[]
  edges: DiagramEdge[]
}
  1. Using this interface in both types:
type GetAutoLayout = {
  kind: 'GetAutoLayout'
  method: AutoLayoutMethod
  viewport: Area
} & DiagramData

type GotAutoLayout = {
  kind: 'GotAutoLayout'
  nodes: DiagramNode[]  // Consider using DiagramData if edges should be included
}

This would:

  • Reduce code duplication
  • Make it easier to maintain consistency between related types
  • Make it clearer when data structures intentionally differ

Also applies to: 217-218

frontend/src/Components/Slices/PlanDialog.elm (1)

150-151: LGTM! Consider making the exception more prominent.

The addition of the clarification about the initial layout exception is valuable for user understanding. However, since this is an important limitation exception, consider making it more prominent rather than using a small element.

Consider this alternative implementation to make the exception more noticeable:

-[ bText ("Ooops, you just hit a plan limit, you can only add " ++ String.pluralize "table" limit ++ " per layout!"), br [] [], small [] [ text "*except on the initial layout" ] ]
+[ bText ("Ooops, you just hit a plan limit, you can only add " ++ String.pluralize "table" limit ++ " per layout!") ]
+, p [ class "text-sm text-gray-500" ] [ text "Note: This limit does not apply to the initial layout." ]
frontend/src/PagesComponents/Organization_/Project_/Updates/Table.elm (1)

Line range hint 452-452: Consider improving the cross-reference comment

The comment references a specific line number in another file: frontend/src/PagesComponents/Organization_/Project_/Updates.elm:502. Line numbers can become incorrect when files change. Consider referencing the function name instead.

-            -- initial position is computed in frontend/src/PagesComponents/Organization_/Project_/Updates.elm:502#computeInitialPosition when size is known
+            -- initial position is computed by computeInitialPosition in Updates.elm when size is known
frontend/src/PagesComponents/Organization_/Project_/Views/Erd.elm (1)

419-431: Simplify button implementation using pattern matching.

The button implementation could be more concise using pattern matching on the table size.

Consider this refactor:

-div [ class "my-8 flex justify-around items-center" ]
-    [ Button.white4 Tw.indigo
-        [ onClick
-            (if Dict.size tables < Conf.constants.manyTablesLimit then
-                ShowAllTables "welcome"
-             else
-                confirm "Show all tables" 
-                    (text ("You are about to show " ++ (tables |> String.pluralizeD "table") ++ 
-                    ". That's a lot. It may slow down your browser and make Azimutt unresponsive. Continue?")) 
-                    (ShowAllTables "welcome")
-            )
-        ]
-        [ text ("Show all " ++ (tables |> String.pluralizeD "table")) ]
-    , span [] [ text "or" ]
-    , Button.white4 Tw.indigo [ onClick (ShowTables topTables Nothing "welcome") ] 
-        [ text ("Show " ++ (topTables |> String.pluralizeL "most central table")) ]
-    ]
+let
+    showAllAction =
+        case Dict.size tables >= Conf.constants.manyTablesLimit of
+            True -> confirm "Show all tables" 
+                (text ("You are about to show " ++ (tables |> String.pluralizeD "table") ++ 
+                ". That's a lot. It may slow down your browser and make Azimutt unresponsive. Continue?")) 
+                (ShowAllTables "welcome")
+            False -> ShowAllTables "welcome"
+in
+div [ class "my-8 flex justify-around items-center" ]
+    [ Button.white4 Tw.indigo [ onClick showAllAction ] 
+        [ text ("Show all " ++ (tables |> String.pluralizeD "table")) ]
+    , span [] [ text "or" ]
+    , Button.white4 Tw.indigo [ onClick (ShowTables topTables Nothing "welcome") ] 
+        [ text ("Show " ++ (topTables |> String.pluralizeL "most central table")) ]
+    ]
frontend/src/PagesComponents/Organization_/Project_/Views/Commands.elm (2)

18-18: Simplify the module alias for clarity.

Consider simplifying the alias in your import statement. Since the module is Models.AutoLayout, using as AutoLayout might improve readability.

Apply this diff to adjust the alias:

-import Models.AutoLayout as AutoLayoutMethod
+import Models.AutoLayout as AutoLayout

79-87: Ensure accurate and consistent labels for layout options.

Some layout option labels may not accurately represent their corresponding layout methods:

  • Line 83: Labeling AutoLayoutMethod.BreadthFirst as "Breadth layout" might be unclear. Consider "Breadth-First layout".
  • Line 85: AutoLayoutMethod.Cose is labeled "Compound layout". Since "CoSE" stands for "Compound Spring Embedder", consider using "CoSE layout".
  • Line 86: AutoLayoutMethod.FCose is labeled "Force layout". Given that "fCoSE" stands for "Fast Compound Spring Embedder", "fCoSE layout" might be more precise.

Apply this diff to adjust the labels:

, ContextMenu.btn "" (ArrangeTables AutoLayoutMethod.BreadthFirst) [] [ text "Breadth layout" ]
, ContextMenu.btn "" (ArrangeTables AutoLayoutMethod.Dagre) [] [ text "Dagre layout" ]
, ContextMenu.btn "" (ArrangeTables AutoLayoutMethod.Cose) [] [ text "Compound layout" ]
, ContextMenu.btn "" (ArrangeTables AutoLayoutMethod.FCose) [] [ text "Force layout" ]
, ContextMenu.btn "" (ArrangeTables AutoLayoutMethod.BreadthFirst) [] [ text "Breadth-First layout" ]
, ContextMenu.btn "" (ArrangeTables AutoLayoutMethod.Dagre) [] [ text "Dagre layout" ]
, ContextMenu.btn "" (ArrangeTables AutoLayoutMethod.Cose) [] [ text "CoSE layout" ]
, ContextMenu.btn "" (ArrangeTables AutoLayoutMethod.FCose) [] [ text "fCoSE layout" ]
frontend/src/PagesComponents/Organization_/Project_/Updates/Canvas.elm (5)

62-64: Notice the warning about diagram changes due to table header size

The comment warns that the diagram can change due to table header size alterations, referencing headerTextSize in Table.elm. Addressing this could improve layout consistency.

Would you like assistance in resolving this issue or creating a GitHub issue to track it?


89-89: Address the TODO: Implement auto-layout toggle on showing all tables

There's a TODO comment indicating that when the layout is empty, the auto-layout should be toggled upon showing all tables. Implementing this would enhance user experience by automatically arranging tables when they are first displayed.

Would you like assistance in implementing this feature or creating a GitHub issue to track it?


104-106: Refactor duplicated code when creating DiagramNode instances

The code for creating DiagramNode instances for tables, rows, and memos is similar and can be refactored to reduce redundancy.

Consider introducing a helper function:

createDiagramNode : String -> String -> Size -> Position -> DiagramNode
createDiagramNode prefix id size position =
    { id = prefix ++ "/" ++ id
    , size = size |> Size.extractCanvas
    , position = position |> Position.extractGrid
    }

Update the nodes definition:

 nodes =
-    (tables |> List.map (\t -> { id = "table/" ++ TableId.toString t.id, size = t.props.size |> Size.extractCanvas, position = t.props.position |> Position.extractGrid }))
-    ++ (rows |> List.map (\r -> { id = "row/" ++ TableRow.toString r.id, size = r.size |> Size.extractCanvas, position = r.position |> Position.extractGrid }))
-    ++ (memos |> List.map (\m -> { id = "memo/" ++ MemoId.toString m.id, size = m.size |> Size.extractCanvas, position = m.position |> Position.extractGrid }))
+    (tables |> List.map (\t -> createDiagramNode "table" (TableId.toString t.id) t.props.size t.props.position))
+    ++ (rows |> List.map (\r -> createDiagramNode "row" (TableRow.toString r.id) r.size r.position))
+    ++ (memos |> List.map (\m -> createDiagramNode "memo" (MemoId.toString m.id) m.size m.position))

152-156: Avoid complex string manipulations for ID parsing

The code uses string splitting and joining to parse IDs from DiagramNode.id. This approach can be error-prone and inefficient. Consider modifying DiagramNode to include separate fields for kind and id, which would eliminate the need for string parsing.

Possible changes:

  • Update DiagramNode to have kind : String and id : String fields.
  • Adjust the creation of DiagramNode instances accordingly.
  • Modify nodesByKind to group by kind without string manipulation.

184-203: Clarify the logic in objectsToFit function

The function determines whether to fit selected objects or all objects based on their selection state. The conditions might benefit from additional comments or restructuring for clarity.

Consider simplifying the conditionals or adding comments to explain the logic paths.

frontend/src/PagesComponents/Organization_/Project_/Updates/Hotkey.elm (3)

Line range hint 248-285: Address the FIXME in cancelElement to improve cancel action management

The cancelElement function contains a FIXME comment indicating the need to maintain a list of cancel actions to manage cancellations properly. This suggests potential issues with the current cancelation logic, possibly leading to inconsistent user experiences.

I can help refactor this function to implement a stack or queue of cancel actions, ensuring they are canceled in order and removed when no longer applicable. Would you like assistance with this, or should I open a new GitHub issue to track this enhancement?


30-30: Unused import might be unnecessary

The import statement:

import PagesComponents.Organization_.Project_.Updates.Extra as Extra exposing (Extra)

Ensure that all functions or types from Extra are utilized in the code. If not all exposed entities are necessary, consider adjusting the import to expose only what is needed to improve code clarity.


82-82: Provide guidance when saving is disabled

In the "save" hotkey handler, when saving is disabled (model.conf.save is False), the user receives a warning: "Can't save in read-only mode". Consider offering guidance on how the user can enable saving or why saving is disabled to enhance user experience.

frontend/ts-src/index.ts (2)

491-491: Incorrect comment URL for 'circle' layout method.

The comment for the 'circle' layout references the 'grid' layout documentation. Please update the URL to point to the correct 'circle' layout documentation.

Apply this diff to correct the comment:

- else if (msg.method === 'circle') { runLayout(msg, {name: 'circle', spacingFactor: 0.3}) } // https://js.cytoscape.org/#layouts/grid
+ else if (msg.method === 'circle') { runLayout(msg, {name: 'circle', spacingFactor: 0.3}) } // https://js.cytoscape.org/#layouts/circle

483-498: Refactor 'getAutoLayout' function to improve maintainability.

The current implementation uses a long chain of if-else statements to handle different layout methods. This can become difficult to maintain as more layout methods are added. Consider refactoring using a mapping of method names to layout configurations.

You can apply this refactor:

 function getAutoLayout(msg: GetAutoLayout): void {
+    const width = msg.viewport.size.width || 1600;
+    const height = msg.viewport.size.height || 900; // default values on 0x0, avoid NaN on ratio :/
+    const pos = msg.viewport.position;
+    const count = msg.nodes.length;
+
+    const layouts: { [key: string]: LayoutOptions } = {
+        'random': { name: 'random', boundingBox: { x1: pos.left, y1: pos.top, w: count * 100, h: count * 100 * (height / width) } },
+        'grid': { name: 'grid', condense: true, boundingBox: { x1: pos.left, y1: pos.top, w: width, h: height } },
+        'circle': { name: 'circle', spacingFactor: 0.3 },
+        'avsdf': { name: 'avsdf', nodeSeparation: 225 } as LayoutOptions,
+        'breadthfirst': { name: 'breadthfirst', circle: true, spacingFactor: 1 },
+        'dagre': { name: 'dagre' },
+        'cose': { name: 'cose', boundingBox: { x1: 0, y1: 0, w: count * 100, h: count * 100 * (height / width) } },
+        'fcose': { name: 'fcose', idealEdgeLength: () => 300, fixedNodeConstraint: [] } as LayoutOptions
+    };
+
+    const layout = layouts[msg.method];
+    if (layout) {
+        runLayout(msg, layout);
+    } else {
+        app.toast(ToastLevel.enum.error, `Unknown auto-layout method '${msg.method}', please report it for a fix.`);
+    }
 }
backend/lib/azimutt_web/templates/website/index.html.heex (7)

30-32: Consider replacing '&' with 'and' for better readability

Using 'and' instead of '&' can improve readability and accessibility, making the text clearer for all users.

Apply this diff:

-Seamless exploration for<br>
-large & complex <span class="text-indigo-600">databases</span>
+Seamless exploration for<br>
+large and complex <span class="text-indigo-600">databases</span>

Line range hint 126-165: Consider removing commented-out code to keep the codebase clean

Instead of commenting out unused sections, consider removing them to prevent clutter and maintain code readability.


173-175: Rephrase for clearer communication

The phrase "to stay performant" might be unclear. Consider rephrasing it to "to maintain efficiency" or "to ensure optimal performance" for better clarity.

Apply this diff:

-Either with a large monolith or many microservices, once your system grows, you need visualization tools to stay performant.
+Either with a large monolith or many microservices, once your system grows, you need visualization tools to maintain efficiency.

188-193: Use consistent and professional terminology

Consider replacing "dev" with "developer" for clarity. Also, rephrase "building incrementally your layouts" to "incrementally building your layouts" for improved readability.

Apply this diff:

-<li>dev joining a new team/company</li>
+<li>developer joining a new team/company</li>
...
-Azimutt makes database exploration easy by building incrementally your layouts with search everywhere, follow relations, find path and more...</p>
+Azimutt makes database exploration easy by incrementally building your layouts with features like search everywhere, follow relations, find paths, and more...</p>

206-209: Improve clarity in list items

Consider rephrasing for better understanding:

  • "engineering efficiency teams improving onboarding" to "engineering teams aiming to improve onboarding processes"

Apply this diff:

-<li>engineering efficiency teams improving onboarding</li>
+<li>engineering teams aiming to improve onboarding processes</li>

258-265: Reconsider the inclusion of the "Why NOT use Azimutt" section

Highlighting reasons not to use the product might detract from the overall positive messaging. Consider addressing potential concerns in a more proactive way, such as an FAQ or a comparison with other tools.


271-276: Consider featuring testimonials from external users for greater impact

Including testimonials from external users or customers can enhance credibility and resonate more with potential users.

frontend/src/PagesComponents/Organization_/Project_/Updates.elm (2)

950-968: Consider removing commented-out code to improve code cleanliness

Leaving large blocks of commented-out code (lines 950-965) can clutter the codebase and reduce readability. If this code is no longer needed, consider removing it entirely to keep the code clean and maintainable.


Line range hint 90-544: Consider refactoring the update function for better maintainability

The update function spans over 450 lines and handles numerous message cases. Refactoring it into smaller, dedicated functions or modules can improve readability, make the codebase easier to navigate, and facilitate testing and future enhancements.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between aeb2c97 and 391d3bc.

⛔ Files ignored due to path filters (2)
  • backend/priv/static/images/icons/checkmark.svg is excluded by !**/*.svg
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (31)
  • backend/assets/css/app.css (1 hunks)
  • backend/assets/tailwind.config.js (1 hunks)
  • backend/lib/azimutt_web/components/icon.ex (2 hunks)
  • backend/lib/azimutt_web/templates/website/index.html.heex (6 hunks)
  • frontend/elm.json (0 hunks)
  • frontend/package.json (1 hunks)
  • frontend/src/Components/Slices/PlanDialog.elm (2 hunks)
  • frontend/src/Conf.elm (1 hunks)
  • frontend/src/Libs/List.elm (2 hunks)
  • frontend/src/Libs/Models/Area.elm (2 hunks)
  • frontend/src/Libs/Set.elm (1 hunks)
  • frontend/src/Models/Area.elm (4 hunks)
  • frontend/src/Models/AutoLayout.elm (1 hunks)
  • frontend/src/Models/Organization.elm (3 hunks)
  • frontend/src/Models/Project/TableRow.elm (2 hunks)
  • frontend/src/PagesComponents/Organization_/Project_/Models.elm (2 hunks)
  • frontend/src/PagesComponents/Organization_/Project_/Models/Erd.elm (2 hunks)
  • frontend/src/PagesComponents/Organization_/Project_/Models/ErdTable.elm (2 hunks)
  • frontend/src/PagesComponents/Organization_/Project_/Updates.elm (14 hunks)
  • frontend/src/PagesComponents/Organization_/Project_/Updates/Canvas.elm (3 hunks)
  • frontend/src/PagesComponents/Organization_/Project_/Updates/Hotkey.elm (7 hunks)
  • frontend/src/PagesComponents/Organization_/Project_/Updates/Table.elm (4 hunks)
  • frontend/src/PagesComponents/Organization_/Project_/Views/Commands.elm (2 hunks)
  • frontend/src/PagesComponents/Organization_/Project_/Views/Erd.elm (5 hunks)
  • frontend/src/PagesComponents/Organization_/Project_/Views/Navbar/Search.elm (5 hunks)
  • frontend/src/Ports.elm (8 hunks)
  • frontend/ts-src/index.ts (5 hunks)
  • frontend/ts-src/services/elm.ts (3 hunks)
  • frontend/ts-src/types/basics.ts (2 hunks)
  • frontend/ts-src/types/ports.ts (6 hunks)
  • libs/models/src/common.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • frontend/elm.json
🔇 Additional comments (58)
frontend/src/Libs/Set.elm (1)

7-7: Good rename from list to set!

The parameter rename better reflects the actual data structure being manipulated, improving code clarity.

frontend/src/Models/AutoLayout.elm (2)

1-7: LGTM! Well-structured module declaration and imports.

The module follows Elm conventions with clear exports and specific imports.


1-83: Verify integration with dependent modules.

The AI summary mentions integration with several modules. Let's verify these dependencies are properly handled.

✅ Verification successful

Based on the shell script results, I can now verify the integration of the AutoLayout module. The results show proper integration across multiple files:

  1. Ports.elm properly imports and uses the module for port definitions, with getAutoLayout function properly defined
  2. Multiple components import and use the AutoLayout functionality:
    • PagesComponents/Organization_/Project_/Models.elm
    • PagesComponents/Organization_/Project_/Views/Commands.elm
    • PagesComponents/Organization_/Project_/Updates/Canvas.elm
  3. The implementation shows proper usage with launchAutoLayout and applyAutoLayout functions in the Canvas updates
  4. The auto-layout is properly integrated into the main update cycle in Updates.elm

Let me verify one more aspect to ensure complete coverage:


Auto-layout module is properly integrated and used

The verification confirms proper integration:

  • All encoders/decoders are correctly used in the ports system
  • Type definitions are properly exposed and used across the application
  • The module is well-integrated into the application's update cycle with proper message handling
  • Port communication for layout operations is correctly implemented
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for proper integration with dependent modules

# Check for imports of AutoLayoutMethod
rg --type elm "import.*Models\.AutoLayout.*AutoLayoutMethod" -A 5

# Check for usage of auto-layout related functions
rg --type elm "launchAutoLayout|applyAutoLayout|getAutoLayout" -A 5

# Check for proper port definitions
rg --type elm "^port.*[Ll]ayout.*:" -A 5

Length of output: 11318


Script:

#!/bin/bash
# Check for proper decoder/encoder usage
rg --type elm "encodeAutoLayoutMethod|decodeDiagramNode|encodeDiagramNode|encodeDiagramEdge" -A 2

# Check for proper type usage
rg --type elm "type.*AutoLayoutMethod|type.*DiagramNode|type.*DiagramEdge" -A 5

Length of output: 3387

frontend/src/Libs/Models/Area.elm (2)

5-7: LGTM: Import statements are well-structured

The new JSON-related imports are appropriately added and follow Elm conventions.


87-92: LGTM: Encoding implementation is correct

The encode function properly handles both position and size fields of the Area type.

Let's verify the existence of the dependent encode functions:

✅ Verification successful

Encode function dependencies verified successfully

Both required encode functions exist with correct type signatures:

  • Position.encode : Position -> Value in frontend/src/Libs/Models/Position.elm
  • Size.encode : Size -> Value in frontend/src/Libs/Models/Size.elm
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify that Position.encode and Size.encode exist in their respective modules
# Expected: Find encode function definitions in both modules

rg -U "encode.*->.*Value" frontend/src/Libs/Models/Position.elm
rg -U "encode.*->.*Value" frontend/src/Libs/Models/Size.elm

Length of output: 177

frontend/package.json (2)

73-76: LGTM! Type definitions are correctly matched.

The TypeScript type definitions are properly aligned with their corresponding package versions.


62-65: Consider updating to latest stable versions.

The Cytoscape dependencies look good, but some packages have newer versions available:

  • cytoscape: 3.31.1 available (current: 3.30.3)
  • cytoscape-fcose: 2.2.1 available (current: 2.2.0)

Let's verify if the current versions have any known vulnerabilities:

✅ Verification successful

Let me verify the package-lock.json file existence and check the actual usage of these dependencies in the codebase.


Consider updating to latest stable versions

The GitHub security advisory check shows no known vulnerabilities for any of the Cytoscape packages at their current versions. While newer versions (cytoscape@3.31.1 and cytoscape-fcose@2.2.1) are available, the current versions are stable and secure. The caret (^) in version numbers will allow minor updates during dependency installation.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for security advisories for Cytoscape and its plugins

# Check for security advisories in npm
for pkg in "cytoscape" "cytoscape-avsdf" "cytoscape-dagre" "cytoscape-fcose"; do
  echo "Checking $pkg..."
  npm audit --json "$pkg"
done

# Check GitHub security advisories
for pkg in "cytoscape" "cytoscape-avsdf" "cytoscape-dagre" "cytoscape-fcose"; do
  echo "Checking GitHub advisories for $pkg..."
  gh api graphql -f query='{
    securityVulnerabilities(first: 5, ecosystem: NPM, package: "'$pkg'") {
      nodes {
        advisory {
          summary
          severity
          publishedAt
        }
        vulnerableVersionRange
        firstPatchedVersion {
          identifier
        }
      }
    }
  }'
done

Length of output: 5288


Script:

#!/bin/bash
# Check for package-lock.json and yarn.lock
fd -t f "package-lock.json|yarn.lock" frontend/

# Check how these dependencies are imported and used
rg -l "cytoscape" frontend/src/
rg "import.*cytoscape" frontend/src/ -A 2

Length of output: 125

backend/assets/css/app.css (1)

31-33: Verify the SVG asset path.

The hardcoded path to the checkmark SVG might break if the assets structure changes.

✅ Verification successful

Let me gather more specific information about the checkmark.svg file and its location.


The SVG asset path is correctly configured

The path /images/icons/checkmark.svg is valid as:

  • The file exists at backend/priv/static/images/icons/checkmark.svg
  • The static assets are served from priv/static, making /images/icons/checkmark.svg the correct public URL
  • No other CSS files reference this asset, indicating consistent usage
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the existence and location of the checkmark SVG file

# Test: Check if the SVG file exists in the expected location
fd --type f "checkmark.svg" --exec file {} \;

# Test: Look for any SVG files to confirm the correct path
fd --type f --extension svg --exec dirname {} \; | sort -u

Length of output: 309


Script:

#!/bin/bash
# Check for the exact location of checkmark.svg
fd "checkmark.svg"

# Check the contents of the icons directory
fd . "backend/priv/static/images/icons" --type f

# Check if there are any references to checkmark.svg in other files
rg "checkmark\.svg" --type css

Length of output: 301

backend/assets/tailwind.config.js (2)

147-149: Verify the checkmark SVG asset and consider accessibility.

While the implementation is correct, there are a few considerations:

  1. The SVG path needs verification
  2. Consider adding appropriate ARIA attributes and alt text for screen readers when using this style

Let's verify the SVG file existence and location:

#!/bin/bash
# Check if the SVG file exists in the expected location
fd -t f "checkmark.svg" -E "node_modules"

# Look for any usage of this list style in the codebase
rg -t html "list-image-checkmark"
rg -t css "list-image-checkmark"

147-149: Clarify relationship with auto-layout improvements.

The addition of a checkmark list style appears unrelated to the PR's objective of improving auto-layout. Could you clarify how this change contributes to the layout improvements?

Let's check for related layout changes:

frontend/src/Models/Organization.elm (2)

16-16: LGTM: Import statement is correctly added.

The import of LayoutName type is necessary for the updated canShowTables function signature.


31-33: Verify the default layout bypass behavior.

The function allows bypassing table count limits when using the default layout. This might have implications on the application's behavior and resource usage.

Let's verify the usage of this bypass:

✅ Verification successful

Default layout bypass is a valid feature with proper limits

The bypass behavior is intentionally designed with the following safeguards:

  • The default layout named "initial layout" is a special case that allows showing tables without counting against plan limits
  • Non-default layouts are properly restricted by plan limits (default: 10 tables)
  • The code properly enforces these limits by showing a plan limit modal when exceeded
  • This implementation aligns with the plan structure defined in both frontend and backend
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check where and how the default layout bypass is used

# Find where canShowTables is called
rg -l "canShowTables" 

# Find the definition of defaultLayout constant
rg "defaultLayout.*=" 

# Look for any comments or documentation about this behavior
rg -i "default.*layout.*bypass|bypass.*default.*layout"

Length of output: 532


Script:

#!/bin/bash
# Let's check the usage context in Updates.elm and understand how this function is used
rg -A 5 -B 5 "canShowTables" frontend/src/PagesComponents/Organization_/Project_/Updates.elm

# Check Feature.layoutTables.default definition
rg "layoutTables.*default" frontend/src/Feature.elm

# Check the Organization type definition to understand the plan structure
ast-grep --pattern 'type alias Organization = { $$$ }'

Length of output: 2499


Script:

#!/bin/bash
# Let's check the Feature module location and content
fd -g "*Feature*.elm"

# Let's understand the plan structure and limits
rg -A 5 "type.*Plan.*=" 

# Check if there's any documentation about layout limits
rg -i "layout.*limit|table.*limit" README.md docs/

Length of output: 1224


Script:

#!/bin/bash
# Check the Feature module content for layout tables default
rg -A 3 "layoutTables" frontend/src/Models/Feature.elm

# Check Plan.elm for layout tables related fields
rg -A 3 "layoutTables" frontend/src/Models/Plan.elm

# Look for any comments about default layout in Conf.elm
rg -A 3 -B 3 "defaultLayout" frontend/src/Conf.elm

Length of output: 1940

frontend/src/Models/Area.elm (6)

1-1: LGTM: Module exports and imports are well-organized

The module header correctly exposes all new functions, and imports are properly organized with the necessary types.

Also applies to: 5-5


65-67: LGTM: Clean implementation of grid conversion

The function provides a clear and type-safe way to convert an AreaLike to a Grid, following Elm's conventions.


70-72: LGTM: Well-structured canvas conversion

The function maintains consistency with the module's style and provides a clear conversion path from AreaLike to Canvas.


75-77: LGTM: Clean extraction implementation

The function provides a clear inverse operation for canvas conversion, maintaining symmetry with other extraction functions in the module.


124-129: LGTM: Consistent merge implementation

The mergeGrid function follows the established pattern of mergeCanvas, providing a clean way to combine multiple grid areas.


132-134: LGTM: Clean position check implementation

The insideCanvas function provides a clear and type-safe way to check if a position falls within a canvas area.

frontend/src/PagesComponents/Organization_/Project_/Models/ErdTable.elm (1)

1-1: LGTM: Export list modification is well-structured.

The rank function is properly exposed and follows Elm's naming conventions.

frontend/src/Conf.elm (1)

60-61: Significant increase in table limits - verify performance impact.

The substantial increases in both limits:

  • manyTablesLimit: 50 → 100 (2x increase)
  • fewTablesLimit: 10 → 30 (3x increase)

While this change allows for handling larger schemas, please ensure that:

  1. The auto-layout performance remains acceptable with larger table counts
  2. UI remains responsive when approaching these new limits
  3. Memory usage stays within reasonable bounds

Let's verify the usage of these constants and potential performance implications:

✅ Verification successful

Table limits are used appropriately with performance safeguards - no concerns

The code review reveals that these limits are used with proper performance safeguards:

  1. When showing all tables above manyTablesLimit (100), the app shows a warning confirmation:

    • "You are about to show X tables. That's a lot. It may slow down your browser and make Azimutt unresponsive. Continue?"
    • This appears in both search and welcome views
  2. For initial table display (when fewTablesLimit = 30):

    • Shows all tables if count is below the limit
    • Otherwise, shows only top-ranked tables up to the limit
    • This prevents overwhelming the view on first load

The increased limits are well-protected by these existing safeguards, making the changes safe to implement.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Search for usage of these constants to understand their impact on layout and performance.

# Find direct references to these constants
echo "=== Direct constant usage ==="
rg -l "manyTablesLimit|fewTablesLimit" 

# Look for performance-related code in layout handling
echo -e "\n=== Layout performance handling ==="
rg -l "performance|layout.*tables|tables.*layout"

# Search for any existing performance tests
echo -e "\n=== Performance tests ==="
rg -l "performance.*test|test.*performance" 

Length of output: 4051


Script:

#!/bin/bash
# Let's examine the actual usage of these constants in the identified files

echo "=== Usage in Updates.elm ==="
rg -A 5 -B 5 "manyTablesLimit|fewTablesLimit" "frontend/src/PagesComponents/Organization_/Project_/Updates.elm"

echo -e "\n=== Usage in Views/Erd.elm ==="
rg -A 5 -B 5 "manyTablesLimit|fewTablesLimit" "frontend/src/PagesComponents/Organization_/Project_/Views/Erd.elm"

echo -e "\n=== Usage in Search.elm ==="
rg -A 5 -B 5 "manyTablesLimit|fewTablesLimit" "frontend/src/PagesComponents/Organization_/Project_/Views/Navbar/Search.elm"

echo -e "\n=== Layout performance handling in Erd.elm ==="
rg -A 5 -B 5 "performance|layout.*tables|tables.*layout" "frontend/src/PagesComponents/Organization_/Project_/Views/Erd.elm"

echo -e "\n=== Layout handling in ErdLayout.elm ==="
rg -A 5 -B 5 "layout.*tables|tables.*layout" "frontend/src/PagesComponents/Organization_/Project_/Models/ErdLayout.elm"

Length of output: 9076

frontend/ts-src/services/elm.ts (3)

24-24: LGTM: Import statement is properly structured

The addition of DiagramNode import aligns with the auto-layout functionality requirements.


61-61: LGTM: Callback registration follows established patterns

The GetAutoLayout callback is properly integrated into the existing callback system.


122-122: LGTM: Method implementation is consistent with class patterns

The implementation follows the established patterns, but we should verify the corresponding message handler in the Elm code.

✅ Verification successful

Implementation verified: Both TypeScript and Elm sides handle GotAutoLayout correctly

The implementation is properly handled on both sides:

  • TypeScript sends GotAutoLayout with DiagramNode[]
  • Elm has proper message type definition in Ports.elm
  • DiagramNode type in Models/AutoLayout.elm matches the expected structure
  • Message handler is implemented in Updates.elm using applyAutoLayout
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the Elm message handler for GotAutoLayout

# Search for the GotAutoLayout message type and handler in Elm files
rg -t elm "type Msg.*= GotAutoLayout" ./frontend
rg -t elm "GotAutoLayout.*->.*Msg" ./frontend

Length of output: 98


Script:

#!/bin/bash
# Let's try a broader search for GotAutoLayout in Elm files
rg -t elm "GotAutoLayout" ./frontend

# Also search for type definitions containing DiagramNode to find relevant Elm modules
rg -t elm "type.*DiagramNode" ./frontend

# Search for files that might handle diagram-related messages
rg -t elm "type Msg.*= .*Diagram" ./frontend

Length of output: 655


Script:

#!/bin/bash
# Let's check the implementation of the GotAutoLayout handler in Updates.elm
rg -A 5 "GotAutoLayout nodes ->" ./frontend/src/PagesComponents/Organization_/Project_/Updates.elm

# Also check the DiagramNode type definition
cat ./frontend/src/Models/AutoLayout.elm

Length of output: 2281

frontend/src/Models/Project/TableRow.elm (2)

1-1: LGTM: Export list properly updated

The module exports have been correctly updated to include the new toString and fromString functions.


81-89: Verify consistent usage patterns

The new toString/fromString functions complement existing toHtmlId/fromHtmlId. Let's verify their usage to ensure consistency.

✅ Verification successful

No issues with the new conversion functions

The code analysis shows that:

  • The new toString/fromString functions are used for internal ID serialization
  • The existing toHtmlId/fromHtmlId functions are used specifically for DOM element IDs
  • Both sets of functions are used consistently throughout the codebase:
    • toHtmlId is used for DOM-related operations (observing sizes, selecting elements)
    • toString is used for data serialization and internal ID handling

The functions serve different purposes and are used appropriately in their respective contexts without any confusion or mixing of concerns.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for potential confusion between generic and HTML ID conversion functions

# Look for places where both conversion types are used
rg -l "toString|fromString|toHtmlId|fromHtmlId" --type elm

# Check for potential mixed usage
ast-grep --pattern 'toString $id' --lang elm
ast-grep --pattern 'toHtmlId $id' --lang elm

Length of output: 6535


Script:

#!/bin/bash
# Let's check the actual usage patterns in the codebase
rg "toString|fromString|toHtmlId|fromHtmlId" frontend/src/Models/Project/TableRow.elm -B 2 -A 2

# Check if these functions are used in tests
rg "toString|fromString|toHtmlId|fromHtmlId" frontend/tests/Models/Project/TableIdTest.elm -B 2 -A 2

# Look for actual usage in the application
rg "TableRow\.(toString|fromString|toHtmlId|fromHtmlId)" -B 2 -A 2

Length of output: 11104

frontend/src/PagesComponents/Organization_/Project_/Views/Navbar/Search.elm (2)

27-27: LGTM: Clean import additions

The new imports for ErdTable and Set are properly organized and align with the implementation changes.

Also applies to: 29-29


35-39: LGTM: Efficient Set implementation

Converting shownTables to a Set improves performance for membership checks from O(n) to O(log n). Good use of function composition for the conversion.

frontend/src/PagesComponents/Organization_/Project_/Models/Erd.elm (2)

220-222: LGTM! Clean implementation of the viewport function.

The function has a clear single responsibility and follows good functional programming practices by composing existing functions.


224-227: LGTM! Good refactoring of viewportM.

The function now properly delegates to viewport for the core logic while handling the Maybe case. This improves code organization and maintainability.

frontend/src/PagesComponents/Organization_/Project_/Models.elm (2)

20-20: LGTM! Clean import statement.

The explicit type exposure of AutoLayoutMethod follows good Elm practices.


284-284: Verify all ArrangeTables message construction sites.

The change from ArrangeTables to ArrangeTables AutoLayoutMethod is a breaking change that requires updates to all code constructing this message.

✅ Verification successful

All ArrangeTables message construction sites have been properly updated

All occurrences of ArrangeTables in the codebase are correctly using the AutoLayoutMethod parameter:

  • The type definition in Models.elm declares it with AutoLayoutMethod
  • The message handler in Updates.elm pattern matches with a method parameter
  • All construction sites in Views/Commands.elm pass specific AutoLayoutMethod values (Random, Grid, Circle, etc.)
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Find all construction sites of ArrangeTables message to ensure they've been updated
# to pass the required AutoLayoutMethod parameter

# Search for ArrangeTables usage in Elm files
rg "ArrangeTables" --type elm

Length of output: 1726

frontend/src/Libs/List.elm (1)

22-22: LGTM: Clean module interface update

The indexBy function is properly exposed and follows the module's alphabetical ordering convention.

frontend/ts-src/types/ports.ts (3)

2-2: LGTM: Import statements are well-organized.

The new imports for auto-layout types are properly placed and organized within the existing import structure.

Also applies to: 35-37


184-185: LGTM: Union type updates are consistent.

The new auto-layout types are properly integrated into both ElmMsg and JsMsg union types, maintaining the established pattern and alphabetical ordering.

Also applies to: 253-254


168-169: LGTM with a verification request.

The new type definitions are well-structured and follow the established pattern. However, please verify the asymmetry between GetAutoLayout and GotAutoLayout:

  • GetAutoLayout includes both nodes and edges
  • GotAutoLayout only includes nodes

Is this intentional? If the edges remain unchanged after auto-layout, it might be worth adding a comment to explain this design decision.

Let's check if there's any documentation or related code that explains this design:

Also applies to: 217-218

frontend/src/PagesComponents/Organization_/Project_/Updates/Table.elm (3)

117-117: LGTM: Good use of auto-layout initialization

The addition of auto-layout initialization when starting with an empty tables list is a good improvement. The code uses the B.cond helper for better readability.


143-148: LGTM: Well-structured code with improved readability

The changes to showAllTables are well-implemented:

  1. Good variable naming improves code clarity
  2. Consistent auto-layout initialization with showTables
  3. Clean state management with proper type handling

15-15: Verify usage of newly imported modules

The newly added imports AutoLayout and launchAutoLayout appear to be unused in this file. This might indicate:

  1. Incomplete implementation of auto-layout features
  2. Leftover imports from refactoring

Also applies to: 39-39

✅ Verification successful

The search results show that both AutoLayout and launchAutoLayout are actively used in the codebase:

  1. AutoLayout is used in Updates.elm with AutoLayout.default
  2. launchAutoLayout is defined in Canvas.elm and used in multiple places including Updates.elm

The imports in Table.elm are justified as this module is part of the auto-layout feature implementation, working together with other modules in the codebase. Let me verify the specific usage in Table.elm to ensure completeness:


Let me check the content of the Table.elm file to see if these imports are used in the implementation:


After reviewing the Table.elm file, I can see that while Models.AutoLayout is imported, it's not directly used in this file. However, launchAutoLayout is indirectly used through the layoutOnLoad = "arrange" setting in both showTables and showAllTables functions when tables list is empty. This is confirmed by the TODO comment in Canvas.elm that references this behavior:

-- TODO: toggle this on show all tables if layout was empty before, see frontend/src/PagesComponents/Organization_/Project_/Updates/Table.elm:128#showAllTables

The imports are part of a larger auto-layout feature implementation where:

  1. Table.elm sets layoutOnLoad = "arrange" when showing tables in an empty layout
  2. This triggers launchAutoLayout in other modules (as seen in Updates.elm)
  3. The AutoLayout module provides the configuration (seen used as AutoLayout.default)

The imports are correctly used and necessary for the auto-layout feature implementation

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if the imported modules are used elsewhere in the codebase

# Check for AutoLayout usage
rg "AutoLayout\." -A 3

# Check for launchAutoLayout usage
rg "launchAutoLayout" -A 3

Length of output: 4008


Script:

#!/bin/bash
# Check for usage of AutoLayout and launchAutoLayout specifically in Table.elm
rg -g "Table.elm" "AutoLayout\.|launchAutoLayout" -A 3

Length of output: 712


Script:

#!/bin/bash
# Get the content of Table.elm to verify the usage
cat frontend/src/PagesComponents/Organization_/Project_/Updates/Table.elm

Length of output: 25617

frontend/src/Ports.elm (2)

189-191: LGTM: Auto-layout implementation is well-structured

The implementation of the auto-layout functionality follows good practices:

  • Clear function signature with appropriate parameters
  • Consistent message handling pattern
  • Proper encoding of all required data

Also applies to: 305-305, 463-464


550-551: Consider handling viewport updates in the auto-layout response

The decoder only processes the nodes from the auto-layout response, while the request sends additional data including viewport and edges. Consider whether the response should also include viewport updates to ensure the newly laid out nodes are properly visible.

-                "GotAutoLayout" ->
-                    Decode.map GotAutoLayout (Decode.field "nodes" (Decode.list decodeDiagramNode))
+                "GotAutoLayout" ->
+                    Decode.map2 (\nodes viewport -> GotAutoLayout nodes (Maybe.withDefault viewport))
+                        (Decode.field "nodes" (Decode.list decodeDiagramNode))
+                        (Decode.maybeField "viewport" Area.decode)
frontend/src/PagesComponents/Organization_/Project_/Views/Erd.elm (4)

3-3: LGTM! Import changes align with UI updates.

The replacement of Tooltip with Alert and addition of Button imports correspond to the UI component changes in the viewEmptyState function.

Also applies to: 5-5


391-393: LGTM! Improved table selection logic.

The new approach of ranking tables and selecting the top 30 is more maintainable and provides better results than using naming conventions.


422-427: LGTM! Good UX for large datasets.

The confirmation dialog for large datasets is a great addition that prevents potential performance issues. The message clearly explains the implications to users.


433-433: LGTM! Improved user guidance.

The Alert component provides better visibility for the important information about column icons and relations.

backend/lib/azimutt_web/components/icon.ex (3)

431-451: Implementation looks good!

The new bars_arrow_down icon follows the established pattern with proper outline, solid, and mini variants. The SVG paths are well-formatted and the implementation is consistent with other icons in the module.


27-27: Proper placement in icon list!

The icon is correctly placed in alphabetical order within the case statement, maintaining the module's organization.


431-451: Code quality and consistency maintained!

The implementation maintains high code quality with proper:

  • Consistent indentation and formatting
  • Reuse of class handling pattern
  • Consistent SVG attributes
frontend/src/PagesComponents/Organization_/Project_/Updates/Canvas.elm (5)

1-1: Updated module exports to include new auto-layout functions

The module now exports applyAutoLayout and launchAutoLayout, aligning with the new auto-layout features added to the application.


5-31: Added necessary imports for auto-layout functionality

The new imports bring in required modules and types such as Models.AutoLayout, ErdColumnProps, ErdColumnRef, ErdLayout, Memo, and Ports. These imports are essential for supporting the auto-layout features and appear to be correctly referenced.


55-55: Correctly mapping objects to their off-grid areas

The code efficiently maps over tables, rows, and memos to obtain their off-grid areas, preparing them for layout computations.


167-169: Ensure safe handling of positions in dictionaries

The use of Maybe.mapOrElse when updating positions is appropriate for gracefully handling missing entries. This ensures that objects without new positions retain their existing state.


171-176: Verify the conditional fitting of the canvas after auto-layout

The if full then condition applies fitCanvasAlgo when all objects are involved. Ensure that this logic aligns with user expectations and that the canvas fitting is correctly triggered.

Would you like to confirm that the full flag accurately represents when all objects are included and that fitCanvasAlgo adjusts the view appropriately?

frontend/src/PagesComponents/Organization_/Project_/Updates/Hotkey.elm (2)

38-38: Ensure all return statements match the updated return type

The handleHotkey function's return type has changed to ( Model, Extra Msg ). Verify that all return statements within this function have been updated accordingly to prevent any type mismatches.


121-121: Verify the zoom reset calculation

The "reset-zoom" hotkey handler sets the zoom level using the expression 1 - currentZoom. If currentZoom is 1, this results in a zoom level of 0, which might not be intended.

Ensure that the zoom reset calculation correctly resets the zoom to the default level. Consider updating the expression to set the zoom level directly to 1:

"reset-zoom" ->
-    ( model, Zoom (1 - (model.erd |> Maybe.mapOrElse (Erd.currentLayout >> .canvas >> .zoom) 0)) |> Extra.msg )
+    ( model, Zoom (1 - (model.erd |> Maybe.mapOrElse (Erd.currentLayout >> .canvas >> .zoom) 1)) |> Extra.msg )

Or simply:

"reset-zoom" ->
    ( model, Zoom (1 - 1) |> Extra.msg )
backend/lib/azimutt_web/templates/website/index.html.heex (4)

7-10: Promotional banner updates look correct

The adjustments to the Black Friday promotional banner are properly implemented.


63-70: Navigation links are appropriately updated

The links and buttons for logged-in and anonymous users are correctly implemented.


80-82: Updated audience list reads well

The addition of 'Tech Support' to the audience list is appropriate.


280-285: Feature section updates are properly implemented

The feature section is correctly structured and leverages partial rendering appropriately.

frontend/src/Models/AutoLayout.elm Show resolved Hide resolved
Comment on lines +85 to +94
( model, model |> moveTables { dx = 0, dy = -10 } |> Maybe.orElse (model |> upDetails) |> Extra.msgM )

"move-right" ->
( model, model |> moveTables { dx = 10, dy = 0 } |> Maybe.orElse (model |> nextDetails) |> Maybe.withDefault Cmd.none )
( model, model |> moveTables { dx = 10, dy = 0 } |> Maybe.orElse (model |> nextDetails) |> Extra.msgM )

"move-down" ->
( model, model |> moveTables { dx = 0, dy = 10 } |> Maybe.withDefault Cmd.none )
( model, model |> moveTables { dx = 0, dy = 10 } |> Extra.msgM )

"move-left" ->
( model, model |> moveTables { dx = -10, dy = 0 } |> Maybe.orElse (model |> prevDetails) |> Maybe.withDefault Cmd.none )
( model, model |> moveTables { dx = -10, dy = 0 } |> Maybe.orElse (model |> prevDetails) |> Extra.msgM )
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Inconsistent handling of 'move' hotkeys

The hotkeys for "move-up", "move-right", and "move-left" include both table movement and detail navigation using Maybe.orElse with functions like upDetails, nextDetails, and prevDetails. However, the "move-down" hotkey only handles table movement and lacks corresponding detail navigation.

Consider implementing a downDetails function to provide consistent behavior across all directional hotkeys. Here's how you might update the code:

-- Add the downDetails function
downDetails : Model -> Maybe Msg
downDetails model =
    onDetails model
        (\_ -> Nothing)
        (\view -> view.table.next |> Maybe.map (.id >> DetailsSidebar.ShowTable))
        (\_ -> Nothing)

-- Update the "move-down" hotkey handler
"move-down" ->
    ( model, model |> moveTables { dx = 0, dy = 10 } |> Maybe.orElse (model |> downDetails) |> Extra.msgM )

@loicknuchel loicknuchel merged commit 59eb086 into main Nov 17, 2024
6 checks passed
@loicknuchel loicknuchel deleted the auto-layout-improvements branch November 17, 2024 20:46
loicknuchel added a commit that referenced this pull request Nov 17, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant