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

Add comparison pages #331

Merged
merged 8 commits into from
Nov 15, 2024
Merged

Add comparison pages #331

merged 8 commits into from
Nov 15, 2024

Conversation

loicknuchel
Copy link
Contributor

@loicknuchel loicknuchel commented Nov 13, 2024

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced support for syntax highlighting of DBML (Database Markup Language) alongside existing AML.
    • Added new routes for pricing, use cases, and feature comparisons in the website.
    • Enhanced the footer with a new "Comparisons" link and dynamic content for connectors.
    • Added a new section for "Data statistics" in the data exploration documentation.
    • Introduced a new HTML template for the AI documentation page.
    • Added new functions for rendering comparison and connector articles in the website view.
  • Improvements

    • Updated SEO metadata across various pages for better visibility.
    • Improved documentation on polymorphic relations and added a new section for AI query generation.
    • Enhanced the XML sitemap with new entries and dynamic last modification dates.
    • Modified the homepage to reflect a new promotional discount and updated product star ratings.
    • Improved layout and functionality of the comparisons page with dynamic content rendering.
  • Bug Fixes

    • Corrected image paths in the converter page to ensure proper loading.
    • Fixed formatting issues in the AML README example for clarity.
    • Improved error handling in the API documentation for source management actions.

Copy link

coderabbitai bot commented Nov 13, 2024

Caution

Review failed

The pull request is closed.

Walkthrough

The pull request introduces multiple changes across various files in the backend of the project. Key modifications include adjustments to configuration settings, enhancements to language definitions for syntax highlighting, updates to API documentation, and the introduction of new templates for comparison pages. Additionally, several functions have been added or modified to improve functionality and user experience, particularly in relation to SEO and dynamic content rendering. Overall, the changes aim to refine existing features and expand the application's capabilities.

Changes

File Path Change Summary
backend/.formatter.exs Updated line_length parameter from 180 to 240.
backend/assets/js/aml.hljs.ts Added relationBeginRegex and relationEndRegex constants for inline relations.
backend/assets/js/app.ts Registered new language dbml for syntax highlighting.
backend/assets/js/dbml.hljs.ts Introduced language definition for DBML with regex patterns.
backend/config/config.exs Updated seo_keywords, azimutt_documentation, version, and version_date entries.
backend/lib/azimutt.ex Enhanced showcase_usages, connectors, and added comparisons function.
backend/lib/azimutt_web/controllers/api/source_controller.ex Updated Swagger API documentation and validation for create, update, and delete actions.
backend/lib/azimutt_web/controllers/blog_controller.ex Reformatted description field and updated image URL generation.
backend/lib/azimutt_web/controllers/gallery_controller.ex Updated image URL generation method.
backend/lib/azimutt_web/controllers/website_controller.ex Added multiple new actions and refined SEO handling across various actions.
backend/lib/azimutt_web/router.ex Added and removed several routes, including new comparison routes.
backend/lib/azimutt_web/templates/sitemap/index.xml.eex Updated sitemap entries and last modification date logic.
backend/lib/azimutt_web/templates/website/_footer.html.heex Added link to "Comparisons" page and modified "Documentation" link behavior.
backend/lib/azimutt_web/templates/website/comparisons/_card.html.heex Introduced a new comparison card template fragment.
backend/lib/azimutt_web/templates/website/comparisons/_footer.html.heex Added call-to-action section and dynamically generated tool comparisons.
backend/lib/azimutt_web/templates/website/comparisons/_h2.html.heex Introduced new <h2> element with dynamic ID.
backend/lib/azimutt_web/templates/website/comparisons/_h3.html.heex Introduced new <h3> element with dynamic ID.
backend/lib/azimutt_web/templates/website/comparisons/_prose.html.heex Added new HTML template fragment for prose content.
backend/lib/azimutt_web/templates/website/comparisons/chartdb.html.heex Added structured content for comparisons with SEO title.
backend/lib/azimutt_web/templates/website/comparisons/datagrip.html.heex Added new comparison page structure with SEO title and footer.
backend/lib/azimutt_web/templates/website/comparisons/dbdiagram.html.heex Introduced comparison between DBML and AML with structured content.
backend/lib/azimutt_web/templates/website/comparisons/dbeaver.html.heex Added new comparison page for DBeaver with SEO title and footer.
backend/lib/azimutt_web/templates/website/comparisons/dbml.html.heex Added comparison page for DBML vs. AML with detailed content.
backend/lib/azimutt_web/templates/website/comparisons/drawdb.html.heex Added structured content for comparisons with SEO title and footer.
backend/lib/azimutt_web/templates/website/comparisons/drawsql.html.heex Added new comparison page structure with SEO title and footer.
backend/lib/azimutt_web/templates/website/comparisons/index.html.heex Enhanced visual layout and functionality of the comparisons page.
backend/lib/azimutt_web/templates/website/comparisons/metabase.html.heex Added new comparison page for Metabase with structured content.
backend/lib/azimutt_web/templates/website/comparisons/navicat.html.heex Added new comparison page for Navicat with SEO title and footer.
backend/lib/azimutt_web/templates/website/comparisons/schemaspy.html.heex Added new comparison page for SchemaSpy with structured content.
backend/lib/azimutt_web/templates/website/connectors/_card.html.heex Modified description rendering to use @connector.description directly.
backend/lib/azimutt_web/templates/website/connectors/_description-short.html.heex Deleted file with conditional rendering logic for short descriptions.
backend/lib/azimutt_web/templates/website/connectors/_footer.html.heex Increased footer width and number of displayed connectors.
backend/lib/azimutt_web/templates/website/converters/_editors-script.html.heex Updated JSON generation logic to use SQL library instead of AML.
backend/lib/azimutt_web/templates/website/converters/converter.html.heex Updated image source paths to use Routes.static_path/2.
backend/lib/azimutt_web/templates/website/docs/ai.html.heex Added new AI documentation page template.
backend/lib/azimutt_web/templates/website/docs/aml/relations.html.heex Updated documentation with hyperlink for further reading on polymorphic relations.
backend/lib/azimutt_web/templates/website/docs/data-exploration.html.heex Added "Data statistics" section and removed "AI query generation" header.
backend/lib/azimutt_web/templates/website/docs/layouts.html.heex Added "Work In Progress" indicators in various sections.
backend/lib/azimutt_web/templates/website/index.html.heex Updated homepage promotional content and alt text for images.
backend/lib/azimutt_web/templates/website/use-cases/analyze.html.heex Replaced static title with dynamic title from @use_case.title.
backend/lib/azimutt_web/templates/website/use-cases/design.html.heex Replaced static title with dynamic title from @use_case.title.
backend/lib/azimutt_web/templates/website/use-cases/document.html.heex Replaced static title with dynamic title from @use_case.title.
backend/lib/azimutt_web/templates/website/use-cases/explore.html.heex Replaced static title with dynamic title from @use_case.title.
backend/lib/azimutt_web/views/layout_view.ex Enhanced type checks for SEO-related functions and modified canonical URL construction.
backend/lib/azimutt_web/views/website_view.ex Added comparison_prose/2 and connector_article/2 functions; restructured existing functions.
backend/test/azimutt/utils/mapx_test.exs Removed unnecessary line breaks in assertions for better readability.
libs/aml/README.md Corrected formatting in example AML code and refined documentation for clarity.
libs/parser-sql/src/postgresBuilder.ts Modified order of properties in addDQL and addDML functions without altering logic.

Poem

In the burrow deep, where changes bloom,
New features sprout, dispelling gloom.
With regex and routes, our paths align,
A hop through code, all will be fine.
So raise your ears, let’s cheer and play,
For Azimutt’s magic grows day by day! 🐇✨


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 14, 2024 18:07
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: 8

🧹 Outside diff range and nitpick comments (54)
backend/lib/azimutt_web/templates/website/comparisons/chartdb.html.heex (2)

1-3: Consider enhancing SEO metadata.

The implementation looks good, but consider adding more SEO-friendly elements like meta description, keywords, and Open Graph tags for better social sharing.

 <%= comparison_prose do %>
     <h1><%= @seo.title %></h1>
+    <meta name="description" content={@seo.description}>
+    <meta property="og:title" content={@seo.title}>
+    <meta property="og:description" content={@seo.description}>
 <% end %>

1-5: Add structured data for better SEO.

Consider adding JSON-LD structured data to improve search engine understanding of the comparison content.

+<script type="application/ld+json">
+{
+  "@context": "https://schema.org",
+  "@type": "Article",
+  "headline": "<%= @seo.title %>",
+  "description": "<%= @seo.description %>",
+  "publisher": {
+    "@type": "Organization",
+    "name": "Azimutt",
+    "url": "https://azimutt.app"
+  }
+}
+</script>
 <%= comparison_prose do %>
     <h1><%= @seo.title %></h1>
 <% end %>
backend/lib/azimutt_web/templates/website/comparisons/datagrip.html.heex (2)

1-3: Maintain consistent indentation

The content inside the comparison_prose block is indented with 4 spaces while the standard convention is 2 spaces.

 <%= comparison_prose do %>
-    <h1><%= @seo.title %></h1>
+  <h1><%= @seo.title %></h1>
 <% end %>

1-5: Consider adding more comparison content

The template currently only contains a title and footer. Consider adding detailed comparison content between DataGrip and Azimutt, such as:

  • Feature comparison table
  • Unique selling points
  • Use case scenarios
  • Screenshots or diagrams

This would provide more value to users evaluating both tools.

backend/lib/azimutt_web/templates/website/comparisons/drawdb.html.heex (1)

1-5: Consider enhancing SEO with additional metadata

The template is quite minimal. Consider adding more SEO elements to improve visibility and ranking.

Consider adding:

  • Meta description
  • Open Graph tags
  • Structured data for better SEO

Example addition:

+<%= if @seo do %>
+  <meta name="description" content={@seo.description}>
+  <meta property="og:title" content={@seo.title}>
+  <meta property="og:description" content={@seo.description}>
+<% end %>
 <%= comparison_prose do %>
     <h1><%= @seo.title %></h1>
 <% end %>
backend/lib/azimutt_web/templates/website/comparisons/metabase.html.heex (2)

1-3: Consider enhancing the page content and SEO metadata.

While the basic structure is good, the page could benefit from additional content and metadata to improve SEO and user experience.

Consider adding:

  • Meta description
  • Structured comparison content
  • Keywords and other relevant SEO tags

Example enhancement:

 <%= comparison_prose do %>
     <h1><%= @seo.title %></h1>
+    <meta name="description" content={@seo.description}>
+    <div class="comparison-content">
+        <section class="features">
+            <!-- Add comparison content here -->
+        </section>
+    </div>
 <% end %>

5-5: LGTM! Clean footer integration.

The footer implementation follows best practices by reusing the shared component and properly passing the required assigns.

Consider removing the extra blank line before the footer render for consistent formatting:

 <%= comparison_prose do %>
     <h1><%= @seo.title %></h1>
 <% end %>
-
 <%= render "comparisons/_footer.html", conn: @conn, tool: @tool %>
backend/lib/azimutt_web/templates/website/comparisons/_h2.html.heex (1)

1-4: Optimize slugify function calls and improve accessibility

The code looks good but could benefit from some improvements:

  1. The slugify function is called twice with the same input. Consider storing the result in a variable.
  2. The anchor link should have an aria-label for better accessibility.
  3. Consider using design system color tokens instead of hardcoded values.

Here's a suggested improvement:

-<h2 id={Azimutt.Utils.Slugme.slugify(@title)} class="group">
+<% slug = Azimutt.Utils.Slugme.slugify(@title) %>
+<h2 id={slug} class="group">
     <%= @title %>
-    <a href={"##{Azimutt.Utils.Slugme.slugify(@title)}"} class="ml-1 text-indigo-600 no-underline hover:underline opacity-0 group-hover:opacity-100 transition-opacity">#</a>
+    <a href={"##{slug}"} 
+       aria-label={"Link to #{@title} section"}
+       class="ml-1 text-primary no-underline hover:underline opacity-0 group-hover:opacity-100 transition-opacity">#</a>
 </h2>

Note: Replace text-primary with your design system's primary color token if available.

backend/lib/azimutt_web/templates/website/docs/data-exploration.html.heex (1)

3-4: Consider adding more detailed content or removing the WIP notice.

The "Work In Progress" message suggests incomplete documentation. Since this PR introduces new comparison pages and data statistics features, it would be valuable to provide at least basic documentation for these features.

Would you like me to help draft the initial documentation content for the data statistics section?

backend/lib/azimutt_web/templates/website/connectors/_footer.html.heex (1)

6-9: Consider handling edge cases and performance optimization

While the implementation is functional, consider these improvements:

  1. Handle the case when there are fewer than 4 other connectors to avoid empty grid spaces
  2. Consider extracting the connector filtering logic to the controller to improve template clarity and reusability
- <%= for connector <- Azimutt.connectors() |> Enum.filter(fn c -> c.id != @connector.id end) |> Enum.shuffle() |> Enum.take(4) do %>
+ <%= for connector <- @other_connectors do %>

Then in the controller:

other_connectors = Azimutt.connectors()
  |> Enum.filter(fn c -> c.id != connector.id end)
  |> Enum.shuffle()
  |> Enum.take(min(4, length(other_connectors)))

render(conn, "show.html", connector: connector, other_connectors: other_connectors)
backend/lib/azimutt_web/templates/website/connectors/_card.html.heex (1)

10-10: Document the expected format of connector descriptions

Since we're moving from a partial that might have handled formatting to direct attribute access, it would be helpful to document the expected format of @connector.description.

Add a comment above the line:

+<%# @connector.description: plain text, single line description of the connector %>
 <p class="mt-2 text-sm text-gray-500"><%= @connector.description %></p>
backend/lib/azimutt_web/templates/website/comparisons/_card.html.heex (3)

1-1: Consider enhancing accessibility for interactive elements

The hover effect suggests this is an interactive element. Consider adding appropriate ARIA attributes to improve screen reader support.

-<div class="relative p-6 rounded-xl bg-white border border-gray-200 hover:border-indigo-500 hover:shadow hover:shadow-indigo-500/50">
+<div class="relative p-6 rounded-xl bg-white border border-gray-200 hover:border-indigo-500 hover:shadow hover:shadow-indigo-500/50" role="article" aria-labelledby="tool-name">

4-9: Add ID to heading for ARIA labelling

The heading needs an ID to match the suggested aria-labelledby attribute on the container.

-        <h3 class="text-base font-semibold leading-6 text-gray-900">
+        <h3 id="tool-name" class="text-base font-semibold leading-6 text-gray-900">

1-12: Document required assigns for the template

Consider adding a module documentation comment at the top of the file to specify the required assigns and their expected structure.

+<%# Required assigns:
+#   - @tool: %{id: string, name: string, description: string}
+#   - @category: %{id: string}
+#   - @conn: connection
+%>
 <div class="relative p-6 rounded-xl bg-white border border-gray-200 hover:border-indigo-500 hover:shadow hover:shadow-indigo-500/50">
backend/assets/js/aml.hljs.ts (1)

7-8: Consider making the relation regex patterns more specific.

The current patterns could be improved:

  1. relationBeginRegex: The pattern is good but consider adding ^ anchor if relations must start at line beginning
  2. relationEndRegex: The pattern /[) \n]/ might be too permissive as it matches any single space. Consider using a more specific pattern like /[)]\s*|\n/ to only match spaces after a closing parenthesis.
-const relationBeginRegex = /-> |fk /
-const relationEndRegex = /[) \n]/
+const relationBeginRegex = /^?(-> |fk )/
+const relationEndRegex = /[)]\s*|\n/
backend/assets/js/dbml.hljs.ts (3)

8-8: Add support for multi-line comments

The comment regex only handles single-line comments. DBML also supports multi-line comments.

-const commentRegex = /\/\/.*/
+const commentRegex = /\/\/.*|\/\*[\s\S]*?\*\//

9-9: Expand keyword coverage

The keyword list is missing some common DBML keywords like 'references', 'as', 'increment', etc.

-const keywordRegex = /Table|pk|primary key|not null|default|unique|note|ref|indexes|enum/
+const keywordRegex = /\b(?:Table|pk|primary key|not null|default|unique|note|ref|references|as|increment|indexes|enum)\b/

11-27: Add JSDoc documentation

The language function lacks documentation explaining its purpose and usage.

Add documentation like this:

+/**
+ * Provides syntax highlighting definition for DBML (Database Markup Language)
+ * @param hljs - Highlight.js API instance
+ * @returns Language definition for DBML
+ * @see https://www.dbml.org/docs/
+ */
 export function language(hljs: HLJSApi): Language {
backend/lib/azimutt_web/templates/website/comparisons/_footer.html.heex (2)

17-17: Enhance image accessibility and configurability

The screenshot's alt text could be more descriptive to improve accessibility. Consider making the image path configurable for easier maintenance.

-<img class="translate-x-6 translate-y-6 transform rounded-md object-cover object-left-top sm:translate-x-16 lg:translate-y-20" src={Routes.static_path(@conn, "/images/screenshots/azimutt-ecommerce.png")} alt="App screenshot">
+<img class="translate-x-6 translate-y-6 transform rounded-md object-cover object-left-top sm:translate-x-16 lg:translate-y-20" 
+    src={Routes.static_path(@conn, "/images/screenshots/azimutt-ecommerce.png")} 
+    alt="Azimutt database diagram interface showing an e-commerce schema example">

12-12: Enhance email link with additional attributes

Consider adding tracking and accessibility attributes to the email link.

-<a href={"mailto:#{Azimutt.config(:contact_email)}"} class="text-sm/6 font-semibold text-white">Book a call <span aria-hidden="true">→</span></a>
+<a href={"mailto:#{Azimutt.config(:contact_email)}?subject=Azimutt Demo Request"} 
+   class="text-sm/6 font-semibold text-white"
+   data-tracking="demo-request-email"
+   aria-label="Book a demo call with us">
+   Book a call <span aria-hidden="true">→</span>
+</a>
backend/assets/js/app.ts (2)

35-35: Consider adding error handling for language registration

The language registration could fail silently if there are issues with the DBML definition. Consider wrapping it in a try-catch block to handle potential errors gracefully.

-Hljs.registerLanguage('dbml', hljsDbml)
+try {
+  Hljs.registerLanguage('dbml', hljsDbml)
+} catch (error) {
+  console.error('Failed to register DBML language:', error)
+}

35-37: Consider dynamic content handling for comparison pages

Since this PR adds comparison pages, ensure that syntax highlighting is properly initialized for dynamically loaded content. The current highlightAll() call only processes content present during initial page load.

Consider adding a LiveView hook to handle syntax highlighting for dynamically loaded comparison content:

const hooks = {
  SyntaxHighlight: {
    mounted() {
      Hljs.highlightElement(this.el)
    },
    updated() {
      Hljs.highlightElement(this.el)
    }
  }
}

Then in your LiveView templates:

<pre><code class="language-dbml" phx-hook="SyntaxHighlight">
  <%= @comparison_content %>
</code></pre>
backend/lib/azimutt_web/templates/sitemap/index.xml.eex (3)

14-20: Consider extracting hardcoded dates into configuration

The date "2024-10-15" is hardcoded multiple times. Consider extracting it into a module attribute or configuration value for easier maintenance.

+ @connectors_last_mod "2024-10-15"
...
-  <url><loc><%= Routes.website_url(@conn, :connectors) %></loc><lastmod>2024-10-15</lastmod></url>
-  <url><loc><%= Routes.website_url(@conn, :connector_new) %></loc><lastmod>2024-10-15</lastmod></url>
+  <url><loc><%= Routes.website_url(@conn, :connectors) %></loc><lastmod>@connectors_last_mod</lastmod></url>
+  <url><loc><%= Routes.website_url(@conn, :connector_new) %></loc><lastmod>@connectors_last_mod</lastmod></url>
   <%= for connector <- Azimutt.connectors() do %>
-  <url><loc><%= Routes.website_url(@conn, :connector, connector.id) %></loc><lastmod><%= connector[:date] || "2024-10-15" %></lastmod></url>
+  <url><loc><%= Routes.website_url(@conn, :connector, connector.id) %></loc><lastmod><%= connector[:date] || @connectors_last_mod %></lastmod></url>

29-35: Well-structured comparison pages implementation

The implementation follows good practices and maintains consistency with other sections. However, consider extracting the hardcoded date "2024-11-15" into a configuration value.

+ @comparisons_last_mod "2024-11-15"
...
-  <url><loc><%= Routes.website_url(@conn, :comparisons) %></loc><lastmod>2024-11-15</lastmod></url>
+  <url><loc><%= Routes.website_url(@conn, :comparisons) %></loc><lastmod>@comparisons_last_mod</lastmod></url>
   <%= for category <- Azimutt.comparisons() do %>
   <%= for tool <- category.tools do %>
-  <url><loc><%= Routes.website_url(@conn, :comparison, category.id, tool.id) %></loc><lastmod><%= tool[:date] || "2024-11-15" %></lastmod></url>
+  <url><loc><%= Routes.website_url(@conn, :comparison, category.id, tool.id) %></loc><lastmod><%= tool[:date] || @comparisons_last_mod %></lastmod></url>

37-39: Consistent with previous sections, consider date configuration

The documentation section follows the same pattern as other sections. For consistency, the hardcoded date "2024-10-07" should also be moved to configuration.

+ @docs_last_mod "2024-10-07"
...
-  <url><loc><%= Routes.website_url(@conn, :docs) %></loc><lastmod>2024-10-07</lastmod></url>
+  <url><loc><%= Routes.website_url(@conn, :docs) %></loc><lastmod>@docs_last_mod</lastmod></url>
   <%= for doc <- Azimutt.doc_pages_flat() do %>
-  <url><loc><%= Routes.website_url(@conn, :doc, doc.path) %></loc><lastmod><%= doc[:date] || "2024-10-07" %></lastmod></url>
+  <url><loc><%= Routes.website_url(@conn, :doc, doc.path) %></loc><lastmod><%= doc[:date] || @docs_last_mod %></lastmod></url>
backend/lib/azimutt_web/templates/website/converters/converter.html.heex (1)

25-25: Fix inconsistent alt text terminology

There's an inconsistency in the alt text: it uses "connector" while the page title and other references use "converter".

Apply this change for consistency:

-    <img class="h-48 w-full object-cover" src={Routes.static_path(@conn, "/images/converters/#{converter.id}.jpg")} alt={"#{converter.name} connector"}>
+    <img class="h-48 w-full object-cover" src={Routes.static_path(@conn, "/images/converters/#{converter.id}.jpg")} alt={"#{converter.name} converter"}>
backend/config/config.exs (1)

30-32: Consider automating version management.

The TODO comment suggests implementing an automated process for version management. Consider using tools like:

  • conventional-changelog for automated version bumping
  • Git hooks to update the version date automatically
  • CI/CD pipeline integration for version management

Would you like me to help create a GitHub issue to track the implementation of automated version management?

backend/lib/azimutt_web/views/layout_view.ex (1)

22-22: Consider optimizing the title concatenation logic.

While the guard clause addition is good for type safety, the title concatenation could be more efficient.

-  def title(%{assigns: %{seo: %{title: title}}}) when is_binary(title), do: title <> if(title |> String.contains?("Azimutt"), do: "", else: " · Azimutt")
+  def title(%{assigns: %{seo: %{title: title}}}) when is_binary(title) do
+    if String.contains?(title, "Azimutt"), do: title, else: title <> " · Azimutt"
+  end
backend/lib/azimutt_web/templates/website/comparisons/index.html.heex (3)

2-6: Add aria-label to describe the decorative SVG pattern

While aria-hidden="true" is correctly used, adding an aria-label would improve accessibility by providing context for screen readers when the SVG is visible.

-<svg class="absolute inset-x-0 top-0 -z-10 h-[64rem] w-full stroke-gray-200 [mask-image:radial-gradient(32rem_32rem_at_center,white,transparent)] pointer-events-none" aria-hidden="true">
+<svg class="absolute inset-x-0 top-0 -z-10 h-[64rem] w-full stroke-gray-200 [mask-image:radial-gradient(32rem_32rem_at_center,white,transparent)] pointer-events-none" aria-hidden="true" aria-label="Background pattern">

54-63: Add empty state handling and optimize iterations

Consider the following improvements:

  1. Add an empty state message when no categories or tools are available
  2. Consider preloading tools to avoid N+1 query issues
+<%= if Enum.empty?(@categories) do %>
+  <div class="text-center py-10">
+    <p class="text-gray-500">No comparison categories available at the moment.</p>
+  </div>
+<% else %>
 <%= for category <- @categories |> Enum.filter(fn c -> length(c.tools) > 0 end) do %>
   <div class="border-b border-gray-200 mb-10">
     <h2 id={category.id} class="text-xl font-semibold leading-6 text-gray-900 pt-10">How Azimutt compares with <%= category.name %> tools</h2>
   </div>
   <div class="grid grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4">
     <%= for tool <- category.tools do %>
       <%= render "comparisons/_card.html", conn: @conn, category: category, tool: tool %>
     <% end %>
   </div>
 <% end %>
+<% end %>

14-15: Consider adding internationalization support

The heading and description text are hardcoded. Consider using gettext for internationalization to make the content maintainable and translatable.

-<h1 class="text-pretty text-5xl font-semibold tracking-tight text-gray-900 sm:text-7xl">Azimutt is your next database toolbox</h1>
-<p class="mt-8 text-pretty text-lg font-medium text-gray-500 sm:max-w-md sm:text-xl/8 lg:max-w-none">Azimutt started as an ERD for large databases, then it evolved including many tools to handle your database. See how it compares with other database tools and even categories.</p>
+<h1 class="text-pretty text-5xl font-semibold tracking-tight text-gray-900 sm:text-7xl"><%= gettext("Azimutt is your next database toolbox") %></h1>
+<p class="mt-8 text-pretty text-lg font-medium text-gray-500 sm:max-w-md sm:text-xl/8 lg:max-w-none"><%= gettext("Azimutt started as an ERD for large databases, then it evolved including many tools to handle your database. See how it compares with other database tools and even categories.") %></p>
libs/aml/README.md (1)

73-73: LGTM! Consider adding example output.

The string formatting correction improves clarity by properly representing the multi-line AML definition. To make the example even more helpful, consider adding a comment showing the expected parsed output.

Add a comment like this:

 const aml = 'users\n  id int pk\n  name varchar\n'
+// Expected output:
+// {
+//   tables: [{
+//     name: 'users',
+//     columns: [
+//       {name: 'id', type: 'int', primaryKey: true},
+//       {name: 'name', type: 'varchar'}
+//     ]
+//   }]
+// }
backend/lib/azimutt_web/templates/website/use-cases/analyze.html.heex (2)

Line range hint 89-96: Add syntax highlighting to code examples.

Consider using syntax highlighting for the JavaScript code examples to improve readability. Phoenix supports syntax highlighting through packages like makeup_js.

Example implementation:

-            <pre><code>const columns = azimutt.getAllTables().flatMap(t => t.columns)
+            <pre><code class="language-javascript">const columns = azimutt.getAllTables().flatMap(t => t.columns)

Line range hint 89-103: Document API version for JavaScript examples.

The code examples expose the internal azimutt JavaScript API. Consider adding:

  1. API version information
  2. Link to API documentation
  3. Browser console requirements

This helps users understand API compatibility and where to find more information.

backend/lib/azimutt_web/templates/website/use-cases/document.html.heex (1)

7-7: Consider adding a fallback for the dynamic title.

The change to use @use_case.title improves template reusability. However, consider adding a fallback value in case @use_case or @use_case.title is nil to prevent potential rendering issues.

-<span class="mt-2 block text-center text-3xl font-bold leading-8 tracking-tight text-gray-900 sm:text-4xl"><%= @use_case.title %></span>
+<span class="mt-2 block text-center text-3xl font-bold leading-8 tracking-tight text-gray-900 sm:text-4xl"><%= @use_case.title || "Database Documentation" %></span>
backend/lib/azimutt_web/templates/website/use-cases/design.html.heex (1)

7-7: Consider adding SEO metadata.

Since we're using dynamic titles, ensure consistent SEO by adding corresponding meta tags.

Add these meta tags in the head section:

+<%= if @use_case do %>
+  <title><%= @use_case.title %> - Azimutt</title>
+  <meta name="description" content={@use_case.description}>
+  <meta property="og:title" content={@use_case.title}>
+  <meta property="og:description" content={@use_case.description}>
+<% end %>
backend/lib/azimutt_web/templates/website/converters/_editors-script.html.heex (4)

Line range hint 1-2: Review CDN dependency versions.

The script loads AML and SQL libraries from CDN with fixed versions:

  • @azimutt/aml@0.1.8
  • @azimutt/parser-sql@0.1.3

Consider:

  1. Using consistent version patterns across dependencies
  2. Documenting the version requirements
  3. Adding integrity hashes for better security

Example improvement:

-<script src="https://cdn.jsdelivr.net/npm/@azimutt/aml@0.1.8/out/bundle.min.js"></script>
-<script src="https://cdn.jsdelivr.net/npm/@azimutt/parser-sql@0.1.3/out/bundle.min.js"></script>
+<script src="https://cdn.jsdelivr.net/npm/@azimutt/aml@0.1.8/out/bundle.min.js" 
+        integrity="sha384-..." 
+        crossorigin="anonymous"></script>
+<script src="https://cdn.jsdelivr.net/npm/@azimutt/parser-sql@0.1.3/out/bundle.min.js" 
+        integrity="sha384-..." 
+        crossorigin="anonymous"></script>

Line range hint 3-8: Consider environment-based script loading.

The commented-out local script references suggest a need for environment-based loading. Consider implementing dynamic script loading based on the environment (development/production).

Example improvement:

+<% if Mix.env() == :dev do %>
+  <script src="/elm/aml.min.js"></script>
+  <script src="/elm/sql.min.js"></script>
+  <script src="/elm/prisma.min.js"></script>
+  <script src="/elm/dbml.min.js"></script>
+<% else %>
   <script src="https://cdn.jsdelivr.net/npm/@azimutt/aml@0.1.8/out/bundle.min.js"></script>
   <script src="https://cdn.jsdelivr.net/npm/@azimutt/parser-sql@0.1.3/out/bundle.min.js"></script>
+<% end %>

Line range hint 107-115: Enhance error handling in format function.

The current error handling in the format function could be improved to:

  1. Provide more specific error messages
  2. Include error details in the output
  3. Log errors for debugging

Example improvement:

 function format(lang, db) {
     try {
         if (lang === 'aml') return aml.generateAml(db)
         if (lang === 'amlv1') return aml.generateAml(db, true)
         if (lang === 'json') return sql.generateJsonDatabase(db)
         if (lang === 'postgres') return sql.generateSql(db, 'postgres')
         if (lang === 'dot') return aml.generateDot(db)
         if (lang === 'mermaid') return aml.generateMermaid(db)
         if (lang === 'markdown') return aml.generateMarkdown(db)
-        return 'Unsupported destination dialect: ' + lang
+        const error = `Unsupported destination dialect: ${lang}`
+        console.error(error)
+        return error
     } catch (e) {
-        return 'Failed to generate ' + lang + (e && e.message ? ': ' + e.message : '')
+        const error = `Failed to generate ${lang}: ${e?.message || 'Unknown error'}`
+        console.error(error, e)
+        return error
     }
 }

Line range hint 166-175: Sanitize URL parameters for security.

The URL parameter handling could benefit from additional security measures:

  1. Input validation for the 'value' parameter
  2. Length limits for URL parameters
  3. Sanitization of decoded content

Example improvement:

 function getUrlValue(lang) {
     try {
         var url = new URL(window.location)
-        var value = url.searchParams.get('value') // automatically decoded!
-        var hash = decodeURIComponent(url.hash.slice(1))
+        var value = sanitizeInput(url.searchParams.get('value'))
+        var hash = sanitizeInput(decodeURIComponent(url.hash.slice(1)))
         var urlInput = (value || hash || '').trim() + '\n'
+        if (urlInput.length > MAX_INPUT_LENGTH) {
+            console.warn('Input exceeds maximum length')
+            return null
+        }
         var parsed = parse(lang, urlInput)
         return isEmptyDatabase(parsed.result) ? null : urlInput
     } catch (e) {
         console.warn('Unable to get stored url value', e)
+        return null
     }
 }
+
+function sanitizeInput(input) {
+    if (!input) return ''
+    // Add appropriate sanitization logic
+    return input.replace(/[<>]/g, '')
+}
backend/lib/azimutt_web/templates/website/comparisons/dbml.html.heex (4)

14-16: Fix typos in the text

There are two typos that need to be corrected:

  1. Line 15: "though" should be "thought"
  2. Line 95: "int" should be "in"
-        But it has some issues: it's different for each database, writing valid SQL will slow your though process, you may want to ignore some SQL requirements or add information not supported by your SQL dialect.
+        But it has some issues: it's different for each database, writing valid SQL will slow your thought process, you may want to ignore some SQL requirements or add information not supported by your SQL dialect.
-        And in fact, everything is optional int AML. Such a schema is usually built with several iterations:
+        And in fact, everything is optional in AML. Such a schema is usually built with several iterations:

Also applies to: 95-96


192-194: Fix grammatical error

The sentence structure needs improvement.

-        Depending on your needs, it can be important to the ability to define what you need with your chosen language.
+        Depending on your needs, the ability to define what you need with your chosen language can be important.

204-209: Enhance code examples with error handling and complex features

Consider improving the code examples:

  1. Add error handling for DBML parsing
  2. Show more complex SQL generation examples using features like indexes, constraints, and relations

Example enhancement for DBML parsing:

 const parser = new Parser()
 const dbml = 'Table users {\n  id int [pk]\n  name varchar\n}\n' // your DBML script
-const database = parser.parse(dbml, 'dbml')
-console.log('database', database)
+try {
+  const database = parser.parse(dbml, 'dbml')
+  console.log('database', database)
+} catch (error) {
+  console.error('Failed to parse DBML:', error.message)
+}

Example enhancement for SQL generation:

 const database = {entities: [{
   name: 'users',
   attrs: [{name: 'id', type: 'int'}, {name: 'name', type: 'varchar'}],
-  pk: {attrs: [['id']]}
+  pk: {attrs: [['id']]},
+  indexes: [{
+    name: 'users_name_idx',
+    attrs: [['name']],
+    unique: true
+  }],
+  checks: [{
+    name: 'name_length',
+    expr: 'length(name) > 0'
+  }]
 }]}

Also applies to: 235-243


159-176: Enhance table accessibility

Consider improving the table's accessibility:

-    <table>
+    <table role="grid" aria-label="Feature comparison between dbdiagram.io and Azimutt">
         <thead><tr><th></th><th>dbdiagram.io</th><th>Azimutt</th></tr></thead>
         <tbody>
-        <tr><th>Table definition</th><td>✅ Yes</td><td>✅ Yes</td></tr>
+        <tr><th scope="row">Table definition</th><td aria-label="dbdiagram.io: Yes">✅ Yes</td><td aria-label="Azimutt: Yes">✅ Yes</td></tr>
         <!-- Apply similar changes to other rows -->
         </tbody>
     </table>
backend/lib/azimutt_web/templates/website/comparisons/dbdiagram.html.heex (4)

11-11: Enhance image accessibility by using SEO title

For better accessibility and SEO, consider using the @seo.title for the image alt text to maintain consistency.

-<img src={@seo.image} alt="dbdiagram.io vs Azimutt">
+<img src={@seo.image} alt={@seo.title}>

15-15: Fix grammatical error

There's a minor grammatical error in the sentence.

-Some tools are specialized in ERDs for databases and can more specialized content like primary keys, indexes, constraints and more.
+Some tools are specialized in ERDs for databases and can show more specialized features like primary keys, indexes, constraints and more.

214-214: Address TODO comment for testimonials

The TODO comment indicates missing testimonials section.

Would you like me to help create a testimonials section or create a GitHub issue to track this task?


231-231: Document the public demo token

The URL token is intentionally public for the demo project. However, it would be good to add a comment explaining this to prevent future security concerns.

-        or check our <a href="https://azimutt.app/45f571a6-d9b8-4752-8a13-93ac0d2b7984/c00d0c45-8db2-46b7-9b51-eba661640c3c?token=9a59ccbb-7a58-4c88-9dfc-692de6177be9" target="_blank" rel="noopener">complex e-commerce demo</a> with 85 tables over 9 databases.
+        <%# This is a public read-only token for the demo project %>
+        or check our <a href="https://azimutt.app/45f571a6-d9b8-4752-8a13-93ac0d2b7984/c00d0c45-8db2-46b7-9b51-eba661640c3c?token=9a59ccbb-7a58-4c88-9dfc-692de6177be9" target="_blank" rel="noopener">complex e-commerce demo</a> with 85 tables over 9 databases.
🧰 Tools
🪛 Gitleaks

231-231: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

backend/lib/azimutt_web/controllers/api/source_controller.ex (1)

232-232: Consider splitting URL documentation by source kind

The consolidated URL property documentation combines multiple use cases into a single description, which might make it less clear for API consumers. Each source kind (DatabaseConnection, SqlRemoteFile, PrismaRemoteFile, JsonRemoteFile) has different URL format requirements and constraints.

Consider using OpenAPI's discriminator pattern to provide kind-specific URL documentation:

-            url(:string, "Database url for DatabaseConnection kind, file url for remote kinds (SqlRemoteFile, PrismaRemoteFile & JsonRemoteFile)", example: "postgresql://postgres:postgres@localhost:5432/azimutt_dev")
+            url(:string, "URL specific to the source kind", required: false)
+            description("Format depends on the source kind:
+              - DatabaseConnection: Database connection URL (e.g., postgresql://user:pass@host:port/db)
+              - SqlRemoteFile: HTTP(S) URL to a SQL file
+              - PrismaRemoteFile: HTTP(S) URL to a Prisma schema file
+              - JsonRemoteFile: HTTP(S) URL to a JSON file")

This would make the documentation more maintainable and clearer for API consumers.

backend/lib/azimutt.ex (2)

319-327: Reduce repetition in connector descriptions

Each description ends with a variation of "... with Azimutt". Consider removing these repetitive endings as they're implicit given the context.

Example improvement for PostgreSQL:

-      %{id: "postgres", name: "PostgreSQL", color: "#699eca", description: "The most powerful open-source relational database, known for extensibility and SQL compliance. Now explorable with Azimutt."},
+      %{id: "postgres", name: "PostgreSQL", color: "#699eca", description: "The most powerful open-source relational database, known for extensibility and SQL compliance."},

351-417: Standardize tool fields across comparisons

The DBML tool entry includes additional fields (title and image) that aren't present in other tools. Consider either:

  1. Adding these fields to other relevant tools for consistency
  2. Moving these fields to a separate specialized structure if they're specific to certain comparison types

Current structure:

%{id: "dbml", name: "DBML", title: "DBML vs AML, what are the differences?", image: "dbml-vs-aml.jpg", description: "..."}

Suggested structure if fields are specific to design language comparisons:

%{
  id: "database-design-language",
  name: "Database Design Language",
  keywords: "database design,database schema,dsl",
  comparison_type: :design_language,
  tools: [
    %{
      id: "dbml",
      name: "DBML",
      description: "...",
      design_language_specific: %{
        comparison_title: "DBML vs AML, what are the differences?",
        comparison_image: "dbml-vs-aml.jpg"
      }
    }
  ]
}
backend/lib/azimutt_web/templates/website/index.html.heex (1)

21-21: Consider fetching GitHub stars count dynamically.

The stars count is hardcoded and will need manual updates. Consider fetching this value dynamically from the GitHub API to ensure it stays current.

-                        1318 stars
+                        <%= AzimuttWeb.Components.GitHubStats.stars_count() %> stars

Add a new component to fetch and cache the stars count:

# lib/azimutt_web/components/github_stats.ex
defmodule AzimuttWeb.Components.GitHubStats do
  use GenServer
  @refresh_interval :timer.hours(1)

  def stars_count, do: GenServer.call(__MODULE__, :stars_count)

  def start_link(_), do: GenServer.start_link(__MODULE__, :ok, name: __MODULE__)

  def init(:ok) do
    {:ok, %{stars: fetch_stars()}, {:continue, :schedule_refresh}}
  end

  def handle_call(:stars_count, _from, state), do: {:reply, state.stars, state}

  def handle_info(:refresh, state), do: {:noreply, %{stars: fetch_stars()}, {:continue, :schedule_refresh}}

  def handle_continue(:schedule_refresh, state) do
    Process.send_after(self(), :refresh, @refresh_interval)
    {:noreply, state}
  end

  defp fetch_stars do
    # Add proper error handling
    {:ok, %{body: body}} = HTTPoison.get("https://api.github.com/repos/azimuttapp/azimutt")
    body |> Jason.decode!() |> Map.get("stargazers_count")
  end
end
backend/lib/azimutt_web/controllers/website_controller.ex (2)

159-182: Simplify nested string interpolation in the image path

In the comparison/3 action, the image path uses nested string interpolation, which can reduce readability. Consider simplifying it for clarity.

Apply this diff to simplify the image path:

-            image: Routes.static_url(conn, "/images/comparisons/#{t[:image] || "#{t.id}-vs-azimutt.jpg"}"),
+            image_name = t[:image] || "#{t.id}-vs-azimutt.jpg"
+            image: Routes.static_url(conn, "/images/comparisons/#{image_name}"),

Alternatively, consider using a helper function to generate the image path.


174-178: Ensure consistent access to map fields

Fields t[:title], t[:image], and t[:keywords] are accessed using bracket notation, while other fields like t.name are accessed using dot notation. For consistency and readability, it may be preferable to use one access method consistently.

If t is a map and certain keys may be nil, using Map.get(t, :key) can make the code clearer.

-              title: t[:title] || "#{t.name} vs Azimutt, which database tool is right for you?",
-              image: Routes.static_url(conn, "/images/comparisons/#{t[:image] || "#{t.id}-vs-azimutt.jpg"}"),
-              keywords: t[:keywords] || c[:keywords]
+              title: Map.get(t, :title, "#{t.name} vs Azimutt, which database tool is right for you?"),
+              image_name = Map.get(t, :image, "#{t.id}-vs-azimutt.jpg"),
+              image: Routes.static_url(conn, "/images/comparisons/#{image_name}"),
+              keywords: Map.get(t, :keywords, c[:keywords])

Ensure that the use of Map.get/3 aligns with how t and c are structured.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 911a70e and 8383952.

⛔ Files ignored due to path filters (25)
  • backend/priv/static/images/comparisons/amundsen-icon.png is excluded by !**/*.png
  • backend/priv/static/images/comparisons/atlan-icon.png is excluded by !**/*.png
  • backend/priv/static/images/comparisons/azimutt-ecommerce-diagram.jpg is excluded by !**/*.jpg
  • backend/priv/static/images/comparisons/azimutt-sample.jpg is excluded by !**/*.jpg
  • backend/priv/static/images/comparisons/beekeeperstudio-icon.png is excluded by !**/*.png
  • backend/priv/static/images/comparisons/chartdb-icon.png is excluded by !**/*.png
  • backend/priv/static/images/comparisons/datadog-icon.png is excluded by !**/*.png
  • backend/priv/static/images/comparisons/datagrip-icon.png is excluded by !**/*.png
  • backend/priv/static/images/comparisons/dbdiagram-ecommerce-diagram.jpg is excluded by !**/*.jpg
  • backend/priv/static/images/comparisons/dbdiagram-icon.png is excluded by !**/*.png
  • backend/priv/static/images/comparisons/dbdiagram-sample.jpg is excluded by !**/*.jpg
  • backend/priv/static/images/comparisons/dbdiagram-vs-azimutt.jpg is excluded by !**/*.jpg
  • backend/priv/static/images/comparisons/dbeaver-icon.png is excluded by !**/*.png
  • backend/priv/static/images/comparisons/dbml-icon.png is excluded by !**/*.png
  • backend/priv/static/images/comparisons/dbml-vs-aml.jpg is excluded by !**/*.jpg
  • backend/priv/static/images/comparisons/drawdb-icon.png is excluded by !**/*.png
  • backend/priv/static/images/comparisons/drawsql-icon.png is excluded by !**/*.png
  • backend/priv/static/images/comparisons/ecommerce-1.jpg is excluded by !**/*.jpg
  • backend/priv/static/images/comparisons/ecommerce-2.jpg is excluded by !**/*.jpg
  • backend/priv/static/images/comparisons/ecommerce-3.jpg is excluded by !**/*.jpg
  • backend/priv/static/images/comparisons/ecommerce-4.jpg is excluded by !**/*.jpg
  • backend/priv/static/images/comparisons/ecommerce-5.jpg is excluded by !**/*.jpg
  • backend/priv/static/images/comparisons/metabase-icon.png is excluded by !**/*.png
  • backend/priv/static/images/comparisons/navicat-icon.png is excluded by !**/*.png
  • backend/priv/static/images/comparisons/schemaspy-icon.png is excluded by !**/*.png
📒 Files selected for processing (48)
  • backend/.formatter.exs (1 hunks)
  • backend/assets/js/aml.hljs.ts (2 hunks)
  • backend/assets/js/app.ts (1 hunks)
  • backend/assets/js/dbml.hljs.ts (1 hunks)
  • backend/config/config.exs (2 hunks)
  • backend/lib/azimutt.ex (4 hunks)
  • backend/lib/azimutt_web/controllers/api/source_controller.ex (1 hunks)
  • backend/lib/azimutt_web/controllers/blog_controller.ex (2 hunks)
  • backend/lib/azimutt_web/controllers/gallery_controller.ex (1 hunks)
  • backend/lib/azimutt_web/controllers/website_controller.ex (5 hunks)
  • backend/lib/azimutt_web/router.ex (1 hunks)
  • backend/lib/azimutt_web/templates/sitemap/index.xml.eex (2 hunks)
  • backend/lib/azimutt_web/templates/website/_footer.html.heex (2 hunks)
  • backend/lib/azimutt_web/templates/website/comparisons/_card.html.heex (1 hunks)
  • backend/lib/azimutt_web/templates/website/comparisons/_footer.html.heex (1 hunks)
  • backend/lib/azimutt_web/templates/website/comparisons/_h2.html.heex (1 hunks)
  • backend/lib/azimutt_web/templates/website/comparisons/_h3.html.heex (1 hunks)
  • backend/lib/azimutt_web/templates/website/comparisons/_prose.html.heex (1 hunks)
  • backend/lib/azimutt_web/templates/website/comparisons/chartdb.html.heex (1 hunks)
  • backend/lib/azimutt_web/templates/website/comparisons/datagrip.html.heex (1 hunks)
  • backend/lib/azimutt_web/templates/website/comparisons/dbdiagram.html.heex (1 hunks)
  • backend/lib/azimutt_web/templates/website/comparisons/dbeaver.html.heex (1 hunks)
  • backend/lib/azimutt_web/templates/website/comparisons/dbml.html.heex (1 hunks)
  • backend/lib/azimutt_web/templates/website/comparisons/drawdb.html.heex (1 hunks)
  • backend/lib/azimutt_web/templates/website/comparisons/drawsql.html.heex (1 hunks)
  • backend/lib/azimutt_web/templates/website/comparisons/index.html.heex (1 hunks)
  • backend/lib/azimutt_web/templates/website/comparisons/metabase.html.heex (1 hunks)
  • backend/lib/azimutt_web/templates/website/comparisons/navicat.html.heex (1 hunks)
  • backend/lib/azimutt_web/templates/website/comparisons/schemaspy.html.heex (1 hunks)
  • backend/lib/azimutt_web/templates/website/connectors/_card.html.heex (1 hunks)
  • backend/lib/azimutt_web/templates/website/connectors/_description-short.html.heex (0 hunks)
  • backend/lib/azimutt_web/templates/website/connectors/_footer.html.heex (1 hunks)
  • backend/lib/azimutt_web/templates/website/converters/_editors-script.html.heex (1 hunks)
  • backend/lib/azimutt_web/templates/website/converters/converter.html.heex (2 hunks)
  • backend/lib/azimutt_web/templates/website/docs/ai.html.heex (1 hunks)
  • backend/lib/azimutt_web/templates/website/docs/aml/relations.html.heex (1 hunks)
  • backend/lib/azimutt_web/templates/website/docs/data-exploration.html.heex (1 hunks)
  • backend/lib/azimutt_web/templates/website/docs/layouts.html.heex (1 hunks)
  • backend/lib/azimutt_web/templates/website/index.html.heex (2 hunks)
  • backend/lib/azimutt_web/templates/website/use-cases/analyze.html.heex (1 hunks)
  • backend/lib/azimutt_web/templates/website/use-cases/design.html.heex (1 hunks)
  • backend/lib/azimutt_web/templates/website/use-cases/document.html.heex (1 hunks)
  • backend/lib/azimutt_web/templates/website/use-cases/explore.html.heex (1 hunks)
  • backend/lib/azimutt_web/views/layout_view.ex (1 hunks)
  • backend/lib/azimutt_web/views/website_view.ex (1 hunks)
  • backend/test/azimutt/utils/mapx_test.exs (1 hunks)
  • libs/aml/README.md (1 hunks)
  • libs/parser-sql/src/postgresBuilder.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • backend/lib/azimutt_web/templates/website/connectors/_description-short.html.heex
✅ Files skipped from review due to trivial changes (6)
  • backend/.formatter.exs
  • backend/lib/azimutt_web/templates/website/comparisons/_h3.html.heex
  • backend/lib/azimutt_web/templates/website/comparisons/_prose.html.heex
  • backend/lib/azimutt_web/templates/website/docs/aml/relations.html.heex
  • backend/lib/azimutt_web/templates/website/docs/layouts.html.heex
  • backend/test/azimutt/utils/mapx_test.exs
🧰 Additional context used
🪛 Gitleaks
backend/lib/azimutt_web/templates/website/comparisons/dbdiagram.html.heex

231-231: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

🔇 Additional comments (64)
backend/lib/azimutt_web/templates/website/comparisons/chartdb.html.heex (1)

5-5: LGTM! Clean footer implementation.

The footer partial is properly rendered with the required assigns.

backend/lib/azimutt_web/templates/website/comparisons/datagrip.html.heex (2)

2-2: Verify @seo assigns initialization

Ensure that the @seo assigns is properly initialized before being used in the template to avoid potential runtime errors.

✅ Verification successful

@seo is properly initialized in the website controller

The @seo assign is properly initialized in backend/lib/azimutt_web/controllers/website_controller.ex for comparison pages. The controller has a specific handler for comparison pages that sets the SEO data:

conn |> render("comparisons/#{category}.html", category: c, seo: %{type: "article"})

This initialization is consistent across all comparison templates, including datagrip.html.heex, as evidenced by the similar usage pattern in other comparison templates like dbeaver.html.heex, schemaspy.html.heex, etc. The layout views also properly handle these SEO assigns through various helper functions.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check how @seo is initialized for comparison pages
# Expected: Find the controller action or plug that sets up @seo assigns

# Look for controller actions setting up @seo
ast-grep --pattern 'def $_(conn, $_) do
  $$$
  assign($$, :seo, $_)
  $$$
end'

# Look for plugs setting up @seo
rg -l "plug.*:seo" backend/lib/azimutt_web/controllers/

Length of output: 139


Script:

#!/bin/bash
# Let's try different approaches to find how @seo is initialized

# Look for files containing 'seo' in the controllers and templates
rg -i "seo" backend/lib/azimutt_web/controllers/ backend/lib/azimutt_web/templates/

# Look for the specific template file and its controller
fd "datagrip.html" backend/
fd "website_controller" backend/

# Look for any assigns related to seo in the codebase
rg "assign.*seo" backend/

Length of output: 8724


5-5: Verify @tool assigns initialization

Ensure that the @tool assigns is properly initialized before being passed to the footer partial to avoid potential runtime errors.

✅ Verification successful

Let me gather more information to verify how the @tool assign is being used and set.


Let me check the website controller and how it handles the comparison pages.


Based on the gathered information, I can now provide the final response:

@tool assign is properly initialized and used

The @tool assign is correctly set in the comparison/2 function of the WebsiteController where it's extracted from the Azimutt comparisons configuration and passed to the template. The footer partial uses this assign to display related comparisons, filtering out the current tool. The implementation is safe and well-structured.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check how @tool is initialized for comparison pages
# Expected: Find the controller action or plug that sets up @tool assigns

# Look for controller actions setting up @tool
ast-grep --pattern 'def $_(conn, $_) do
  $$$
  assign($$, :tool, $_)
  $$$
end'

# Look for the footer partial to understand expected @tool structure
fd -e "html.heex" _footer.html backend/lib/azimutt_web/templates/website/comparisons/

Length of output: 240


Script:

#!/bin/bash
# Check the content of the footer partial
cat backend/lib/azimutt_web/templates/website/comparisons/_footer.html.heex

# Look for controller or router handling comparison pages
rg -l "comparisons" backend/lib/azimutt_web/controllers/
rg -l "comparisons" backend/lib/azimutt_web/router.ex

# Look for any module setting tool assign
rg "tool =" backend/lib/azimutt_web/controllers/

Length of output: 2577


Script:

#!/bin/bash
# Check the website controller content
cat backend/lib/azimutt_web/controllers/website_controller.ex

# Check the router configuration for comparison routes
rg "comparisons|/compare" backend/lib/azimutt_web/router.ex

Length of output: 9098

backend/lib/azimutt_web/templates/website/comparisons/dbeaver.html.heex (2)

5-5: Consider adding parameter validation for the footer.

While the footer inclusion is correct, consider adding validation for @tool to ensure it's properly set for DBeaver comparison.

#!/bin/bash
# Check how tool parameter is used in the footer
rg -A 5 "comparisons/_footer.html" --type heex

1-3: Consider adding validation and content for the comparison page.

While the structure is correct, consider:

  1. Adding a fallback for @seo.title in case it's nil
  2. Including actual comparison content between DBeaver and Azimutt
backend/lib/azimutt_web/templates/website/comparisons/drawdb.html.heex (2)

1-3: ⚠️ Potential issue

Add nil checking for @seo assign

The template directly accesses @seo.title without checking if @seo is nil, which could cause runtime errors.

Let's verify the pattern across other comparison pages:

Consider adding a nil check:

 <%= comparison_prose do %>
-    <h1><%= @seo.title %></h1>
+    <h1><%= if @seo, do: @seo.title, else: "DrawDB Comparison" %></h1>
 <% end %>

5-5: Verify footer partial and add nil checking for @tool assign

The template passes @tool to the footer partial without checking if it's nil.

Let's verify the footer partial and its usage:

Consider adding a nil check before rendering:

-<%= render "comparisons/_footer.html", conn: @conn, tool: @tool %>
+<%= if @tool do %>
+  <%= render "comparisons/_footer.html", conn: @conn, tool: @tool %>
+<% end %>
backend/lib/azimutt_web/templates/website/comparisons/drawsql.html.heex (2)

5-5: LGTM! Footer implementation follows best practices.

The footer implementation correctly:

  • Uses the shared partial template
  • Passes the required parameters (conn and tool)
  • Follows DRY principle

1-3: Verify if the comparison content is complete.

The template currently only contains a title without any actual comparison content. This seems incomplete compared to other comparison pages mentioned in the summary.

Let's check other comparison templates for consistency:

backend/lib/azimutt_web/templates/website/comparisons/metabase.html.heex (1)

1-5: Verify consistency with other comparison templates.

Let's ensure this template follows the same structure as other comparison pages.

✅ Verification successful

Based on the shell script results, I can see that all comparison templates follow the exact same structure:

  1. They all start with comparison_prose block containing the title
  2. They all end with the footer partial render
  3. Some have more content within the comparison_prose block

The template under review follows this consistent pattern perfectly, so I can generate the final response:

Template structure is consistent with other comparison pages

The template follows the standard structure used across all comparison pages with the comparison_prose wrapper and footer partial.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check structure consistency across comparison templates
# Expected: Similar structure with comparison_prose and footer

# Find and display all comparison template files
fd --type f . backend/lib/azimutt_web/templates/website/comparisons/ -e heex -E '_*.heex' -x cat {}

Length of output: 36492

backend/lib/azimutt_web/templates/website/comparisons/navicat.html.heex (3)

1-5: Verify consistency with other comparison pages

The template structure is minimal. Let's verify it follows the same pattern as other comparison pages.

#!/bin/bash
# Description: Compare structure with other comparison pages

# List all comparison templates
echo "Checking structure of other comparison pages:"
fd "\.html\.heex$" backend/lib/azimutt_web/templates/website/comparisons/

# Compare content patterns across comparison pages
echo -e "\nChecking content patterns in comparison pages:"
rg -l "comparison_prose" backend/lib/azimutt_web/templates/website/comparisons/

1-3: Verify the comparison_prose function and SEO title assignment

The structure looks good for SEO optimization with the title in an h1 tag. However, let's verify the implementation details.

✅ Verification successful

The comparison page structure and implementation are correct

The implementation is properly structured and consistent with other comparison pages:

  • The comparison_prose is correctly implemented as a component using _prose.html.heex partial, providing proper styling and layout
  • The SEO title is correctly assigned in the controller's comparison function
  • The structure matches other comparison pages like dbdiagram.html.heex
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the comparison_prose function implementation and SEO title assignment

# Check for comparison_prose function definition
echo "Checking for comparison_prose function definition:"
rg -l "def comparison_prose" backend/

# Check for @seo assign in the controller
echo -e "\nChecking for @seo assign in controllers:"
rg -l "assign.*:seo" backend/lib/azimutt_web/controllers/

Length of output: 2323


Script:

#!/bin/bash
# Let's check the _prose.html.heex partial and website controller for SEO assignments

# Check the prose partial implementation
echo "Checking _prose.html.heex content:"
cat backend/lib/azimutt_web/templates/website/comparisons/_prose.html.heex

# Check the website controller for SEO assignments
echo -e "\nChecking website controller for SEO assignments:"
rg -A 5 "def comparison" backend/lib/azimutt_web/controllers/website_controller.ex

# Check other comparison pages structure for reference
echo -e "\nChecking structure of another comparison page:"
head -n 5 backend/lib/azimutt_web/templates/website/comparisons/dbdiagram.html.heex

Length of output: 1640


5-5: Verify the footer partial and tool assignment

The footer rendering follows Phoenix conventions. Let's verify the implementation details.

✅ Verification successful

Footer partial and tool assignment are properly implemented

The verification shows that:

  • The footer partial exists at the correct location and follows Phoenix conventions
  • The @tool assign is properly set in the website controller's comparison action
  • The footer template uses the @tool parameter to filter out the current tool from the comparison cards
  • The implementation follows a consistent pattern across all comparison pages

The code is working as intended with no issues to address.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the footer partial existence and tool assignment

# Check for footer partial
echo "Checking for footer partial:"
fd "_footer.html" backend/lib/azimutt_web/templates/

# Check for @tool assign in controllers
echo -e "\nChecking for @tool assign in controllers:"
rg -l "assign.*:tool" backend/lib/azimutt_web/controllers/

# Check tool usage in footer
echo -e "\nChecking how @tool is used in footer:"
rg "@tool" backend/lib/azimutt_web/templates/website/comparisons/_footer.html

Length of output: 2323


Script:

#!/bin/bash
# Let's verify both the footer partial and tool assignment with more specific checks

# Check footer partial content
echo "Checking footer partial content:"
cat backend/lib/azimutt_web/templates/website/comparisons/_footer.html.heex

# Check for tool assignment in website controller
echo -e "\nChecking tool assignment in website controller:"
rg "tool" -A 5 -B 5 backend/lib/azimutt_web/controllers/website_controller.ex

# Check navicat page specifically for tool assignment
echo -e "\nChecking navicat page for tool references:"
cat backend/lib/azimutt_web/templates/website/comparisons/navicat.html.heex

Length of output: 4434

backend/lib/azimutt_web/templates/website/comparisons/schemaspy.html.heex (2)

1-3: Verify the comparison_prose helper function implementation

The code looks correct, but we should verify the existence and implementation of the comparison_prose helper function to ensure proper content wrapping.

✅ Verification successful

The comparison_prose helper is properly implemented and used correctly

The verification shows that:

  • The helper is defined in backend/lib/azimutt_web/views/website_view.ex and renders the _prose.html template
  • The template provides proper styling with Tailwind's prose classes and consistent layout
  • It's consistently used across all comparison pages (drawsql, dbeaver, navicat, etc.)
  • The implementation correctly passes the block content via @inner_content
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for the comparison_prose function definition
ast-grep --pattern 'def comparison_prose do
  $$$
end'

# Search for other usages of comparison_prose in templates
rg -g '*.{heex,eex}' 'comparison_prose'

Length of output: 1184


Script:

#!/bin/bash
# Search for the comparison_prose function definition in view helpers
rg -g '*.ex' 'def comparison_prose' -A 5

# Search for any module that might contain the comparison_prose function
fd -e ex -x grep -l "comparison_prose"

Length of output: 835


Script:

#!/bin/bash
# Let's check the template being rendered
rg -g '*.{heex,html}' -l "_prose.html"

# And see its content
fd -g "*_prose.html*" -x cat {}

Length of output: 462


5-5: Verify footer partial and required assigns

The footer partial implementation looks correct, but we should verify:

  1. The existence of the footer partial template
  2. The required assigns (@conn and @tool) are properly defined
✅ Verification successful

Let me gather more information about the tool assignment in the controller.


Let me search for the controller and the tool assignment with a different approach.


Let me check the complete render call and the Azimutt module for tool definitions.


Based on the gathered information, I can now provide the final response:

The footer partial and required assigns are properly implemented

The verification confirms:

  • The footer partial exists at backend/lib/azimutt_web/templates/website/comparisons/_footer.html.heex
  • The footer partial uses @conn for route helpers and @tool to display related comparisons
  • The @tool assign is properly set in the comparison function of WebsiteController through the Azimutt.comparisons() function
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if the footer partial exists
fd "_footer.html" backend/lib/azimutt_web/templates

# Search for @tool assign definition in the controller
ast-grep --pattern 'def $_(conn, $$$) do
  $$$
  render($$$, tool: $_)
  $$$
end'

Length of output: 465


Script:

#!/bin/bash
# Search for the controller that renders this template
rg -l "schemaspy.html" backend/lib/azimutt_web/controllers/

# Check the content of the footer partial
cat backend/lib/azimutt_web/templates/website/comparisons/_footer.html.heex

# Search for any function that sets the tool variable in controllers
rg "tool = " backend/lib/azimutt_web/controllers/

Length of output: 2435


Script:

#!/bin/bash
# Search for schemaspy in controllers with context
rg -A 5 "schemaspy" backend/lib/azimutt_web/controllers/

# Search for render calls with "schemaspy" template
ast-grep --pattern 'render(conn, "schemaspy.html", $$$)'

# Look for the website controller which likely handles this
rg -A 10 "def comparison" backend/lib/azimutt_web/controllers/

Length of output: 2374


Script:

#!/bin/bash
# Get the complete render call for the comparison function
rg -A 15 'render\("comparisons/\#{t\.id}\.html"' backend/lib/azimutt_web/controllers/website_controller.ex

# Look for the Azimutt module that defines comparisons
fd "azimutt.ex" backend/lib/
cat $(fd "azimutt.ex" backend/lib/)

Length of output: 23273

backend/lib/azimutt_web/templates/website/docs/ai.html.heex (3)

1-1: LGTM! Header implementation follows best practices.

The header rendering correctly passes the connection and page context.


9-9: LGTM! Footer implementation includes proper navigation context.

The footer correctly implements pagination with prev/next navigation.


1-9: Verify template consistency with other documentation pages.

Let's ensure this template follows the same structure as other documentation pages and is properly integrated into the navigation flow.

✅ Verification successful

Template structure is consistent with documentation standards

The verification results show that the AI documentation page follows the same template structure as all other documentation pages in the codebase:

  • Uses the standard header/footer partials
  • Implements the doc_prose helper consistently
  • Includes proper navigation with prev/next links in the footer
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for consistent template structure across documentation pages

# Check for consistent header/footer usage
echo "Checking header/footer consistency across doc templates..."
rg -l 'render "docs/_header\.html"' backend/lib/azimutt_web/templates/website/docs/
rg -l 'render "docs/_footer\.html"' backend/lib/azimutt_web/templates/website/docs/

# Check for consistent doc_prose usage
echo "Checking doc_prose usage..."
rg "doc_prose" backend/lib/azimutt_web/templates/website/docs/

# Check for proper navigation linking
echo "Checking navigation structure..."
rg "prev:|next:" backend/lib/azimutt_web/templates/website/docs/

Length of output: 16651

backend/lib/azimutt_web/templates/website/docs/data-exploration.html.heex (1)

6-8: Verify the logical flow of documentation sections.

The reordering of sections (adding "Data statistics" and removing "AI query generation") changes the documentation flow. Ensure this new order aligns with the user's typical exploration journey through the application.

Consider organizing the sections based on:

  1. Basic to advanced features
  2. Most frequently used features
  3. Natural workflow progression
✅ Verification successful

Let me gather more context about the documentation structure and content to better assess the logical flow.


Let me check the index file to understand the overall documentation structure and navigation flow.


Let me check the menu structure and the content of schema-exploration and main-features files to understand the feature organization and typical user journey.


Based on the gathered information, I can now provide the final response:

The documentation reorganization appears logically sound

The changes follow a natural progression from basic data exploration (statistics) to more specific operations (SQL queries and navigation). The AI query generation feature has been appropriately moved to a dedicated AI documentation section (ai.html.heex), which better organizes advanced features by their domain.

Most documentation pages are marked as "Work in Progress", indicating an ongoing documentation restructuring effort. The current ordering in data-exploration.html.heex follows a logical flow from overview (statistics) to specific actions (queries and navigation).

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for references to these features in other documentation files
# to ensure consistent documentation structure

echo "Checking documentation structure and feature references..."

# Look for related documentation sections
rg -l "Data statistics|SQL query|Rows navigation|AI query" backend/lib/azimutt_web/templates/website/docs/

# Check for any orphaned references to AI query generation
rg "AI query generation" backend/

Length of output: 529


Script:

#!/bin/bash
# Check the content of both documentation files to understand the structure and flow
echo "Content of data-exploration.html.heex:"
cat backend/lib/azimutt_web/templates/website/docs/data-exploration.html.heex

echo -e "\nContent of ai.html.heex:"
cat backend/lib/azimutt_web/templates/website/docs/ai.html.heex

# Check if there's a documentation index or navigation file
echo -e "\nLooking for documentation index/navigation:"
fd -e heex . backend/lib/azimutt_web/templates/website/docs/ | grep -i "index\|nav\|sidebar"

Length of output: 1242


Script:

#!/bin/bash
# Check the documentation index and navigation structure
echo "Content of documentation index:"
cat backend/lib/azimutt_web/templates/website/docs/index.html.heex

# Check for any other documentation files to understand the complete structure
echo -e "\nAll documentation files:"
fd -e heex . backend/lib/azimutt_web/templates/website/docs/ -x echo "{}"

Length of output: 4199


Script:

#!/bin/bash
# Check the menu structure and related feature documentation
echo "Content of _menu.html.heex:"
cat backend/lib/azimutt_web/templates/website/docs/_menu.html.heex

echo -e "\nContent of schema-exploration.html.heex:"
cat backend/lib/azimutt_web/templates/website/docs/schema-exploration.html.heex

echo -e "\nContent of main-features.html.heex:"
cat backend/lib/azimutt_web/templates/website/docs/main-features.html.heex

Length of output: 1072

backend/lib/azimutt_web/templates/website/connectors/_footer.html.heex (1)

1-1: LGTM: Layout container width increase

The increased max-width provides better content display space for the new 4-column layout while maintaining proper responsive padding.

backend/lib/azimutt_web/templates/website/connectors/_card.html.heex (1)

10-10: Verify HTML escaping for connector description

The direct use of @connector.description looks cleaner than using a partial. However, ensure that proper HTML escaping is in place to prevent XSS vulnerabilities.

✅ Verification successful

Based on the results, I can now generate the final response since we have found that:

  1. Phoenix.HTML is properly configured in the application
  2. The framework automatically escapes HTML content by default unless explicitly marked as safe
  3. The direct use of @connector.description in a template will be automatically HTML escaped by Phoenix.HTML

HTML escaping is properly handled

The direct use of @connector.description is safe as Phoenix.HTML automatically escapes all content in templates by default. This is a built-in security feature of the framework that prevents XSS vulnerabilities without requiring explicit escaping.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for HTML escaping configuration or sanitization of @connector.description
rg -l "connector.*description" --type elixir

# Look for the connector schema/struct definition to verify field type
ast-grep --pattern 'schema "connectors" do
  $$$
  field :description, $_
  $$$
end'

Length of output: 133


Script:

#!/bin/bash
# Search for connector description with more context
rg "description" --type elixir -B2 -A2

# Look for schema definition with a broader pattern
ast-grep --pattern 'schema $_ do
  $$$
  field :description, $_
  $$$
end'

# Search for HTML escaping configuration in Phoenix
rg "html_escape|safe|raw|sanitize" --type elixir

# Look for connector struct or type definition
rg "connector" --type elixir -B2 -A2

Length of output: 87385


Script:

#!/bin/bash
# Look for HTML escaping in Phoenix configuration
rg "html_escape|safe|raw|sanitize" --type heex

# Look for the Phoenix HTML configuration
rg "Phoenix.HTML" --type elixir

# Look for the connector card template usage
rg -B2 -A2 "_card.html.heex" --type heex

Length of output: 1427

backend/lib/azimutt_web/templates/website/comparisons/_card.html.heex (2)

10-10: Verify content sanitization for tool description

Ensure that the tool description is properly sanitized to prevent XSS attacks. While HEEx automatically escapes content by default, it's good practice to explicitly document this security consideration.


2-2: Verify the existence of all tool icons

The image path is dynamically generated using the tool ID. We should verify that all required icons exist in the static assets directory.

✅ Verification successful

Let me try a different approach to find the tool definitions and verify the images.


Let me try one more approach focusing on finding the tool definitions and checking the assets directory structure.


All tool icons are present and follow the naming convention

Based on the verification results, I can confirm that:

  • All tool IDs defined in backend/lib/azimutt.ex have corresponding icon files in backend/priv/static/images/comparisons/
  • The naming convention {tool.id}-icon.png is consistently followed
  • The icons exist for all comparison tools: dbdiagram, drawsql, drawdb, chartdb, dbml, dbeaver, datagrip, navicat, beekeeperstudio, schemaspy, amundsen, atlan, datadog, and metabase
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if all tool icons exist in the static assets directory

# First, find all tool IDs referenced in the codebase
echo "Looking for tool definitions..."
rg -g '!*.html*' -g '!*.md' -A 5 'tool\.id' 

# Then, check if corresponding images exist
echo "Checking for image files..."
fd -p "priv/static/images/comparisons/*-icon.png"

Length of output: 882


Script:

#!/bin/bash
# First, let's find files that might contain tool definitions
echo "Looking for files with tool definitions..."
fd -e ex -e exs | xargs rg -l "tool.*id"

# Then let's look at their content
echo -e "\nLooking at tool definitions content..."
fd -e ex -e exs | xargs rg "tool.*id.*=.*\".*\"" -A 2

# Finally, check for image files in possible locations
echo -e "\nChecking for image files..."
fd "icon.png" -p "priv/static"

Length of output: 483


Script:

#!/bin/bash
# First, let's check the directory structure to find where static assets are stored
echo "Checking directory structure..."
fd -t d "static|public|assets" --max-depth 4

# Look for tool definitions in all files
echo -e "\nLooking for tool definitions..."
rg -g '!*.html*' -g '!*.md' "id:.*\".*\".*name:.*\".*\"" -B 1 -A 3

# Check for any image files related to comparisons
echo -e "\nLooking for comparison related images..."
fd -e png -e jpg -e jpeg -e svg | rg -i "compar|tool"

Length of output: 10947

backend/lib/azimutt_web/controllers/gallery_controller.ex (1)

26-26: Good improvement using Routes.static_url!

Using Routes.static_url is the correct approach for generating static asset URLs in Phoenix. It's more robust than string concatenation and handles different environments properly.

Let's verify the URL generation across different environments:

✅ Verification successful

Using Routes.static_url is correct and safe

The usage of Routes.static_url in the gallery controller is consistent with its usage across the application for generating static asset URLs. It's used in multiple controllers (website, blog, gallery) specifically for SEO-related image URLs, which is exactly the intended use case.

The function ensures proper URL generation by:

  • Handling host configuration automatically
  • Generating absolute URLs required for SEO/social media cards
  • Following Phoenix best practices for static asset handling
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check static URL configuration across environments
# Expected: Each environment should have proper host configuration for static assets

# Check config files for static URL configuration
echo "Checking static URL configuration:"
rg -A 3 "static_url|url" config/

Length of output: 161


Script:

#!/bin/bash
# Let's try to find configuration files in the repository
echo "Finding configuration files:"
fd -t f "config.exs|prod.exs|dev.exs|test.exs"

echo -e "\nChecking URL configurations in these files:"
fd -t f "config.exs|prod.exs|dev.exs|test.exs" --exec rg -l "url|host|endpoint"

echo -e "\nChecking actual configurations:"
fd -t f "config.exs|prod.exs|dev.exs|test.exs" --exec cat {}

Length of output: 106054


Script:

#!/bin/bash
# Let's check the endpoint configuration in the config files
echo "Checking endpoint configuration in config files:"
fd -t f "config.exs|prod.exs|dev.exs|test.exs" -x grep -l "endpoint" {} \; -exec echo "=== {}" \; -exec grep -A 5 "endpoint" {} \;

echo -e "\nChecking static URL configuration in endpoint:"
fd -t f "config.exs|prod.exs|dev.exs|test.exs" -x grep -l "static_url" {} \; -exec echo "=== {}" \; -exec grep -A 5 "static_url" {} \;

echo -e "\nChecking Routes module usage:"
rg "Routes.static_url" -A 2

Length of output: 4154

backend/lib/azimutt_web/views/website_view.ex (1)

28-31: Well-structured template rendering functions!

The new functions follow established patterns and best practices:

  • Consistent with existing module structure
  • Clear naming that reflects their purpose
  • Proper use of default arguments and block syntax
  • Maintains module cohesion with related functionality
backend/assets/js/aml.hljs.ts (1)

23-23: LGTM! Good refactoring to improve maintainability.

Moving the regex patterns to named constants at the top of the file improves code maintainability and reusability.

backend/assets/js/dbml.hljs.ts (2)

1-1: LGTM: Clean and precise imports

The imports are correctly typed and specifically import only the needed types from highlight.js.


17-17: Address commented code and FIXME

There are two issues that need attention:

  1. Commented-out rule for entity title highlighting
  2. FIXME comment for attribute type highlighting

These should either be implemented or removed to maintain clean code.

Would you like help implementing these rules correctly?

Also applies to: 19-19

backend/lib/azimutt_web/controllers/blog_controller.ex (2)

14-14: LGTM! Clear and concise SEO description.

The description effectively communicates Azimutt's value proposition while maintaining good SEO practices.


29-29: Good improvement using Routes.static_url!

Using Routes.static_url instead of URL concatenation is the correct approach for generating static asset URLs in Phoenix. This ensures proper URL generation regardless of the application's mounted path or hostname configuration.

Let's verify that all article banner paths are compatible with static_url:

✅ Verification successful

Let me check the actual banner paths in the markdown files to ensure they are properly formatted.


Let me try a different approach to check the banner paths.


Let me check the content of one of the markdown files to verify the banner format.


Based on the evidence gathered, I can now provide the final response:

The use of Routes.static_url is correct and compatible with banner paths

From the code analysis, we can see that:

  • Banner paths in articles use {{base_link}} placeholder which is properly replaced by Markdown.base_link(path) in Article.ex
  • All banner images are correctly stored under /priv/static/blog/ with proper relative paths
  • The final paths start with "/" as required by Routes.static_url
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if article banner paths are properly formatted for static_url
# Expected format: Should start with "/" and be relative to the static directory

# Search for banner definitions in article files
rg -g '*.{ex,exs}' 'banner:.*' -A 2

Length of output: 1104


Script:

#!/bin/bash
# Check the actual banner paths in markdown files
fd -e md -e mdx . backend/priv/static/blog/ -x grep -l "banner:"  {} \; -exec cat {} \; | grep -A 1 "banner:"

Length of output: 259


Script:

#!/bin/bash
# First find markdown files
fd -e md -e mdx . backend/priv/static/blog/

# Also check if the static directory exists and list its contents
ls -R backend/priv/static/

Length of output: 13358


Script:

#!/bin/bash
# Check the content of a recent blog post to see banner definition
cat "backend/priv/static/blog/2024-11-06-what-is-a-polymorphic-relation/what-is-a-polymorphic-relation.md" | head -n 10

Length of output: 714

backend/assets/js/app.ts (1)

32-32: Verify DBML language definition file exists

Let's ensure the DBML language definition file is present in the codebase.

✅ Verification successful

DBML language definition file is present and properly implemented

The DBML language definition file exists at the expected location and contains a well-structured implementation following the highlight.js language definition guidelines. The file includes:

  • Proper type imports from highlight.js
  • Comprehensive regex patterns for DBML syntax elements
  • Complete language configuration with appropriate token scopes
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the existence and content of the DBML language definition file

# Check if the file exists and show its content
fd --type f "dbml.hljs.ts" --exec cat {}

Length of output: 1410

backend/lib/azimutt_web/templates/website/converters/converter.html.heex (1)

10-10: LGTM! Proper usage of Routes.static_path

The image source is correctly using Routes.static_path/2 for dynamic path generation, and includes appropriate alt text for accessibility.

backend/config/config.exs (2)

14-14: LGTM! SEO keywords are well-structured.

The consolidated keywords accurately represent the application's purpose and features, with proper comma separation and no duplicates.


20-20: Verify the documentation URL change impact.

The documentation URL has changed from docs.azimutt.app to azimutt.app/docs. Please ensure that:

  1. Proper redirects are in place for existing links
  2. All documentation references in the codebase are updated
✅ Verification successful

No impact found from documentation URL change

The search results confirm that:

  1. There are no references to the old URL docs.azimutt.app in the codebase
  2. All documentation links using the new format azimutt.app/docs are already up to date

The change appears to be already consistently implemented across the codebase with no remaining references to update.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Search for old documentation URL references that need updating

echo "Searching for old documentation URL references..."
rg -i "docs\.azimutt\.app"

echo "Checking for potential documentation links that might need updates..."
rg -i "documentation.*https?://"

Length of output: 5508

backend/lib/azimutt_web/views/layout_view.ex (3)

26-29: LGTM! Consistent type safety improvements.

The addition of is_binary guard clauses across these SEO-related functions improves type safety in a consistent manner.

Also applies to: 35-35


32-32: Verify the impact of switching to static_url for canonical URLs.

The change from Routes.url(conn) <> request_path to Routes.static_url(conn, request_path) could affect SEO if canonical URLs change. Please ensure:

  1. All canonical URLs remain valid
  2. No duplicate content issues are introduced
  3. SEO rankings are preserved
✅ Verification successful

Based on the results, I can now provide a final response:

The canonical URL change appears to be safe and consistent

The codebase scan reveals:

  • The canonical URL implementation exists only in layout_view.ex with no other conflicting implementations
  • The function has a fallback clause that uses AzimuttWeb.Endpoint.url() for cases without request_path
  • No other URL generation patterns using request_path were found elsewhere in the codebase
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other URL generation patterns to ensure consistency
rg -l 'Routes\.(url|static_url).*request_path' --type elixir

# Look for potential canonical URL implementations
ast-grep --pattern 'def canonical($$$)'

Length of output: 1068


39-40: Verify the default Open Graph image asset.

While the guard clause addition is good, please ensure:

  1. The default image /images/og/azimutt.jpg exists
  2. The image follows Open Graph recommendations (2:1 ratio, optimal resolution)
backend/lib/azimutt_web/templates/website/comparisons/index.html.heex (1)

66-66: Verify footer partial existence

The footer partial is included correctly, but let's verify its existence.

✅ Verification successful

Footer partial exists and is correctly used

The footer partial is correctly included and exists in the expected location. The file backend/lib/azimutt_web/templates/website/comparisons/_footer.html.heex contains a comparison-specific footer section that also includes the base footer partial (_footer.html), creating a proper footer hierarchy. Both files are properly structured and contain valid content.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if the footer partial exists
fd --type f "_footer.html" backend/lib/azimutt_web/templates/website/

Length of output: 337


Script:

#!/bin/bash
# Check the content of the footer partial in the same directory
cat backend/lib/azimutt_web/templates/website/comparisons/_footer.html.heex

Length of output: 2323


Script:

#!/bin/bash
# Check the content of the base footer partial
cat backend/lib/azimutt_web/templates/website/_footer.html.heex

Length of output: 9982

backend/lib/azimutt_web/templates/website/use-cases/analyze.html.heex (1)

7-7: LGTM! Verify @use_case assignment.

The dynamic title implementation looks good and follows proper HEEx syntax.

Let's verify the @use_case assignment is properly handled:

✅ Verification successful

LGTM! @use_case assignment is properly handled.

The verification shows:

  • The use_case is correctly assigned in website_controller.ex through the use_cases_show action
  • The @use_case.title is consistently used across all use case templates (analyze, document, explore, design)
  • The assignment includes proper error handling using Result.map
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if @use_case is properly assigned in the controller and other templates
# Expected: Find controller action assigning @use_case and consistent usage across templates

# Find the controller assigning @use_case
rg -t ex "use_case" backend/lib/azimutt_web/controllers/

# Check consistent usage in other templates
rg -t heex "use_case\.title" backend/lib/azimutt_web/templates/

Length of output: 178


Script:

#!/bin/bash
# Search for use_case assignments in controller files (without file type filter)
rg "use_case" backend/lib/azimutt_web/controllers/

# Search for use_case.title usage in template files (without file type filter)
rg "use_case\.title" backend/lib/azimutt_web/templates/

# List all files in controllers and templates to understand structure
find backend/lib/azimutt_web/controllers/ -type f
find backend/lib/azimutt_web/templates/ -type f

Length of output: 20110

backend/lib/azimutt_web/templates/website/use-cases/document.html.heex (1)

Line range hint 108-108: Verify if iframe attributes should be dynamic.

The iframe contains hardcoded values for project-id, layout, and token. Consider if these should be dynamic values passed through the template context.

✅ Verification successful

Based on the search results, I can generate the final response as I have enough information to make a determination:

Hardcoded iframe values are intentional for documentation and blog posts

The project ID, layout, and token values are consistently used across blog posts and documentation pages, serving as demo/showcase examples. This is evident from:

  • Same project ID used in multiple blog posts about documentation and ERD features
  • Similar iframe embeds found in blog posts with different tokens for different showcase purposes
  • Alternative embedding methods using project-url and sql-source parameters seen in other examples
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if these values are used elsewhere or defined in configuration

# Search for the project ID
echo "Searching for project ID 'adbe859a-08c4-4386-9af3-be165b30e000'..."
rg "adbe859a-08c4-4386-9af3-be165b30e000"

# Search for the token
echo "Searching for token 'ec6ef23d-cd86-4521-955a-2ea1aedbf758'..."
rg "ec6ef23d-cd86-4521-955a-2ea1aedbf758"

# Search for similar iframe embeds
echo "Searching for similar iframe embeds..."
rg 'src="https://azimutt.app/embed\?'

Length of output: 5756

backend/lib/azimutt_web/templates/website/use-cases/design.html.heex (1)

7-7: Verify @use_case assignment in the controller.

The change to use dynamic titles is good, but we should ensure that @use_case is always properly assigned in the controller to avoid potential runtime errors.

✅ Verification successful

Let me search for the correct controller that renders this use case template.


Let me check where these use cases are defined to ensure they have the required title field.


Let me check where the use cases are defined in the codebase.


Based on the evidence gathered, I can now provide the final response:

@use_case is properly assigned and safe to use

The @use_case variable is properly assigned in the controller through Azimutt.showcase_usages(), which defines a static list of use cases with all required fields including title. The template will always receive a valid use case since the controller uses pattern matching and Result type to handle the case when the use case is not found.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if @use_case is properly assigned in the controller
# Expected: Find the controller action that renders this template and verify the assignment

# Find the controller
rg -A 10 "def.*design" backend/lib/azimutt_web/controllers/

Length of output: 1557


Script:

#!/bin/bash
# Find the controller that renders the use-cases/design template
rg -l "use-cases/design" backend/lib/azimutt_web/controllers/

# Also search for any controller with 'use_case' assignment
rg "use_case" backend/lib/azimutt_web/controllers/

Length of output: 786


Script:

#!/bin/bash
# Find where use cases are defined
rg -A 10 "use_cases" backend/lib/azimutt_web/

# Also look for any struct or type definition related to use cases
ast-grep --pattern "defmodule $_ do
  $$$
  defstruct $$$
  $$$
end"

Length of output: 22565


Script:

#!/bin/bash
# Find where showcase_usages is defined
rg -A 10 "showcase_usages" backend/lib/

Length of output: 11003

backend/lib/azimutt_web/templates/website/_footer.html.heex (2)

42-42: LGTM: Well-structured addition of Comparisons link

The new Comparisons link is properly integrated into the Solutions section, following consistent styling and accessibility patterns.


64-64: LGTM: Improved UX by opening Documentation in same tab

Good change to remove target="_blank" from the Documentation link. Opening documentation in the same tab is better UX as it's a core part of the site navigation. This follows web best practices of not forcing new tabs unless necessary (like external sites).

backend/lib/azimutt_web/templates/website/converters/_editors-script.html.heex (1)

108-108: Verify JSON format compatibility after switching libraries.

The change from aml.generateJsonDatabase to sql.generateJsonDatabase might affect the JSON output format. Ensure that:

  1. The output JSON structure remains compatible with downstream consumers
  2. The schema validation still works with aml.schemaJsonDatabase (used in Monaco editor setup)

Let's verify the JSON schema compatibility:

✅ Verification successful

Based on the search results, I can now provide a conclusive response:

No compatibility issues with JSON format switch

The change from aml.generateJsonDatabase to sql.generateJsonDatabase is safe because:

  1. Both libraries import the same JSON schema and generator from @azimutt/models package
  2. The Monaco editor validation uses aml.schemaJsonDatabase which is also imported from @azimutt/models
  3. All parsers (SQL, AML, DBML, Prisma) use the same shared JSON schema and generator functions

The change only affects which module exposes the shared functionality, not the actual JSON format or schema validation.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if the JSON schema validation is compatible with both libraries

# Search for JSON schema usage and validation
rg -A 5 'schemaJsonDatabase|parseJsonDatabase|generateJsonDatabase'

# Search for any JSON-related tests that might need updates
fd -e test.js -e test.ts -e spec.js -e spec.ts | xargs rg 'generateJsonDatabase'

Length of output: 19466

backend/lib/azimutt_web/templates/website/use-cases/explore.html.heex (1)

7-7: Consider adding HTML escaping for the dynamic title.

While the dynamic title implementation looks good, ensure proper HTML escaping is applied to prevent potential XSS vulnerabilities.

Let's verify the controller implementation and HTML escaping:

Consider using Phoenix's HTML escaping helpers:

-<span class="mt-2 block text-center text-3xl font-bold leading-8 tracking-tight text-gray-900 sm:text-4xl"><%= @use_case.title %></span>
+<span class="mt-2 block text-center text-3xl font-bold leading-8 tracking-tight text-gray-900 sm:text-4xl"><%= Phoenix.HTML.html_escape(@use_case.title) %></span>
backend/lib/azimutt_web/templates/website/comparisons/dbml.html.heex (4)

41-151: LGTM! Well-structured syntax comparison

The side-by-side comparison effectively demonstrates the differences between DBML and AML. The iterative schema development example is particularly helpful in showing AML's flexibility and progressive enhancement approach.


250-254: LGTM! Well-implemented interactive editor

The responsive layout for the Monaco editors is well-implemented using Tailwind classes, and the configuration is properly modularized using a partial.


262-277: LGTM! Effective conclusion

The conclusion effectively summarizes the comparison while maintaining an engaging tone and encouraging user feedback.


279-279: LGTM! Clean footer implementation

The footer is properly modularized using a partial and correctly passes the required variables.

backend/lib/azimutt_web/router.ex (3)

45-54: LGTM! Routes are well organized

The routes are logically grouped and follow a consistent pattern:

  • Index routes (/use-cases, /features)
  • Show routes for individual items (/use-cases/:use_case_id, /features/:feature_id)

77-78: Verify WebsiteController implementation for portal routes

The portal routes have been reintroduced. Ensure that the corresponding controller actions are implemented:

  • :portal for the main portal page
  • :portal_subscribed for the subscription confirmation page
#!/bin/bash
# Check if the portal controller actions are implemented
ast-grep --pattern 'def portal(conn, _params) do
  $$$
end

def portal_subscribed(conn, _params) do
  $$$
end' backend/lib/azimutt_web/controllers/website_controller.ex

61-63: Verify WebsiteController implementation for comparison routes

The new comparison routes follow RESTful conventions. However, ensure that the corresponding controller actions are implemented:

  • :comparisons for the index page
  • :comparison for individual tool comparisons
✅ Verification successful

Controller actions are properly implemented

The required controller actions for the comparison routes are correctly implemented in WebsiteController:

  • comparisons/2 action renders the index page with all comparison categories
  • comparison/2 action handles individual tool comparisons with category and tool parameters
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if the controller actions are implemented
ast-grep --pattern 'def comparisons(conn, _params) do
  $$$
end

def comparison(conn, %{"category" => $_, "tool" => $_}) do
  $$$
end' backend/lib/azimutt_web/controllers/website_controller.ex

Length of output: 274


Script:

#!/bin/bash
# Let's try a different approach to check the controller implementation
rg -A 5 "def comparisons|def comparison" backend/lib/azimutt_web/controllers/website_controller.ex

Length of output: 725

backend/lib/azimutt_web/templates/website/comparisons/dbdiagram.html.heex (1)

1-235: Overall template structure looks good!

The template is well-structured, uses semantic HTML, and properly implements HEEx components. The content is comprehensive and organized logically.

🧰 Tools
🪛 Gitleaks

231-231: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

backend/lib/azimutt.ex (2)

170-170: LGTM: Enhanced usage descriptions with clear titles

The added titles improve clarity and SEO by providing specific context for each use case.

Also applies to: 178-178, 186-186, 194-194


475-475: LGTM: Well-placed AI integration documentation entry

The AI integration documentation entry is appropriately added to the main features section.

backend/lib/azimutt_web/templates/website/index.html.heex (1)

88-88: LGTM! Improved alt text for better accessibility.

The updated alt text "E-commerce database demo" is more descriptive and better explains the image content, improving both accessibility and SEO.

libs/parser-sql/src/postgresBuilder.ts (2)

173-173: LGTM! Property order change prevents potential overrides.

The changes ensure that the statement property is not accidentally overridden by any properties from the spread operation. This is a good defensive programming practice.

Also applies to: 179-179


173-173: Verify that spread operation doesn't contain conflicting properties.

Let's ensure that the removeFieldsDeep(stmt, ['token']) operation doesn't produce objects with a statement property that could conflict with our index.

Also applies to: 179-179

✅ Verification successful

Based on the analysis of the code, I can now provide the final response:

Property ordering in spread operation is safe.

The removeFieldsDeep function recursively removes specified fields (in this case 'token') from objects and arrays, returning a new object. The spread operation with the statement: index property is safe because:

  1. The function only removes the specified fields and doesn't add any new ones
  2. The statement property is consistently used as a simple number across the codebase for tracking statement indices
  3. No evidence found of the removed 'token' object containing a conflicting 'statement' property
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Search for any AST definitions that might contain a 'statement' property
# This helps verify that our property ordering change is necessary and won't conflict

# Look for AST type definitions that might contain 'statement' property
ast-grep --pattern 'type $name = {
  $$$
  statement: $_
  $$$
}'

# Also check for interface definitions
ast-grep --pattern 'interface $name {
  $$$
  statement: $_
  $$$
}'

Length of output: 138


Script:

#!/bin/bash
# Let's try a different approach to find any usage or definition of 'statement' property
# First, let's see the structure of 'stmt' object that's being processed

# Find the type/interface of stmt parameter
ast-grep --pattern 'function $name($_: $type) {
  $$$
  removeFieldsDeep(stmt, $_)
  $$$
}'

# Search for any direct references to statement property
rg "statement:" -A 2 -B 2

# Look at the implementation of removeFieldsDeep to understand what it returns
ast-grep --pattern 'function removeFieldsDeep'

Length of output: 45017


Script:

#!/bin/bash
# Let's look at the implementation of removeFieldsDeep and any similar functions
rg -A 5 "function removeFieldsDeep|function removeUndefined" 

# Also check if there are any other usages of statement property in objects
rg "statement:" -B 3 -A 1 --no-filename | sort -u

Length of output: 25163

backend/lib/azimutt_web/controllers/website_controller.ex (6)

32-34: LGTM!

The SEO metadata for the aml/2 action has been correctly updated.


39-40: LGTM!

The pricing/2 action is correctly implemented and includes the necessary SEO metadata.


43-50: LGTM!

The use_cases_show/2 action properly fetches the use case and renders the template with the correct SEO metadata.


64-65: Prevent out-of-bounds access when getting the next feature

When accessing features |> Enum.at(index + 1), if index + 1 is equal to or greater than the length of the features list, Enum.at/2 will return nil. Ensure this is the desired behavior or consider adding bounds checking to prevent potential errors.

As a follow-up, please verify that the template correctly handles next: nil when there is no next feature.


Line range hint 81-94: LGTM!

The connector/2 action correctly retrieves the connector and renders the template with the appropriate SEO metadata.


99-107: LGTM!

The converters/2 action is properly implemented with the correct SEO metadata.

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: 5

🧹 Outside diff range and nitpick comments (11)
backend/lib/azimutt_web/templates/layout/_head_seo.html.heex (1)

58-67: Enhance Organization schema with additional properties and dynamic URLs.

The Organization schema could be improved with more properties and configurable URLs.

Consider applying these changes:

 <script type="application/ld+json">
   {
     "@context": "https://schema.org",
     "@type": "Organization",
     "name": "Azimutt",
-    "url": "https://azimutt.app",
-    "logo": "https://azimutt.app/images/logo_dark.svg"
+    "url": "<%= Routes.static_url(@conn, "/") %>",
+    "logo": "<%= Routes.static_url(@conn, "/images/logo_dark.svg") %>",
+    "sameAs": [
+      "https://github.com/azimuttapp",
+      "https://twitter.com/azimuttapp"
+    ],
+    "description": "Entity Relationship Diagram made for real world applications"
   }
 </script>
backend/lib/azimutt_web/controllers/website_controller.ex (3)

43-50: Consider improving error handling and code organization

While the implementation is functional, consider these improvements:

  1. Extract the use case lookup logic into a separate private function
  2. Add explicit error handling for the case when use_case_id is not found
+ defp find_use_case(id) do
+   Azimutt.showcase_usages()
+   |> Enum.find(fn u -> u.id == id end)
+   |> Result.from_nillable()
+ end

  def use_cases_show(conn, %{"use_case_id" => use_case_id}) do
-   Azimutt.showcase_usages()
-   |> Enum.find(fn u -> u.id == use_case_id end)
-   |> Result.from_nillable()
+   find_use_case(use_case_id)
    |> Result.map(fn use_case ->
      conn |> render("use-cases/#{use_case_id}.html",
        use_case: use_case,
        seo: %{type: "article", title: use_case.title, description: use_case.description}
      )
+   end)
+   |> Result.or_else(
+     conn
+     |> put_status(:not_found)
+     |> render("404.html")
    )
  end

218-218: Consider extracting page title construction

The page title construction logic could be moved to a separate function for better maintainability.

+ defp build_doc_page_title(page) do
+   "Azimutt documentation > " <> (page.parents |> Enum.map_join("", fn p -> p.name <> " > " end)) <> page.name
+ end

- title: "Azimutt documentation > " <> (page.parents |> Enum.map_join("", fn p -> p.name <> " > " end)) <> page.name
+ title: build_doc_page_title(page)

Line range hint 1-245: Consider introducing SEO and lookup service modules

The controller has grown significantly with repeated patterns for SEO metadata and entity lookups. Consider extracting these into dedicated service modules:

  1. AzimuttWeb.SEOService - for building SEO metadata
  2. Azimutt.LookupService - for finding features, use cases, comparisons, etc.

This would improve maintainability and reduce duplication.

backend/lib/azimutt_web/templates/website/comparisons/dbml.html.heex (3)

43-91: Enhance accessibility for code comparison section.

Consider adding ARIA attributes and language indicators for better screen reader support:

-<div class="flex gap-x-3 gap-y-5 flex-col sm:flex-row">
+<div class="flex gap-x-3 gap-y-5 flex-col sm:flex-row" role="region" aria-label="Code comparison">
-        <div class="grow">
+        <div class="grow" role="presentation">
-            <pre class="mb-0"><code class="hljs dbml">
+            <pre class="mb-0"><code class="hljs dbml" aria-label="DBML code example">

159-176: Improve table accessibility for feature comparison.

The table needs proper accessibility attributes for better screen reader support:

-<table>
+<table aria-label="Feature comparison between DBML and AML">
     <thead>
-        <tr><th></th><th>DBML</th><th>AML</th></tr>
+        <tr>
+            <th scope="col">Feature</th>
+            <th scope="col">DBML</th>
+            <th scope="col">AML</th>
+        </tr>
     </thead>
     <tbody>
-        <tr><th>Table definition</th>
+        <tr><th scope="row">Table definition</th>

204-209: Standardize error handling in code examples.

The DBML example lacks error handling while the AML example includes it. Consider making the error handling consistent across examples:

 const parser = new Parser()
 const dbml = 'Table users {\n  id int [pk]\n  name varchar\n}\n' // your DBML script
-const database = parser.parse(dbml, 'dbml')
-console.log('database', database)
+try {
+    const database = parser.parse(dbml, 'dbml')
+    console.log('database', database)
+} catch (e) {
+    console.error('parsing error', e.message)
+}

Also applies to: 221-230

backend/lib/azimutt_web/templates/website/comparisons/dbdiagram.html.heex (4)

11-11: Enhance image accessibility with descriptive alt text

The alt text "dbdiagram.io vs Azimutt" could be more descriptive to better serve users relying on screen readers.

-<img src={@seo.image} alt="dbdiagram.io vs Azimutt">
+<img src={@seo.image} alt="Feature comparison between dbdiagram.io and Azimutt database visualization tools">

23-45: Enhance table semantics for better accessibility

The comparison table could benefit from improved semantic markup:

  1. Add scope="col" to header cells
  2. Add scope="row" to row header cells
  3. Use more semantic symbols for feature support
 <table>
-    <thead><tr><th></th><th>dbdiagram.io</th><th>Azimutt</th></tr></thead>
+    <thead><tr><th scope="col"></th><th scope="col">dbdiagram.io</th><th scope="col">Azimutt</th></tr></thead>
     <tbody>
-    <tr><th>Visual schema exploration</th><td>✅ Yes</td><td>✅ Yes</td></tr>
+    <tr><th scope="row">Visual schema exploration</th><td><span aria-label="Supported">✅ Yes</span></td><td><span aria-label="Supported">✅ Yes</span></td></tr>

100-156: Add copy buttons to code examples

Consider adding copy buttons to the code examples to improve user experience. This would allow users to easily try out both syntaxes.


214-214: Track TODO comment in issue tracker

The TODO comment about adding testimonials should be tracked in the issue tracker for better visibility and follow-up.

Would you like me to create a GitHub issue to track this enhancement?

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 8383952 and 539d10c.

📒 Files selected for processing (7)
  • backend/assets/js/dbml.hljs.ts (1 hunks)
  • backend/lib/azimutt.ex (4 hunks)
  • backend/lib/azimutt_web/controllers/website_controller.ex (5 hunks)
  • backend/lib/azimutt_web/templates/layout/_head_seo.html.heex (1 hunks)
  • backend/lib/azimutt_web/templates/website/comparisons/dbdiagram.html.heex (1 hunks)
  • backend/lib/azimutt_web/templates/website/comparisons/dbml.html.heex (1 hunks)
  • backend/lib/azimutt_web/templates/website/comparisons/index.html.heex (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • backend/assets/js/dbml.hljs.ts
  • backend/lib/azimutt_web/templates/website/comparisons/index.html.heex
🧰 Additional context used
🪛 Gitleaks
backend/lib/azimutt_web/templates/website/comparisons/dbdiagram.html.heex

231-231: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

🔇 Additional comments (10)
backend/lib/azimutt_web/templates/layout/_head_seo.html.heex (1)

42-42: Verify SEO type assignment in controllers.

Let's ensure that the SEO type and required properties are properly set in the controllers.

✅ Verification successful

SEO implementation for articles is properly structured

The SEO implementation in _head_seo.html.heex follows best practices by:

  • Correctly implementing article schema markup using Schema.org
  • Including all necessary meta tags for social sharing (OpenGraph and Twitter)
  • Providing proper fallback to Organization schema when not an article
  • Using proper JSON-LD structure for structured data
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for SEO type assignments and required properties in controllers

# Look for SEO type assignments in controllers
echo "Checking for SEO type assignments in controllers..."
rg -t elixir "assigns\[:seo\].*type.*article" backend/lib/azimutt_web/controllers/

# Look for required article properties
echo "Checking for required article properties..."
rg -t elixir "assigns\[:seo\].*\b(published_at|updated_at|author)\b" backend/lib/azimutt_web/controllers/

Length of output: 402


Script:

#!/bin/bash
# Let's try a broader search to understand SEO assignments in controllers and templates

# Check for any SEO assignments in controllers
echo "Checking for SEO assignments in controllers..."
rg -t elixir "assigns\[:seo\]" backend/lib/azimutt_web/controllers/ -A 3 -B 1

# Check for article-related templates
echo "Checking for article-related templates..."
fd -e heex -e eex . backend/lib/azimutt_web/templates/ --exec grep -l "article"

# Check for SEO usage in templates
echo "Checking for SEO usage in templates..."
rg "@seo" backend/lib/azimutt_web/templates/ -A 2 -B 1

Length of output: 12951


Script:

#!/bin/bash
# Let's check specifically for blog and article-related SEO implementations

# Check blog controller for SEO assignments
echo "Checking blog controller for SEO assignments..."
rg -t elixir "assigns.*seo" backend/lib/azimutt_web/controllers/blog_controller.ex -A 3 -B 1

# Check the head_seo template implementation
echo "Checking head_seo template implementation..."
cat backend/lib/azimutt_web/templates/layout/_head_seo.html.heex

# Check for article schema implementation
echo "Checking for article schema implementation..."
rg -t heex "schema.org.*Article" backend/lib/azimutt_web/templates/

Length of output: 3395

backend/lib/azimutt_web/controllers/website_controller.ex (3)

32-34: LGTM: SEO metadata improvements for AML page

The SEO metadata changes provide clear and descriptive content for the AML page.


77-78: LGTM: Well-structured SEO improvements for connectors and converters

Good implementation of SEO metadata across connector and converter pages with consistent structure and meaningful descriptions.

Also applies to: 83-83, 92-96, 101-109, 115-125


241-245: LGTM: Simple and clear implementation for portal and legal pages

The implementation is appropriate for these static pages.

backend/lib/azimutt_web/templates/website/comparisons/dbml.html.heex (3)

1-28: LGTM! Well-structured introduction section.

The introduction effectively presents the topic with clear navigation and proper HTML structure.


262-277: LGTM! Effective conclusion with clear call-to-action.

The conclusion effectively summarizes the comparison and encourages user engagement.


250-254: 🛠️ Refactor suggestion

Add error handling and loading states for Monaco editor.

Consider adding error handling and loading states for better user experience:

 <div class="my-5 mx-auto flex flex-col md:flex-row space-y-8 md:space-y-0 md:space-x-8 justify-center w-full max-w-7xl">
+    <div id="editor-loading" class="text-center hidden">Loading editor...</div>
+    <div id="editor-error" class="text-red-500 text-center hidden">Failed to load editor</div>
     <div id="monaco-editor-left" class="relative w-full md:w-1/2 h-96 border border-gray-300 rounded-lg overflow-hidden"></div>
     <div id="monaco-editor-right" class="relative w-full md:w-1/2 h-96 border border-gray-300 rounded-lg overflow-hidden"></div>
 </div>
backend/lib/azimutt.ex (3)

170-170: LGTM! Enhanced clarity with descriptive titles

The addition of descriptive titles to each usage entry improves clarity and user experience by providing better context about each feature's purpose.

Also applies to: 178-178, 186-186, 194-194


319-327: LGTM! Improved connector descriptions and standardized fields

The changes have:

  • Standardized the connector fields by removing the inconsistent date field
  • Added detailed and informative descriptions for each database connector
  • Included appropriate brand colors for visual identification

351-417: Verify the status of commented tool entries

The new comparison structure is well-organized, but there are many commented-out tool entries. Consider:

  1. Adding a TODO comment if these are planned for future implementation
  2. Removing them if they're not planned for implementation
  3. Adding documentation about the criteria for including tools in comparisons
✅ Verification successful

Let me try another search strategy to find any documentation or comments about these tool comparisons.


Based on the search results, I can now provide a final response as there's clear evidence about the comparison feature's implementation:

Commented tools are intentionally disabled for now

The commented tools in the comparison list are part of a structured website feature for tool comparisons (/vs route). Only tools with complete comparison pages (like dbdiagram.io and DBML) are currently enabled, while others are intentionally commented out until their comparison pages are implemented.

Evidence:

  • The codebase has a dedicated comparison system with routes, templates, and content
  • Active comparisons have detailed pages (eg: comparisons/dbdiagram.html.heex, comparisons/dbml.html.heex)
  • The comparison feature is actively used in the website navigation and sitemap

No action needed as this is an intentional implementation detail rather than an oversight.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if there's any documentation about tool comparison criteria
fd README.md | xargs rg -l "tool comparison|comparison criteria"

# Check if there are any related TODO items
rg -i "todo.*tool comparison" 

Length of output: 212


Script:

#!/bin/bash
# Look for any documentation files in docs or similar directories
fd -t f "(README|CONTRIBUTING|docs/|doc/)" -E "node_modules"

# Search for any comments or documentation about comparisons
rg "comparison" -g "!*.{json,lock}" -A 3

# Check git history for any context about these commented tools
git log -p -- backend/lib/azimutt.ex | rg -A 5 -B 5 "comparisons"

Length of output: 28171

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: 4

🧹 Outside diff range and nitpick comments (8)
backend/lib/azimutt_web/views/layout_view.ex (4)

25-26: Consider enforcing title length constraints and improving readability.

While the logic is correct, consider these improvements:

  1. The title length constraint (55-70 chars) mentioned in the comment isn't enforced
  2. The conditional concatenation could be more readable

Consider this refactor:

-  def og_title(%{assigns: %{seo: %{title: title}}}) when is_binary(title), do: title <> if(title |> String.contains?("Azimutt"), do: "", else: " · Azimutt")
+  def og_title(%{assigns: %{seo: %{title: title}}}) when is_binary(title) do
+    suffix = if String.contains?(title, "Azimutt"), do: "", else: " · Azimutt"
+    full_title = title <> suffix
+    String.slice(full_title, 0, 70)
+  end

51-58: Add documentation and improve Twitter handle extraction.

The Twitter card functions would benefit from:

  1. Documentation explaining the expected format and values
  2. More robust Twitter handle extraction

Consider adding documentation and improving the implementation:

+  @doc """
+  Returns the Twitter card type.
+  Possible values: "summary", "summary_large_image", "app", or "player"
+  """
   def og_twitter_card(%{assigns: %{seo: %{card: card}}}), do: card
   def og_twitter_card(_conn), do: "summary_large_image"

+  @doc """
+  Returns the Twitter handle of the content creator.
+  """
   def og_twitter_creator(%{assigns: %{seo: %{twitter_creator: twitter_creator}}}), do: twitter_creator
   def og_twitter_creator(_conn), do: og_twitter_site(%{})

+  @doc """
+  Returns the Twitter handle for the website.
+  Safely extracts handle from URL or direct handle format.
+  """
   def og_twitter_site(%{assigns: %{seo: %{twitter_site: twitter_site}}}), do: twitter_site
-  def og_twitter_site(_conn), do: if(Azimutt.config(:azimutt_twitter), do: "@" <> (Azimutt.config(:azimutt_twitter) |> String.split("/") |> List.last()), else: "")
+  def og_twitter_site(_conn) do
+    case Azimutt.config(:azimutt_twitter) do
+      nil -> ""
+      handle when binary_part(handle, 0, 1) == "@" -> handle
+      url -> "@" <> (url |> String.split("/") |> List.last() |> String.replace(~r/[^a-zA-Z0-9_]/, ""))
+    end
+  end

60-61: Add documentation and consider configurable logo path.

The Azimutt-specific functions would benefit from:

  1. Documentation explaining their purpose
  2. Configurable logo path instead of hardcoding

Consider this improvement:

+  @doc """
+  Returns the base URL for Azimutt application
+  """
   def og_azimutt_url(conn), do: Routes.url(conn)

+  @doc """
+  Returns the URL for Azimutt logo
+  """
-  def og_azimutt_logo(conn), do: Routes.static_url(conn, "/images/logo_dark.svg")
+  def og_azimutt_logo(conn) do
+    logo_path = Application.get_env(:azimutt, :logo_path, "/images/logo_dark.svg")
+    Routes.static_url(conn, logo_path)
+  end

21-61: Consider architectural improvements for SEO metadata handling.

While the implementation is functional, consider these architectural improvements:

  1. Add comprehensive module documentation explaining the SEO strategy
  2. Implement validation for metadata values (length constraints, format validation)
  3. Consider extracting common SEO metadata patterns into a reusable behavior
  4. Add tests to verify metadata generation for different scenarios

Would you like me to help with any of these improvements? I can:

  1. Generate module documentation
  2. Create a validation module for SEO metadata
  3. Design a reusable SEO behavior
  4. Generate test cases
backend/lib/azimutt_web/controllers/website_controller.ex (2)

43-50: Consider improving error handling

While the Result monad usage is good, the function should handle the error case explicitly instead of relying on the fallback controller.

 def use_cases_show(conn, %{"use_case_id" => use_case_id}) do
   Azimutt.showcase_usages()
   |> Enum.find(fn u -> u.id == use_case_id end)
   |> Result.from_nillable()
-  |> Result.map(fn use_case ->
+  |> case do
+  {:ok, use_case} ->
     conn |> render("use-cases/#{use_case_id}.html", use_case: use_case, seo: %{type: "article", title: use_case.title, description: use_case.description})
-  end)
+  {:error, _} ->
+    conn
+    |> put_status(:not_found)
+    |> render("404.html")
+  end
 end

119-123: Consider extracting common SEO metadata patterns

There's duplicate SEO metadata construction logic between converter and convert actions. Consider extracting this into a private function.

+ defp converter_seo(converter) do
+   %{
+     title: "#{converter.name} converter by Azimutt",
+     description: "Azimutt is freely providing you converters to transform several database schema dialects from one to another 🤘",
+     image: Routes.static_url(conn, "/images/converters/#{converter.id}.jpg")
+   }
+ end

Also applies to: 139-143

backend/lib/azimutt.ex (2)

319-327: Consider refining connector descriptions

While the descriptions are informative, they all end with similar phrases about Azimutt integration. Consider making the integration aspect implicit and focus on unique characteristics of each database system.

For example:

-  %{id: "postgres", name: "PostgreSQL", color: "#699eca", description: "The most powerful open-source relational database, known for extensibility and SQL compliance. Now explorable with Azimutt."},
+  %{id: "postgres", name: "PostgreSQL", color: "#699eca", description: "The most powerful open-source relational database, known for extensibility and SQL compliance."},

359-362: Need assistance with implementing commented tools?

There are several commented tool entries across different categories. I can help implement these entries with proper descriptions and metadata.

Would you like me to:

  1. Generate complete tool entries with appropriate descriptions?
  2. Create a GitHub issue to track the implementation of these tools?

Also applies to: 384-388, 395-396, 403-405, 412-413, 420-421

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 539d10c and 67c4377.

📒 Files selected for processing (5)
  • backend/lib/azimutt.ex (4 hunks)
  • backend/lib/azimutt_web/controllers/website_controller.ex (5 hunks)
  • backend/lib/azimutt_web/templates/layout/_head_seo.html.heex (2 hunks)
  • backend/lib/azimutt_web/templates/sitemap/index.xml.eex (2 hunks)
  • backend/lib/azimutt_web/views/layout_view.ex (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • backend/lib/azimutt_web/templates/layout/_head_seo.html.heex
  • backend/lib/azimutt_web/templates/sitemap/index.xml.eex
🔇 Additional comments (6)
backend/lib/azimutt_web/views/layout_view.ex (1)

21-22: LGTM! Clean implementation of Open Graph type handling.

The pattern matching with guard clause ensures type safety, and the default fallback is appropriate.

backend/lib/azimutt_web/controllers/website_controller.ex (4)

32-34: LGTM: Improved SEO metadata

The SEO metadata updates enhance discoverability with a clear title, descriptive content, and proper image URL handling.


54-71: Previous review comments are still applicable

The implementation could benefit from the optimizations and bounds checking suggested in the previous review comments.


161-185: Previous refactoring suggestions are still applicable

The implementation could benefit from the structural improvements suggested in the previous review comments, including extracting lookup logic and simplifying nested Result operations.


242-243: LGTM: Simple template rendering actions

The portal actions are straightforward and follow the pattern of simple template rendering.

backend/lib/azimutt.ex (1)

170-170: LGTM: Enhanced clarity with descriptive titles

The addition of the title field to each usage entry improves clarity by providing more context about each feature's purpose.

Also applies to: 178-178, 186-186, 194-194

backend/lib/azimutt_web/views/layout_view.ex Outdated Show resolved Hide resolved
backend/lib/azimutt.ex Show resolved Hide resolved
backend/lib/azimutt.ex Show resolved Hide resolved
@loicknuchel loicknuchel merged commit aeb2c97 into main Nov 15, 2024
3 of 4 checks passed
@loicknuchel loicknuchel deleted the comparison-pages branch November 15, 2024 10:48
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