<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: ToolJet</title>
    <description>The latest articles on DEV Community by ToolJet (tooljet).</description>
    <link>https://dev.to/tooljet</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F5047%2F8494f13c-ffc6-4700-9c7d-f95b8280d9bd.png</url>
      <title>DEV Community: ToolJet</title>
      <link>https://dev.to/tooljet</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tooljet"/>
    <language>en</language>
    <item>
      <title>Build a Fleet Operations Dashboard with ToolJet MCP</title>
      <dc:creator>Athulya R</dc:creator>
      <pubDate>Wed, 09 Sep 2026 14:18:12 +0000</pubDate>
      <link>https://dev.to/tooljet/build-a-fleet-operations-dashboard-with-tooljet-mcp-4f6c</link>
      <guid>https://dev.to/tooljet/build-a-fleet-operations-dashboard-with-tooljet-mcp-4f6c</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;You build a fleet operations dashboard with ToolJet MCP for depot supervisors and fleet managers who need live answers on electric van performance without waiting for an analyst. An agent generated the app, and the result is a structured ToolJet application, with data, queries and interface working together, not a screen or a code drop. The app gives the team one place to answer operational questions that would otherwise need manual slicing of telemetry and master data. The rest of this tutorial shows the finished dashboard and the prompt in this article so you can reproduce the same internal app on your own fleet data.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgjk5crc5gmrw7avc9ibd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgjk5crc5gmrw7avc9ibd.png" title="Fleet Operations Dashboard built with ToolJet MCP: fleet metrics 1 home" alt="Build a fleet operations dashboard with ToolJet MCP, the finished application: fleet metrics 1 home" width="800" height="745"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;fleet metrics 1 home&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7mbn1buurzhtu29hux2a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7mbn1buurzhtu29hux2a.png" title="Fleet Operations Dashboard built with ToolJet MCP: fleet metrics 2 vehicle detail" alt="Build a fleet operations dashboard with ToolJet MCP, the finished application: fleet metrics 2 vehicle detail" width="800" height="688"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;fleet metrics 2 vehicle detail&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What We're Building
&lt;/h2&gt;

&lt;p&gt;The first page is an exploration surface, with metric, breakdown, depot and period controls driving KPI tiles, charts and a sortable table. A saved-view panel sits underneath, so the fleet director can store one combination for the rest of the team. The second page narrows to one van, with a search picker, a plain-English summary, an identity card, a trend chart, a peer comparison gauge, maintenance context and a table of daily readings. Each block answers a different operational question, and the layout keeps the comparison view and the vehicle view close enough to move between them without leaving the app.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Two pages, fleet comparison and single-vehicle detail&lt;/li&gt;
&lt;li&gt;Metric, breakdown, depot and period controls&lt;/li&gt;
&lt;li&gt;Shared saved views for the fleet director and team&lt;/li&gt;
&lt;li&gt;Comparison against fleet and depot averages&lt;/li&gt;
&lt;li&gt;Derived exceptions, maintenance context and CSV exports&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Build a Fleet Operations Dashboard with ToolJet MCP for Telemetry Review
&lt;/h2&gt;

&lt;p&gt;Want to build it yourself? Start with the &lt;a href="https://github.com/ToolJet/mcp-v2" rel="noopener noreferrer"&gt;ToolJet MCP repository&lt;/a&gt; for setup instructions, supported agents, and everything you need to follow along.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Consolidated Build Prompt
&lt;/h2&gt;

&lt;p&gt;The real build took several passes, and the requirements are consolidated into one prompt that could have been used from the start. You can use the prompt in this article to reproduce the same app in one shot.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Build a two-page self-serve ToolJet app called Fleet Operations Dashboard for a fleet operations team running electric delivery vans across multiple depots.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Page 1, Fleet metrics, lets supervisors explore telemetry by metric, depot, model, driver or week, compare against fleet and depot averages, save shared views, and review charts, KPI tiles and a breakdown table. Page 2, Vehicle detail, lets a user pick one van, read a plain-English summary, inspect its identity card, trend chart, peer comparison gauge, maintenance context, exception flags and daily readings table.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Use a clean, light, spreadsheet-adjacent visual style, with white surfaces, a light page background, one accent blue, hairline borders and compact hierarchy.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Use InfluxDB for telemetry and Supabase Postgres for vehicle, depot, model and driver data. Do not use ToolJet DB. Derive fleet size, depot count and date range from live data so the app keeps working as the fleet changes.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Model the app with &lt;code&gt;fleet_saved_view&lt;/code&gt;, &lt;code&gt;vehicle&lt;/code&gt;, &lt;code&gt;depot&lt;/code&gt;, &lt;code&gt;model&lt;/code&gt; and &lt;code&gt;driver&lt;/code&gt;. Keep saved views visible to the team, but gate the save control so only the fleet director can create them in the UI.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Aggregate everything in InfluxDB with parameterized Flux. Distance and hours sum, energy per km is a mileage-weighted mean, battery health uses the latest reading per vehicle before averaging, and utilisation is a simple mean.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Bind every Plotly chart to a query that returns an already-stringified JSON figure, and export every chart and table to CSV.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  How ToolJet MCP Builds the App
&lt;/h2&gt;

&lt;p&gt;ToolJet MCP works through the whole application, not just the screen, because the data model, queries, component tree and the wiring between them land as one structured ToolJet application. Enterprise app building does not end at the interface, because data connectivity, workflows, permissions, deployment and ongoing change are the rest of the job, and the generated app sits on a runtime that carries those where supported instead of handing you a codebase to operate yourself. The path stays open, so you can use AI generation, then visual editing, then code where a case needs it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The One Thing That Broke
&lt;/h2&gt;

&lt;p&gt;A read-only saved views panel went blank even though the row still existed in Postgres. The cause was a shared label-shaping query, where an edit for a new heading had been made against an older cached version and silently dropped the HTML that rendered the panel. Fixed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Data Created
&lt;/h2&gt;

&lt;p&gt;The app ended up with five tables. &lt;code&gt;fleet_saved_view&lt;/code&gt; was created by the build to store the shared view name, metric, breakdown, depot and saver details, while &lt;code&gt;vehicle&lt;/code&gt;, &lt;code&gt;depot&lt;/code&gt;, &lt;code&gt;model&lt;/code&gt; and &lt;code&gt;driver&lt;/code&gt; provide the master records that fill the selectors, labels and comparisons.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enterprise Features for Your Fleet Operations Dashboard
&lt;/h2&gt;

&lt;p&gt;A fleet dashboard handles driver names, depot assignments, and vehicle maintenance status. ToolJet covers that governance at the platform layer, so you configure it once instead of rebuilding it in every app.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SSO and SCIM:&lt;/strong&gt; sign in with SAML, OIDC, or LDAP, and provision users automatically&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Role-based access control:&lt;/strong&gt; scope permissions to the app, the data source, and each query&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audit logs:&lt;/strong&gt; track every login, edit, and approval decision for compliance review&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Air-gapped deployment:&lt;/strong&gt; self-host on Docker or Kubernetes so your data stays in your network&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multiplayer editing:&lt;/strong&gt; several builders work on the same app, with versioning and Git sync&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Final Takeaways
&lt;/h2&gt;

&lt;p&gt;This build shows that a fleet supervisor can start from live telemetry and master data, then land on a dashboard that compares depots, explains one vehicle and keeps a shared saved-view list for the team. The important part is that the output is a structured ToolJet application, so the pages, queries and bindings stay in the same place after generation. You can keep refining the interface visually, drop into code when a metric rule or interaction needs it, and rely on the runtime for the data and workflow pieces where supported instead of scattering the app across separate files. That keeps later change inside the app model, not in a rewrite.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try ToolJet MCP
&lt;/h2&gt;

&lt;p&gt;Build your own fleet operations dashboard with ToolJet MCP, then request a &lt;a href="https://tooljet.com/schedule-demo" rel="noopener noreferrer"&gt;ToolJet demo&lt;/a&gt; to map it onto live telemetry.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQs
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Is this just a UI generator?
&lt;/h3&gt;

&lt;p&gt;No, the result is a structured ToolJet application, so the data model, queries, pages and component wiring stay together after generation. You keep editing the same app in the runtime instead of taking over a separate codebase when the agent finishes.&lt;/p&gt;

&lt;h3&gt;
  
  
  What does ToolJet MCP do for this fleet dashboard?
&lt;/h3&gt;

&lt;p&gt;It turns the prompt in this article into a two-page ToolJet build with live telemetry queries and the page structure already connected. The same prompt also covers the saved-view flow, the single-vehicle drilldown and the comparison logic.&lt;/p&gt;

&lt;h3&gt;
  
  
  What does the Fleet Operations Dashboard show?
&lt;/h3&gt;

&lt;p&gt;The first page compares depot, model, driver and week views across distance, energy efficiency, battery health, hours in service and utilisation, with fleet averages folded into the same view. The second page drills into one van with recent readings, maintenance status and rule-derived exceptions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I reproduce it from the prompt in this article?
&lt;/h3&gt;

&lt;p&gt;Yes, the condensed prompt is written so you can start from one specification and rebuild the same app in a single shot. It already includes the pages, the visual direction, the data sources, the metric rules and the shared-view behaviour.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do I need to write code for the charts and metrics?
&lt;/h3&gt;

&lt;p&gt;No, the metric work belongs in Flux, not in JavaScript. The chart components bind to the query output, and code only comes in when a particular interaction or visual shape needs an exception.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I connect existing telemetry and master data?
&lt;/h3&gt;

&lt;p&gt;Yes, the dashboard is built around existing InfluxDB telemetry and Supabase Postgres master records. The app derives comparisons from live data, so the vehicle count and depot structure come from the sources instead of hardcoded values or fixed assumptions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can several people work on the same dashboard?
&lt;/h3&gt;

&lt;p&gt;Yes, the app lives as one shared project, so teammates review and refine the same structure rather than cloning separate copies. The saved-view list gives the team a common set of named configurations to work from.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>python</category>
    </item>
    <item>
      <title>Build an RFP Response Manager with ToolJet MCP</title>
      <dc:creator>Athulya R</dc:creator>
      <pubDate>Tue, 08 Sep 2026 13:02:05 +0000</pubDate>
      <link>https://dev.to/tooljet/build-an-rfp-response-manager-with-tooljet-mcp-2m8e</link>
      <guid>https://dev.to/tooljet/build-an-rfp-response-manager-with-tooljet-mcp-2m8e</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;An RFP response team uses this app to track active requests, assign SMEs, and keep approved answers easy to reuse. Build an RFP response manager with ToolJet MCP, and the result is a structured ToolJet application, with data, queries and interface kept together for day-to-day work. It gives the team one place to manage assignments, responses, and reuse without bouncing between separate tools. The walkthrough that follows shows how the prompt below becomes a business app the team can keep changing as the process changes. That keeps the screen as one layer of the system, not the whole job. The app stays readable at a glance, but it also carries the data and review work behind the surface.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F745hmtin75udigznetpt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F745hmtin75udigznetpt.png" title="RFP Hub built with ToolJet MCP: RFP Hub ToolJet" alt="Build an rfp response manager with ToolJet MCP, the finished application: RFP Hub ToolJet" width="800" height="577"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;RFP Hub ToolJet&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What We're Building
&lt;/h2&gt;

&lt;p&gt;The Dashboard is a triage board. A stage filter changes the visible cards, while the metric row stays global so you can read open work, overdue items, and win rate without losing the wider picture. Each card carries the client, a due-state cue, and a progress ring, and dragging a card changes stage instead of creating a duplicate. Clicking a card opens the record in the detail view. The detail page splits the workflow into question review and team context, with the reviewer modal built around a short summary, reassignment, answer editing, and the approve or reject actions. The library page is the reuse path, where search, snippet previews, and the open modal help the team move approved text back into a new response. Each surface reads status through colour and shape before the text.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A dashboard that keeps the RFP pipeline readable at a glance&lt;/li&gt;
&lt;li&gt;Question review with SME reassignment and quick approve or reject actions&lt;/li&gt;
&lt;li&gt;A searchable content library for approved answers and reuse&lt;/li&gt;
&lt;li&gt;Global metrics that stay stable while the board is filtered&lt;/li&gt;
&lt;li&gt;State carried through colour, shape, and compact hierarchy&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Build an RFP Response Manager with ToolJet MCP for the Dashboard, Detail View, and Content Library
&lt;/h2&gt;

&lt;p&gt;Want to build it yourself? Start with the &lt;a href="https://github.com/ToolJet/mcp-v2" rel="noopener noreferrer"&gt;ToolJet MCP repository&lt;/a&gt; for setup instructions, supported agents, and everything you need to follow along.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the Full Run Cost
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Tool calls, build and testing&lt;/td&gt;
&lt;td&gt;75&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  The Consolidated Build Prompt
&lt;/h2&gt;

&lt;p&gt;The real build took several passes. The prompt below consolidates those requirements into one specification you can use from the start, so you can reproduce the app in a single run and compare the output with the same page structure. It keeps the model, the pages, and the interaction rules in one place.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Build a polished internal app called RFP Hub in ToolJet.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Make it an internal RFP response manager for Evotix with three pages only: Dashboard for pipeline tracking, RFP Detail for question review, and Content Library for reusable answers. The Dashboard should show a stage filter, global health tiles, a board with due-state cues and progress, and a chart of SME workload. RFP Detail should open from a board card, show the selected record, split into Questions and Team tabs, and include a reviewer modal with summary, reassignment, answer editing, and approve or reject actions. Content Library should support search, opening a saved answer in a modal, copying that answer, and adding new entries.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Use a dense but clean enterprise layout. Keep the palette in the cream, charcoal, tomato, amber, and olive family, with strong hierarchy, compact rows, and colour used only where it carries state.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Use four related tables: rfps, smes, questions, and library_entries. The app should lean on queries for aggregation, derive stage and review state from the data model, and keep expensive logic out of component bindings.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Every query-backed surface needs loading, empty, and error states. Every write should toast, refresh only the affected data, and avoid duplicate firing. No component should repeat a filter or column that already appears elsewhere.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  How ToolJet MCP Builds the App
&lt;/h2&gt;

&lt;p&gt;ToolJet MCP works through the whole application, not just the screen, so the data model, pages, components, queries, and the wiring between them land as one structured ToolJet application. That matters because enterprise app building does not stop at interface design, it also includes data connectivity, workflows, permissions, deployment, and ongoing change, and the generated app sits on a runtime that carries those where supported rather than handing you a codebase to operate yourself. In this build, the board, detail page, and library stay aligned around the same records and review state, and the path stays open from AI generation to visual editing and then to code where a special case needs it. That is the difference you feel after generation, because the app is ready for edits instead of being a pile of output to package up first.&lt;/p&gt;

&lt;h2&gt;
  
  
  The One Thing That Broke
&lt;/h2&gt;

&lt;p&gt;The question modal opened blank on the first click because the row click event fired before the selected question was ready, so the modal mounted without data. The fix was to bind the modal to the row data that was available at open time, which made the summary, answer, and reviewer actions appear immediately.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enterprise Features for Your RFP Response Manager
&lt;/h2&gt;

&lt;p&gt;An RFP app holds client names, deal values, and assigned SMEs. ToolJet covers that governance at the platform layer, so you configure it once instead of rebuilding it in every app.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SSO and SCIM:&lt;/strong&gt; sign in with SAML, OIDC, or LDAP, and provision users automatically&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Role-based access control:&lt;/strong&gt; scope permissions to the app, the data source, and each query&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audit logs:&lt;/strong&gt; track every login, edit, and approval decision for compliance review&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Air-gapped deployment:&lt;/strong&gt; self-host on Docker or Kubernetes so your data stays in your network&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multiplayer editing:&lt;/strong&gt; several builders work on the same app, with versioning and Git sync&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Final Takeaways
&lt;/h2&gt;

&lt;p&gt;This build shows a complete RFP workflow in one place, from pipeline review to question assignment to reusable approved answers. The important part is not that the interface was generated, but that the output is a structured ToolJet application that you keep editing after the first build. That matters when the review process changes, because visual editing handles most updates, code is there when a special case needs it, and the runtime keeps the data and workflow layer together where supported. You end up with a maintained app model, and ToolJet gives you room for later changes without rebuilding the app shape.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try ToolJet MCP
&lt;/h2&gt;

&lt;p&gt;Build an RFP response manager with ToolJet MCP for your team, then request a &lt;a href="https://tooljet.com/schedule-demo" rel="noopener noreferrer"&gt;ToolJet demo&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQs
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is ToolJet MCP in this RFP tutorial?
&lt;/h3&gt;

&lt;p&gt;ToolJet MCP is the bridge from the prompt in this article to a working RFP Hub app in ToolJet. It builds the dashboard, detail view, and content library as one connected application instead of leaving you with disconnected pieces.&lt;/p&gt;

&lt;h3&gt;
  
  
  What does RFP Hub help a proposal team do?
&lt;/h3&gt;

&lt;p&gt;RFP Hub keeps active requests, assigned SMEs, and approved answers in one place. The dashboard shows stage and urgency, the detail page turns each question into a review task, and the library keeps reusable responses searchable.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do you reproduce an RFP response manager with ToolJet MCP?
&lt;/h3&gt;

&lt;p&gt;Use the prompt in this article, then review the page structure, data model, and interaction rules against your own process. The same pattern works when you swap in your own RFP records and answer library.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do you need to write code for the whole app?
&lt;/h3&gt;

&lt;p&gt;You do not need code for the whole app, because ToolJet assembles the pages, tables, queries, and interactions in the builder. You can still drop into code for a specific edge case while keeping the rest visual.&lt;/p&gt;

&lt;h3&gt;
  
  
  What happens to the app after the agent finishes?
&lt;/h3&gt;

&lt;p&gt;The agent leaves you with a structured ToolJet application, not just a screen or a code dump. The runtime keeps the data, queries, and workflow layer in place where supported, so you continue editing the app instead of taking over generated code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can you connect existing RFP data and answer sources?
&lt;/h3&gt;

&lt;p&gt;Yes, you map your current RFP records and approved answers into the same app shape and keep the review flow intact. The tutorial shows the pattern, so you can adapt the dashboard, detail page, and library to your own source of truth.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can multiple people work on the same app?
&lt;/h3&gt;

&lt;p&gt;Yes, multiple people can keep shaping the same app in ToolJet while proposal managers, SMEs, and reviewers use different screens on the same records. That keeps the workflow aligned when questions, answers, and review rules change together.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>Build Expense Reimbursement App With ToolJet MCP</title>
      <dc:creator>Athulya R</dc:creator>
      <pubDate>Mon, 07 Sep 2026 13:09:14 +0000</pubDate>
      <link>https://dev.to/tooljet/build-expense-reimbursement-app-with-tooljet-mcp-1k5m</link>
      <guid>https://dev.to/tooljet/build-expense-reimbursement-app-with-tooljet-mcp-1k5m</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Build expense reimbursement app with ToolJet MCP when employees need to submit claims, attach receipts, group them into weekly reports, and track payment status through approval and finance. An agent can generate the build, and the result is a structured ToolJet application, data, queries and interface together, that the team keeps running rather than a screen or a code drop. In this article, you get the app shape, the model behind it, and the prompt that reproduces it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.tooljet.com%2Fwp-content%2Fuploads%2F2026%2F09%2Fbuild-expense-management-app-tooljet-mcp-dashboard-8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.tooljet.com%2Fwp-content%2Fuploads%2F2026%2F09%2Fbuild-expense-management-app-tooljet-mcp-dashboard-8.png" title="Expense &amp;amp; Reimbursement Management built with ToolJet MCP — ExpenseSheet" alt="Build expense reimbursement app with ToolJet MCP, the finished application — ExpenseSheet" width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;ExpenseSheet&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What We're Building
&lt;/h2&gt;

&lt;p&gt;The first page keeps employee work in one view: KPI tiles at the top, a spend chart, a status breakdown, a needs-attention panel, and a tab switch that leaves only one table visible at a time. The expense modal changes its fields with the selected type, so mileage, accommodation, training, and client entertainment each expose the data that matters for that item. The second page shifts to submitted reports, line-level review in a modal, the approval history, the reimbursement queue, and the policy reference table. Dense layout, short labels, and status cues make the page read like an operations console instead of a brochure.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Generates a structured ToolJet application from the prompt, with tables, pages, queries and wiring created together&lt;/li&gt;
&lt;li&gt;Keeps the app editable in the builder after generation, with visual edits for layout and code for edge cases&lt;/li&gt;
&lt;li&gt;Runs the expense workflow on a live runtime so data, approvals and payment state stay connected where supported&lt;/li&gt;
&lt;li&gt;Fits internal operations work that needs review steps, history and later changes without rebuilding the app&lt;/li&gt;
&lt;li&gt;Can run on infrastructure you control where supported, which suits internal finance systems&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/OicJe9cDzj8" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Build Expense Reimbursement App With ToolJet MCP From One Prompt
&lt;/h2&gt;

&lt;p&gt;Want to build it yourself? Start with the &lt;a href="https://github.com/ToolJet/mcp-v2" rel="noopener noreferrer"&gt;ToolJet MCP repository&lt;/a&gt; for setup instructions, supported agents, and everything you need to follow along.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Consolidated Build Prompt
&lt;/h2&gt;

&lt;p&gt;The build took several passes to settle the page split, the data model, and the interaction rules. The requirements here are consolidated into one prompt so you can reproduce the app in a single run.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Build a polished internal app for expense and reimbursement management in ToolJet.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Keep it to 2 pages. The employee page shows a title and short purpose line, a 2x2 KPI tile area, a spend chart, a status breakdown chart, a needs-attention panel, and a tabbed area so only one table is visible at a time. The second page is for approvals and finance, with an approval inbox, a review modal that shows line-level decisions and history, a reimbursement queue, and a policy reference table.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Use 7 tables: employees, policies, expenses, weekly_reports, report_expenses, approvals, and reimbursements. Model approvals and payments as linked records, not loose status fields, so report state and payment state stay consistent.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Make the form rules do the work. Every expense must point to a policy, over-limit items become policy exceptions with a reason, receipt-required policies disable submit until a bill is attached, and only expenses not already attached to a report can be added. Managers can approve a whole report or send back individual lines, employees can reopen sent-back reports, and reimbursement status must flow through raised, processing, paid, and failed while writing every decision to history.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Use a dense, light-mode enterprise style with restrained colour, compact spacing, and clear hierarchy.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  How ToolJet MCP Builds the App
&lt;/h2&gt;

&lt;p&gt;ToolJet MCP works through the full application structure, so the data model, pages, components, queries and the wiring between them land as one structured ToolJet application. That matters because expense work does not stop at the interface, it keeps moving through data connectivity, workflows, permissions, deployment and later change, and the generated app sits on a runtime that carries those where supported instead of handing you a codebase to operate yourself. From there, you can keep shaping the app visually and drop into code only where a case needs it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Data Created
&lt;/h2&gt;

&lt;p&gt;The app ended up with seven tables in three groups: &lt;code&gt;exp_employees&lt;/code&gt; and &lt;code&gt;exp_policies&lt;/code&gt; hold the people and reimbursement rules, &lt;code&gt;exp_expenses&lt;/code&gt;, &lt;code&gt;exp_weekly_reports&lt;/code&gt; and &lt;code&gt;exp_report_expenses&lt;/code&gt; hold the claim flow, and &lt;code&gt;exp_approvals&lt;/code&gt; plus &lt;code&gt;exp_reimbursements&lt;/code&gt; record decisions and payment progress. The approval table is the audit trail, and the reimbursement table pushes the payment state back onto the claim view.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Page&lt;/th&gt;
&lt;th&gt;Components&lt;/th&gt;
&lt;th&gt;What they cover&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Expense Workspace&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;33&lt;/td&gt;
&lt;td&gt;Four KPI tiles, spend and status charts, a needs-attention panel, segmented expense and report tables, and three modals for submission, detail, and report creation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Approvals &amp;amp; Finance&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;26&lt;/td&gt;
&lt;td&gt;An approval inbox with five filters, a KPI strip, a reimbursement queue, a policy reference table, and three modals for review, reimbursement, and policy detail&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Build Results
&lt;/h2&gt;

&lt;p&gt;The agent produced two pages, seven ToolJet DB tables, 23 queries, and 59 components. The tables below show what landed in the app.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Pages&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ToolJet DB tables&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Queries&lt;/td&gt;
&lt;td&gt;23&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Components&lt;/td&gt;
&lt;td&gt;59&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Prompt iterations&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Code files to maintain&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyu0jiu2f4qjdhioxjeov.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyu0jiu2f4qjdhioxjeov.png" alt=" " width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Enterprise Features for Your Expense Management App
&lt;/h2&gt;

&lt;p&gt;An expense app handles payroll data, receipts, and approval chains. ToolJet covers that governance at the platform layer, so you configure it once instead of rebuilding it in every app.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SSO and SCIM:&lt;/strong&gt; Sign in with SAML, OIDC, or LDAP, and provision users automatically.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Role-based access control:&lt;/strong&gt; Scope permissions to the app, the data source, and each query.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audit logs:&lt;/strong&gt; Track every login, edit, and approval decision for compliance review.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Air-gapped deployment:&lt;/strong&gt; Self-host on Docker or Kubernetes so finance data stays in your network.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multiplayer editing:&lt;/strong&gt; Several builders work on the same app, with versioning and Git sync.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ToolJet Workflows adds the notification layer. One workflow fires when a weekly report is submitted, posts to the manager in Slack, emails the employee through Gmail, and writes the decision back to &lt;code&gt;exp_approvals&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Takeaways
&lt;/h2&gt;

&lt;p&gt;This build shows that a single specification can produce a working expense workflow where employees submit claims, managers review line items, and finance tracks reimbursement status on the same record. The value is that you keep a real ToolJet application, not generated code, so the next change lands in the app rather than in a file tree you now own. Visual edits handle the common changes, code covers the edge cases, and the runtime keeps the data and workflow pieces tied together as the process changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try ToolJet MCP
&lt;/h2&gt;

&lt;p&gt;Build an expense reimbursement app with ToolJet MCP using your claims and policy data, then request a &lt;a href="https://tooljet.com/schedule-demo" rel="noopener noreferrer"&gt;ToolJet demo&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQs
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How do I build expense reimbursement app with ToolJet MCP?
&lt;/h3&gt;

&lt;p&gt;Use the prompt in this article to generate the two-page app, then tune the tables, statuses and form rules to match your own policies. The page split keeps employee work separate from approvals and finance without turning the build into a screen-only demo.&lt;/p&gt;

&lt;h3&gt;
  
  
  What does ToolJet MCP produce after the prompt runs?
&lt;/h3&gt;

&lt;p&gt;It produces a structured ToolJet application, not a loose bundle of files. For this build, the employee workspace, approvals page, queries and table wiring all land inside the same app.&lt;/p&gt;

&lt;h3&gt;
  
  
  What happens to the app after the agent finishes?
&lt;/h3&gt;

&lt;p&gt;The result stays inside the ToolJet runtime, so you keep editing the same app instead of taking ownership of generated code. Data, queries and workflow changes stay connected, and you can move between visual editing and code when a case needs it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does this app support weekly reports and partial approvals?
&lt;/h3&gt;

&lt;p&gt;Yes. The employee groups expenses into weekly reports, managers can approve a whole report or send back individual lines, and the reimbursement state follows the same claim through review and payment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I use the same structure with my own expense data?
&lt;/h3&gt;

&lt;p&gt;Yes. The model is built around employees, policies, expenses, reports, approvals and reimbursements, so you can map the same shape onto your own source of truth. The key is keeping the claim, report and payment records linked.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do I need to write code to finish the build?
&lt;/h3&gt;

&lt;p&gt;No. You can generate the structure first, use the visual editor for most changes, and only drop into code when a field or rule needs custom logic. That keeps the app readable when policy rules or approval steps change.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>productivity</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Build a Hospital Analytics Dashboard with ToolJet MCP</title>
      <dc:creator>Athulya R</dc:creator>
      <pubDate>Fri, 04 Sep 2026 06:15:57 +0000</pubDate>
      <link>https://dev.to/tooljet/build-a-hospital-analytics-dashboard-with-tooljet-mcp-1ifm</link>
      <guid>https://dev.to/tooljet/build-a-hospital-analytics-dashboard-with-tooljet-mcp-1ifm</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Hospital administrators need one place to watch admissions and departmental activity, and this tutorial shows how to build a hospital analytics dashboard with ToolJet MCP. The agent generates a structured ToolJet application, so the data, queries and interface travel together as one app, not as a screen or a code drop. You get a trilingual operations dashboard you can read as a single workspace, with admissions data feeding the charts, the label set changing with the selected language, and the heatmap showing where activity concentrates. The walkthrough points you at the finished result first, then the pieces underneath it, so the prompt in this article can be reused without guessing the shape of the app.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.tooljet.com%2Fwp-content%2Fuploads%2F2026%2F09%2Fbuild-hospital-analytics-dashboard-tooljet-mcp-dashboard-16.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.tooljet.com%2Fwp-content%2Fuploads%2F2026%2F09%2Fbuild-hospital-analytics-dashboard-tooljet-mcp-dashboard-16.png" title="Hospital Analytics Dashboard built with ToolJet MCP — English" alt="Build a hospital analytics dashboard with ToolJet MCP, the finished application — English" width="800" height="691"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;English&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.tooljet.com%2Fwp-content%2Fuploads%2F2026%2F09%2Fbuild-hospital-analytics-dashboard-tooljet-mcp-dashboard-2-9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.tooljet.com%2Fwp-content%2Fuploads%2F2026%2F09%2Fbuild-hospital-analytics-dashboard-tooljet-mcp-dashboard-2-9.png" title="Hospital Analytics Dashboard built with ToolJet MCP — French" alt="Build a hospital analytics dashboard with ToolJet MCP, the finished application — French" width="800" height="691"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;French&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.tooljet.com%2Fwp-content%2Fuploads%2F2026%2F09%2Fbuild-hospital-analytics-dashboard-tooljet-mcp-dashboard-3-9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.tooljet.com%2Fwp-content%2Fuploads%2F2026%2F09%2Fbuild-hospital-analytics-dashboard-tooljet-mcp-dashboard-3-9.png" title="Hospital Analytics Dashboard built with ToolJet MCP — Spanish" alt="Build a hospital analytics dashboard with ToolJet MCP, the finished application — Spanish" width="800" height="670"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Spanish&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What We're Building
&lt;/h2&gt;

&lt;p&gt;The page uses a compact operations layout, with a KPI row at the top and a chart grid beneath it. The header keeps a language selector visible while the titles and captions update in place, so the same layout speaks English, Spanish or French without cloning the page. One chart is a heatmap, and the admissions matrix only works once the series is shaped to match the component. The rest of the grid gives the admissions view enough context to read trends without leaving the screen, which keeps the layout dense and easy to scan. That means the user can switch languages, inspect the KPI tiles, and compare departmental activity in one pass instead of moving between separate views.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Admissions data reads from ToolJet DB&lt;/li&gt;
&lt;li&gt;Translated labels switch between English, Spanish and French&lt;/li&gt;
&lt;li&gt;Four KPI tiles sit above the chart grid&lt;/li&gt;
&lt;li&gt;A heatmap shows admissions by department and day&lt;/li&gt;
&lt;li&gt;Validation and seeded rows confirm the dashboard works on load&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Build a Hospital Analytics Dashboard with ToolJet MCP and Adapt the Charts
&lt;/h2&gt;

&lt;p&gt;Want to build it yourself? Start with the &lt;a href="https://github.com/ToolJet/mcp-v2" rel="noopener noreferrer"&gt;ToolJet MCP repository&lt;/a&gt; for setup instructions, supported agents, and everything you need to follow along.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Consolidated Build Prompt
&lt;/h2&gt;

&lt;p&gt;The real build took several passes, and the requirements are consolidated here into one prompt that could have been used from the start. You can use the prompt in this article to reproduce the same hospital operations dashboard in a single shot.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Build a hospital operations analytics dashboard in ToolJet, on ToolJet DB, for hospital administrators who need one screen for admissions and departmental activity.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Store admissions records with a department, an admission date and a patient count, and seed them with realistic demo data across several departments and weeks so every chart has something to show on load. Keep a separate table holding the interface labels for each supported language.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Use three queries: one that reads the admissions rows, one RunJS query that reshapes them into the series each chart needs including a department-by-day matrix for the heatmap, and one RunJS query that returns the label set for the currently selected language.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Lay out a single page: a KPI row of four statistic tiles, then a grid of four charts inside two containers, one of which is a heatmap of admissions by department and day. Put a language selector in the header offering English, Spanish and French.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Selecting a language must re-render every heading, axis title and tile caption from the label query, without reloading the page and without duplicating the layout per language. The heatmap must render actual cells rather than only axes, so shape the series to whatever the chart component expects before binding it.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Aim for a dense enterprise operations screen: restrained colour, tight spacing, clear type hierarchy, readable at a glance. Run the app validation and confirm the queries return seeded rows before finishing.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  How ToolJet MCP Builds the App
&lt;/h2&gt;

&lt;p&gt;ToolJet MCP works through the whole application, not the visible layer, so the data model, queries, pages, components and wiring land together as one structured ToolJet application. That matters because the job does not end at the interface, the dashboard still depends on data connectivity, workflow logic and later change, and the generated app stays on the runtime that carries those where supported instead of handing you a codebase to run yourself. From there, you can keep moving between AI generation, visual editing and code when the shape of the data needs it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The One Thing That Broke
&lt;/h2&gt;

&lt;p&gt;The heatmap showed its axes but no cells, so the admissions view looked empty even though the chart frame was present. The series shape did not match the chart's expectation, and reshaping the computed dataset made the department by day grid render correctly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Data Created
&lt;/h2&gt;

&lt;p&gt;The app ended up with two tables. &lt;code&gt;hospital_admissions&lt;/code&gt; stores admissions records by department and date, and &lt;code&gt;translations&lt;/code&gt; holds the label set for each supported language. The first table feeds the charts and the second drives the live text swap in the header and KPI captions.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Got Generated
&lt;/h2&gt;

&lt;p&gt;The results table shows the validation state and the query checks that confirmed the seeded admissions rows and the working language logic.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Components&lt;/td&gt;
&lt;td&gt;19&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Queries&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Repair cycles&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Final validation&lt;/td&gt;
&lt;td&gt;0 errors&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.tooljet.com%2Fwp-content%2Fuploads%2F2026%2F09%2Fbuild-hospital-analytics-dashboard-tooljet-mcp-components-inspector.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.tooljet.com%2Fwp-content%2Fuploads%2F2026%2F09%2Fbuild-hospital-analytics-dashboard-tooljet-mcp-components-inspector.png" title="Hospital Analytics Dashboard built with ToolJet MCP — components" alt="Hospital Analytics Dashboard component tree generated by ToolJet MCP" width="800" height="453"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The components ToolJet MCP created, in the ToolJet inspector&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Takeaways
&lt;/h2&gt;

&lt;p&gt;This build shows that a hospital operations dashboard can come out of one prompt as a structured app, with live label switching, reshaped chart data and a heatmap that renders real cells. Once generation ends, the useful artifact is the application itself, because you keep working in ToolJet instead of taking ownership of a code bundle. Visual editing covers routine layout changes, code handles the edge cases, and the data layer stays attached to the interface as the requirements change. That is the part that matters, since the dashboard keeps its data and its presentation aligned while the team keeps iterating.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try ToolJet MCP
&lt;/h2&gt;

&lt;p&gt;Build your own hospital operations dashboard from admissions data, then request a &lt;a href="https://tooljet.com/schedule-demo" rel="noopener noreferrer"&gt;ToolJet demo&lt;/a&gt; today.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQs
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What does ToolJet MCP build in this tutorial?
&lt;/h3&gt;

&lt;p&gt;ToolJet MCP turns the prompt in this article into a structured ToolJet application with admissions data, translated labels, charts and a heatmap. The output is an application shape, not a loose screen sketch, so the dashboard has data and behavior together.&lt;/p&gt;

&lt;h3&gt;
  
  
  What happens after the agent finishes?
&lt;/h3&gt;

&lt;p&gt;The agent leaves you with a structured ToolJet application, not a screen mockup or a code bundle. The runtime keeps the app, data connections and workflow logic together where supported, so later changes happen in the same place and the dashboard keeps working as one unit.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do you build a hospital analytics dashboard with ToolJet MCP?
&lt;/h3&gt;

&lt;p&gt;Use the prompt in this article as the build spec and connect admissions rows plus the translation table. The same layout gives you the KPI row, chart grid and language switching without rebuilding the page by hand, and the heatmap keeps the department view in place.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can you keep editing after generation?
&lt;/h3&gt;

&lt;p&gt;Yes, the visual editor handles layout changes and code handles edge cases when the chart data changes shape. The same app stays open for both, so you can adjust the dashboard without starting over or losing the bindings that already work.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do you need to write code to make the charts work?
&lt;/h3&gt;

&lt;p&gt;No, the charts run from queries, reshaped data and visual bindings. Code only comes in when a component needs a different data shape, a custom label set, or a chart-specific adjustment that the editor cannot express cleanly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can you point the dashboard at your own admissions data?
&lt;/h3&gt;

&lt;p&gt;Yes, swap the seeded rows for your own source and keep the same admissions and translations tables. The dashboard logic stays the same, only the inputs change, so the same layout can read a different hospital feed.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>beginners</category>
    </item>
    <item>
      <title>What Do You Build With a Brand-New MCP? We Built a "Launch War Room"</title>
      <dc:creator>Karan Rathod</dc:creator>
      <pubDate>Fri, 28 Aug 2026 12:48:12 +0000</pubDate>
      <link>https://dev.to/tooljet/what-do-you-build-with-a-brand-new-mcp-we-built-a-launch-war-room-ala</link>
      <guid>https://dev.to/tooljet/what-do-you-build-with-a-brand-new-mcp-we-built-a-launch-war-room-ala</guid>
      <description>&lt;p&gt;&lt;a href="https://tooljet.com/" rel="noopener noreferrer"&gt;ToolJet&lt;/a&gt; was the first internal tool builder to go AI-native, but ToolJet MCP takes that idea somewhere much more interesting. Instead of stopping at “describe an app and generate one,” agents can now work directly inside ToolJet, creating and refining the same components, queries, data and workflows a human builder would use. The end result is still a proper ToolJet app, not a code dump that looks impressive on day one and turns into a maintenance problem six months later. That opens up a much bigger surface area: agents that can design, build, test and iterate on real internal tools, while everything remains inspectable and maintainable inside the platform.&lt;/p&gt;

&lt;p&gt;So rather than test it on another toy dashboard, we gave it a workflow where things could actually go wrong.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Link to the ToolJet MCP GitHub repo: &lt;a href="https://github.com/ToolJet/tooljet-mcp" rel="noopener noreferrer"&gt;https://github.com/ToolJet/tooljet-mcp&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here's a sneak peek of the output:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgjmiifnlhyqwhb9ic1rj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgjmiifnlhyqwhb9ic1rj.png" alt=" " width="800" height="419"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvfqhe07fctqfe1mo84kx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvfqhe07fctqfe1mo84kx.png" alt=" " width="799" height="421"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkipdu2aflls8k6fm8epn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkipdu2aflls8k6fm8epn.png" alt=" " width="800" height="373"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnhxjmh5l60awliamaya8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnhxjmh5l60awliamaya8.png" alt=" " width="800" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Check out the public preview of the app: &lt;a href="https://app.tooljet.ai/applications/187c1d73-25c8-4071-8dfc-7b6f5f38a42d" rel="noopener noreferrer"&gt;Shiproom&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And here is how much it cost:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkwvpokcd36yuzm7f4gcv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkwvpokcd36yuzm7f4gcv.png" alt=" " width="799" height="210"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  We Asked ToolJet MCP To Solve A Real Problem
&lt;/h2&gt;

&lt;p&gt;Every team that ships software has some version of the same Thursday afternoon. The launch is two days away, QA says they are done, Legal may or may not have signed off, and someone remembers a blocker sitting in a Slack thread that nobody has opened since Tuesday.&lt;/p&gt;

&lt;p&gt;The spreadsheet, naturally, is green, but nobody can confidently answer whether the launch is actually ready.&lt;/p&gt;

&lt;p&gt;Shiproom is our attempt to fix that. It is a two-page internal app where teams can track launch checks, blockers and approvals, see a live readiness score, and eventually record the decision to ship or delay.&lt;/p&gt;

&lt;p&gt;We started with one prompt that covered the whole application: what each page should do, how the data should behave, the rules around blockers and approvals, and the visual direction. We also asked for populated, loading, empty and error states up front, because otherwise those are exactly the screens that tend to get forgotten.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fe58i7e86zdzsq19hckj8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fe58i7e86zdzsq19hckj8.png" alt=" " width="800" height="434"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From there, the flow was straightforward: generate the design, build the underlying tables and logic, assemble the app, then check the result against the original design.&lt;/p&gt;

&lt;h2&gt;
  
  
  First, It Designed the App
&lt;/h2&gt;

&lt;p&gt;The design direction was intentionally restrained. Shiproom uses cool greys, compact rows, a limited blue accent and status colours only where they are useful. Internal tools need hierarchy, enough density to be useful and just enough visual emphasis to make the important things obvious.&lt;/p&gt;

&lt;p&gt;The first page is the launch list. It shows upcoming and recent launches, but the next launch gets most of the attention with a countdown, readiness score and the biggest blocker called out at the top.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F534yalwrxvqamz5vpt7x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F534yalwrxvqamz5vpt7x.png" alt=" " width="799" height="562"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The second page is the actual war room. It is divided into four sections: critical checks, blockers, approvals and the final ship decision. The idea was to keep the whole workflow visible without turning it into a miniature project management suite.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fghr2ccs1zajyfen8mldd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fghr2ccs1zajyfen8mldd.png" alt=" " width="800" height="857"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The design also covered the states that usually disappear from demo apps. We asked for populated, empty, loading and error states in the same prompt, so those were treated as part of the product rather than cleanup work after the happy path was finished.&lt;/p&gt;

&lt;p&gt;The empty state still explains what the screen is for instead of simply displaying nothing. The loading state uses skeleton rows rather than pretending stale data is current, while the error state keeps the last successful values visible, marks them as stale and disables the signals that should no longer be trusted.&lt;/p&gt;

&lt;p&gt;That last detail matters more than the skeletons. If the application cannot refresh its data, it probably should not confidently tell someone that a launch is ready to ship.&lt;/p&gt;

&lt;h2&gt;
  
  
  Then It Built the Data Model
&lt;/h2&gt;

&lt;p&gt;The database did not exist beforehand either.&lt;/p&gt;

&lt;p&gt;The app ended up with three tables. &lt;code&gt;launches&lt;/code&gt; contains the basic launch information, &lt;code&gt;launch_checks&lt;/code&gt; contains the things that need to happen before shipping, and &lt;code&gt;launch_events&lt;/code&gt; records what actually happens along the way.&lt;/p&gt;

&lt;p&gt;The interesting part is what is missing. There is no permanent &lt;code&gt;status&lt;/code&gt; field that gets rewritten every time something changes, and there is no stored readiness percentage or &lt;code&gt;is_blocked&lt;/code&gt; flag either. The current state is derived from the event history instead.&lt;/p&gt;

&lt;p&gt;Completing a check creates an event. Recording an approval creates another. Opening or resolving a blocker does the same, and the final SHIP or DELAY decision is written into that same history.&lt;/p&gt;

&lt;p&gt;For a launch application, that felt like the right model. A ship decision is exactly the sort of thing somebody may need to explain six weeks later, and constantly overwriting a status field throws away most of that context.&lt;/p&gt;

&lt;p&gt;There was also a practical reason for using an append-only model. In our own testing, insert-style writes had behaved more predictably than some update and delete paths, so this architecture also kept us away from a class of problems we already knew could become awkward.&lt;/p&gt;

&lt;p&gt;It is always convenient when the cleaner design is also the one least likely to ruin your afternoon.&lt;/p&gt;

&lt;h2&gt;
  
  
  From Design to a Working ToolJet App
&lt;/h2&gt;

&lt;p&gt;Once the design and data model were in place, the agent built the actual application.&lt;/p&gt;

&lt;p&gt;The final result contained:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Pages&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;2&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Components&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;94&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Queries&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;12&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Repair cycles&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;2&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Final validation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;✓ 0 errors&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Ninety-four components sounds slightly absurd until you open almost any internal application and remember how much of a UI is containers inside other containers.&lt;/p&gt;

&lt;p&gt;What interested us more was what those 94 components became. They were still ToolJet components, sitting inside a normal ToolJet application with normal queries and bindings.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Feyjykrpuqohnf94k72np.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Feyjykrpuqohnf94k72np.png" alt=" " width="800" height="439"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The agent did not generate a React project next to ToolJet and leave us with another repository to own afterwards. A human can open the application, inspect a query, change a component, alter a binding or continue building visually through the same platform.&lt;/p&gt;

&lt;p&gt;That is probably my favourite part of the MCP approach. AI did the assembly without changing what the application fundamentally is.&lt;/p&gt;

&lt;p&gt;This becomes much more interesting once you stop thinking about the first generated app and start thinking about the hundredth.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Readiness Logic Stayed Simple
&lt;/h2&gt;

&lt;p&gt;Shiproom calculates readiness from three things: required checks, approvals and blockers.&lt;/p&gt;

&lt;p&gt;Required checks carry most of the score, approvals contribute another part, and unresolved critical blockers affect both the readiness calculation and the launch state.&lt;/p&gt;

&lt;p&gt;There is no complicated model behind it, and there should not be. A readiness score does not need to discover a new branch of mathematics; it just needs everyone involved in the launch to be looking at the same definition of ready.&lt;/p&gt;

&lt;p&gt;The checks are grouped into Product, Engineering and Go-to-Market, with ownership and required/optional status visible directly in the war room. As those checks change, the readiness score follows automatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Critical Blocker Means Blocked
&lt;/h2&gt;

&lt;p&gt;One rule we did not want the application to soften was the meaning of a critical blocker.&lt;/p&gt;

&lt;p&gt;If an unresolved critical blocker exists, the launch becomes &lt;strong&gt;Blocked&lt;/strong&gt;. The ship control remains unavailable until that blocker is resolved, the required checks are complete and the necessary approvals are in.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl2ae26pl38wzq4m7qg2u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl2ae26pl38wzq4m7qg2u.png" alt=" " width="800" height="405"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2eh4p11cx4i3sqec2aob.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2eh4p11cx4i3sqec2aob.png" alt=" " width="799" height="372"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The final SHIP or DELAY decision can only be recorded by the launch owner. When that happens, the application stores the person, timestamp, decision and optional note as part of the permanent event history.&lt;/p&gt;

&lt;p&gt;The modal also tells the user exactly why a ship decision is unavailable. One critical blocker unresolved, one required check still open or Legal approval still missing is much more useful than a disabled button with no explanation.&lt;/p&gt;

&lt;p&gt;It is a small interface detail, but “you cannot ship” works considerably better when followed by “and here is why”.&lt;/p&gt;

&lt;h2&gt;
  
  
  We Added AI in One Place
&lt;/h2&gt;

&lt;p&gt;Shiproom has one generative AI feature: a button called &lt;strong&gt;Give me the launch brief&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It produces a short summary that somebody can paste into Slack or another channel, but the model is not given the raw tables and asked to work out whether the launch is healthy.&lt;/p&gt;

&lt;p&gt;The application calculates those facts itself first, including readiness, missing approvals, unfinished checks, blockers and current status. The model receives that prepared state and turns it into readable English.&lt;/p&gt;

&lt;p&gt;The interface also shows what the generated summary was based on.&lt;/p&gt;

&lt;p&gt;This is generally how we prefer AI features inside operational applications to behave. Normal application logic decides what is true, while the model deals with the part it is actually useful for: communicating that information clearly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Then We Tried to Find Things That Were Wrong
&lt;/h2&gt;

&lt;p&gt;The finished application passed static validation with zero errors, but we still checked it in the browser.&lt;/p&gt;

&lt;p&gt;That turned out to be important.&lt;/p&gt;

&lt;p&gt;A visual verification pass compared the running application against the original design and found that the loading state was wrong. Instead of showing the designed skeleton state, the app was effectively displaying a frozen table.&lt;/p&gt;

&lt;p&gt;The application was structurally valid, but the finished screen was still wrong.&lt;/p&gt;

&lt;p&gt;A repair pass corrected the issue, and another check confirmed the fix.&lt;/p&gt;

&lt;p&gt;This is one of the clearer lessons from these builds. Static validation is good at structural problems, while opening the application and actually using it catches a different class of issues.&lt;/p&gt;

&lt;p&gt;You probably want both.&lt;/p&gt;

&lt;h2&gt;
  
  
  One Thing Broke Along the Way
&lt;/h2&gt;

&lt;p&gt;The build was not spotless, which made it much more useful.&lt;/p&gt;

&lt;h3&gt;
  
  
  Seven Queries at Once Was Too Ambitious
&lt;/h3&gt;

&lt;p&gt;At one point the build tried to create seven ToolJet Database queries together.&lt;/p&gt;

&lt;p&gt;Three persisted and four hit a Postgres deadlock.&lt;/p&gt;

&lt;p&gt;Creating the remaining four individually worked immediately.&lt;/p&gt;

&lt;p&gt;This is easy enough to repair in one build, but partial success is awkward for automation because the agent has to work out exactly what survived before it can safely retry anything.&lt;/p&gt;

&lt;p&gt;For operations like this, slower and predictable beats fast and occasionally mysterious.&lt;/p&gt;

&lt;h3&gt;
  
  
  Small Query Changes Were Better Handled by Low-Code
&lt;/h3&gt;

&lt;p&gt;We also ran into cases where changing a tiny part of a large query meant retransmitting the entire query body.&lt;/p&gt;

&lt;p&gt;It worked. But since ToolJet is a low-code platform suitable for business users, I'd prefer changing certain things manually in seconds instead of relying on MCP all the time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Then the Browser Found the Real Bug
&lt;/h2&gt;

&lt;p&gt;This was the most useful failure in the entire build.&lt;/p&gt;

&lt;p&gt;A blocker has an owner, and the application was accidentally deriving that owner from the person responsible for the latest blocker event.&lt;/p&gt;

&lt;p&gt;At first glance, that sounds reasonable. In practice, it meant Maya could open a critical blocker, Dev could add a one-line status update, and the blocker would quietly become Dev's.&lt;/p&gt;

&lt;p&gt;Nothing crashed. The layout looked fine. Validation still returned zero errors.&lt;/p&gt;

&lt;p&gt;The browser QA pass caught it because it performed the actual workflow and then read the result back.&lt;/p&gt;

&lt;p&gt;The fix itself was tiny. Ownership needed to come from the original &lt;code&gt;blocker_open&lt;/code&gt; event rather than whichever blocker event happened most recently.&lt;/p&gt;

&lt;p&gt;This is exactly the sort of bug that makes a green validator slightly less comforting. The application structure was correct, but the application behaviour was not.&lt;/p&gt;

&lt;h2&gt;
  
  
  What We Took Away From It
&lt;/h2&gt;

&lt;p&gt;The surprising part was not that MCP could create 94 components and twelve queries. We expected it to be good at assembly.&lt;/p&gt;

&lt;p&gt;What changed was how little of that assembly needed to involve us.&lt;/p&gt;

&lt;p&gt;The harder decisions remained exactly where they were before. Someone still had to decide whether a critical blocker should merely produce a warning or completely prevent shipping, how readiness should be calculated, who should be allowed to record the final decision and whether the AI brief should work from raw data or facts the application had already derived.&lt;/p&gt;

&lt;p&gt;Someone also still had to notice that posting a blocker update had quietly reassigned its owner.&lt;/p&gt;

&lt;p&gt;Those are application-design problems rather than component-building problems, and MCP does not make them disappear.&lt;/p&gt;

&lt;p&gt;What it removes is a large amount of the mechanical work around them.&lt;/p&gt;

&lt;p&gt;That is also why we are more interested in AI working through ToolJet than in simply generating another codebase. Low-code became useful because every internal application did not need to become its own small software project, and AI should push that abstraction further rather than undo it.&lt;/p&gt;

&lt;p&gt;With MCP, the agent can design the application, create its tables, build its queries, wire the interactions and check the finished result, while what gets left behind is still a ToolJet app that somebody else can open and understand.&lt;/p&gt;

&lt;p&gt;There are still rough edges. We hit query deadlocks, noisy linting, inefficient updates, a naming collision and one genuine logic bug that static validation missed.&lt;/p&gt;

&lt;p&gt;That is probably a more useful result than a flawless demo.&lt;/p&gt;

&lt;p&gt;The distance between describing an internal workflow and seeing a working version of it inside the workspace is getting very small.&lt;/p&gt;

&lt;p&gt;You still have to know what you want the application to do.&lt;/p&gt;

&lt;p&gt;Thankfully, MCP has not automated that part yet.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>mcp</category>
      <category>app</category>
      <category>software</category>
    </item>
    <item>
      <title>How to Integrate ToolJet with ServiceNow for Enhanced Operations 🚀</title>
      <dc:creator>Athulya R</dc:creator>
      <pubDate>Tue, 11 Aug 2026 06:50:28 +0000</pubDate>
      <link>https://dev.to/tooljet/how-to-integrate-tooljet-with-servicenow-for-enhanced-operations-1h1j</link>
      <guid>https://dev.to/tooljet/how-to-integrate-tooljet-with-servicenow-for-enhanced-operations-1h1j</guid>
      <description>&lt;p&gt;Most enterprises already run their operations on &lt;a href="https://www.servicenow.com/in/" rel="noopener noreferrer"&gt;ServiceNow&lt;/a&gt;. The question isn't "should we use ServiceNow." That's already decided. The real question is simpler: can teams build the tools they need, faster, without replacing what's already working?&lt;/p&gt;

&lt;p&gt;This piece isn't a feature list. It's what a platform team actually wants when they bring ToolJet in next to ServiceNow, and what's realistically possible today.&lt;/p&gt;

&lt;h2&gt;
  
  
  What enterprises are usually asking for
&lt;/h2&gt;

&lt;p&gt;When a platform team already on &lt;a href="https://tooljet.com/servicenow" rel="noopener noreferrer"&gt;ServiceNow&lt;/a&gt; looks at ToolJet, the ask is almost never "replace ServiceNow." It's usually some version of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Our employees need a simpler way to submit and track requests than the ServiceNow portal gives them."&lt;/li&gt;
&lt;li&gt;"We want dashboards that show what's happening across our tickets, without building a whole reporting stack."&lt;/li&gt;
&lt;li&gt;"Can we let people update or close things without training them on ServiceNow itself?"&lt;/li&gt;
&lt;li&gt;"Can our automations kick off from somewhere friendlier than ServiceNow's own forms?"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In other words: ServiceNow stays the system of record. ToolJet becomes a faster front door for the specific things people do every day.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why teams look for a front door at all
&lt;/h2&gt;

&lt;p&gt;ServiceNow already has its own builders: Creator Studio for forms, UI Builder for pages, Flow Designer for automation. They're capable tools, built for ServiceNow admins who live in the platform daily. That's exactly who they're designed for.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 1. ServiceNow Studio's landing screen, one of several separate builder products.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fml10731pmc88fwwmmteu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fml10731pmc88fwwmmteu.png" alt="ServiceNow Studio's landing screen, one of several separate builder products" width="800" height="403"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 2. Creator Studio's form builder, used to design a record form field by field.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fb5izyjx3384j2utwat4y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fb5izyjx3384j2utwat4y.png" alt="Creator Studio's form builder, used to design a record form field by field" width="800" height="401"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 3. ServiceNow UI Builder, used to assemble custom pages from components.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Furaibh2cw2rnigf1ptme.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Furaibh2cw2rnigf1ptme.png" alt="ServiceNow UI Builder, used to assemble custom pages from components" width="800" height="399"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 4. Inspecting a component's data and config inside UI Builder.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn9d80wmvfozhwnckuxvu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn9d80wmvfozhwnckuxvu.png" alt="Inspecting a component's data and config inside UI Builder" width="800" height="402"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For a smaller, faster internal tool, though, this can feel like a lot of setup. Each one is a separate product with its own learning curve, and a simple request form can mean hopping between two or three of them just to ship one thing.&lt;/p&gt;

&lt;p&gt;ToolJet's role here isn't "better than ServiceNow's tools." It's a different tradeoff: one canvas, simpler building blocks, and a shorter path from idea to working app, for the kinds of internal tools that don't need everything ServiceNow's native builders offer.&lt;/p&gt;

&lt;h2&gt;
  
  
  ServiceNow App Engine vs ToolJet
&lt;/h2&gt;

&lt;p&gt;This is usually the first question a ServiceNow team asks, since App Engine Studio is ServiceNow's own answer to "build apps faster." It's a fair comparison to make, and worth being direct about.&lt;/p&gt;

&lt;p&gt;App Engine Studio is a capable low-code builder, and if a team is already deep in the ServiceNow ecosystem and wants everything to stay inside it, that's a reasonable choice. It's built and governed the way the rest of ServiceNow is.&lt;/p&gt;

&lt;p&gt;A few practical differences are worth knowing before assuming App Engine is the default answer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Self-hosted is an option with ToolJet, not with App Engine.&lt;/strong&gt; App Engine only runs inside ServiceNow's own cloud. ToolJet is open source and can be self-hosted, which matters for teams with data residency requirements or a preference for running their own infrastructure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost tends to work differently.&lt;/strong&gt; App Engine Studio typically carries its own per-app or per-user licensing on top of a ServiceNow contract. ToolJet has open-source and self-hosted path, so a small internal tool doesn't automatically come with additional per-builder licensing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Getting started doesn't require ServiceNow-specific training.&lt;/strong&gt; Building well in App Engine generally benefits from ServiceNow platform knowledge and, for more complex apps, formal ServiceNow training or certification. ToolJet's canvas and query panel are meant to be picked up directly, without that same ramp-up.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of this makes App Engine a bad choice. It makes sense for teams building deeply native ServiceNow applications that need to live inside that governance model long-term. ToolJet fits better when the goal is a smaller, faster tool, built by a team that doesn't want to invest in ServiceNow-specific platform expertise just to ship it.&lt;/p&gt;

&lt;h2&gt;
  
  
  A real example: an Employee Self-Service Request Portal
&lt;/h2&gt;

&lt;p&gt;To test this, a small internal tool was built end to end: an employee fills out a request (what it's about, how urgent, a short description), hits Submit, and it becomes a real ticket in ServiceNow. Below the form, a live table shows their own requests, pulled straight from ServiceNow.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 5. The finished Employee Self-Service Request Portal, running against a live ServiceNow instance.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F33bs2ed2w2sjlxz1kzpz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F33bs2ed2w2sjlxz1kzpz.png" alt="The finished Employee Self-Service Request Portal, running against a live ServiceNow instance" width="800" height="375"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is a stand-in for the most common ask above: a simpler front end for something ServiceNow already does, but does clunkily for a regular employee.&lt;/p&gt;

&lt;p&gt;Connecting to ServiceNow itself is a one-time setup step: an instance URL, and either basic auth or OAuth 2.0.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 6. ServiceNow data source setup inside ToolJet. Client ID blurred.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fc7qjc6gnonv33chto4xf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fc7qjc6gnonv33chto4xf.png" alt="ServiceNow data source setup inside ToolJet, client ID blurred" width="799" height="435"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How the app was actually built
&lt;/h2&gt;

&lt;p&gt;This is the part that's usually skipped, so here it is plainly.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 7. The full query panel behind the app: every query on the left, myRequests open on the right.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcytgv31av2fwznallcc3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcytgv31av2fwznallcc3.png" alt="The full query panel behind the app: every query on the left, myRequests open on the right" width="800" height="403"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Every piece of the app is backed by a small, focused query against ServiceNow, and every query does one job:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;myRequests&lt;/code&gt;: pulls the employee's existing requests from the incident table (List Records)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;createRequest&lt;/code&gt;: creates a new request when the form is submitted (Create Record)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;getPriorityChoices&lt;/code&gt;: populates the Priority dropdown straight from ServiceNow's own choice list, so it can't go out of sync&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;deleteRecord&lt;/code&gt;: removes a request&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;countTotalRequest, countOpenRequests, countHighPriority, countClosedRequests&lt;/code&gt;: four small counting queries, one per KPI card&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ListFlows&lt;/code&gt;: lists ServiceNow's Flow Designer automations, for later use&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these are complicated. Each one is one ServiceNow operation, configured once, then reused by a component on the canvas: a table, a form field, a stat card, a button. That's the actual "how": connect once, build a small query per data need, then wire components to those queries.&lt;/p&gt;

&lt;p&gt;The result is a form, four live KPI cards (Total, Open, High Priority, Resolved), and a request table, all reading and writing real ServiceNow data, with nothing custom running in between.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generating the app instead of building it by hand
&lt;/h2&gt;

&lt;p&gt;Everything above was built query by query. There's also a newer, faster path worth showing: describing the app in plain language and letting ToolJet generate it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 8. ToolJet's AI builder breaking a plain-language request into a two-phase implementation plan.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3k2olovgevzoqw68dwj6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3k2olovgevzoqw68dwj6.png" alt="ToolJet's AI builder breaking a plain-language request into a two-phase implementation plan" width="800" height="1459"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Given a description of a ServiceNow self-service portal, the AI builder splits the work into phases (first the core request management, then the stats dashboard), and asks for approval before building each phase. That review step matters: it means someone can check the plan before any queries or components get created, rather than getting a black box.&lt;/p&gt;

&lt;p&gt;Once approved, it generates working queries against the ServiceNow data source directly, including ones with real filtering logic, not just plain lookups:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 9. An AI-generated query, list_requests, with dynamic filters built from dropdown and search inputs.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl5qj4i68qdil287nt9x6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl5qj4i68qdil287nt9x6.png" alt="An AI-generated query, list_requests, with dynamic filters built from dropdown and search inputs" width="800" height="300"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And it produces an app shell to match, with its own navigation and layout:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 10. The AI-generated app, a different visual take on the same self-service request portal.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9i0pvy68u1pha77lvkf9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9i0pvy68u1pha77lvkf9.png" alt="The AI-generated app, a different visual take on the same self-service request portal" width="800" height="321"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This doesn't replace understanding the underlying queries. It's still worth knowing what List Records, Create Record, and the rest actually do, since that's what makes the generated output reviewable rather than a leap of faith. What it changes is the starting point: instead of an empty canvas, there's already a working draft to adjust.&lt;/p&gt;

&lt;h2&gt;
  
  
  The takeaway
&lt;/h2&gt;

&lt;p&gt;If an enterprise already runs on ServiceNow and wants faster, friendlier tools without giving up ServiceNow as the system of record, that's already buildable today, whether hand built query by query or generated from a plain-language description. Triggering deeper ServiceNow automation is close, but it needs a bit of setup on the ServiceNow side first. Neither tool replaces the other. They're doing different jobs.&lt;/p&gt;

&lt;p&gt;Everything above was built and tested against a live ServiceNow Personal Developer Instance using ToolJet Cloud, not a demo environment or a simulated data source.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://tooljet.com/schedule-demo" rel="noopener noreferrer"&gt;Book a demo&lt;/a&gt; and see open-source enterprise low-code in action, built for real internal application delivery.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Build An Audio Transcriber and Analyzer using ToolJet and OpenAI🎙️</title>
      <dc:creator>Karan Rathod</dc:creator>
      <pubDate>Thu, 29 Aug 2024 12:00:00 +0000</pubDate>
      <link>https://dev.to/tooljet/build-an-audio-transcriber-and-analyzer-using-tooljet-and-openai-1109</link>
      <guid>https://dev.to/tooljet/build-an-audio-transcriber-and-analyzer-using-tooljet-and-openai-1109</guid>
      <description>&lt;p&gt;In this hands-on tutorial, we’ll learn how to build a powerful audio transcriber and analyzer using ToolJet and Open AI. We'll quickly design an intuitive UI using ToolJet's pre-built &lt;a href="https://docs.tooljet.com/docs/tooljet-concepts/what-are-components/" rel="noopener noreferrer"&gt;components&lt;/a&gt;, and then use the platform's &lt;a href="https://docs.tooljet.com/docs/tooljet-concepts/what-are-queries" rel="noopener noreferrer"&gt;query builder&lt;/a&gt; to interact with Open AI for audio transcription and analysis.&lt;/p&gt;

&lt;p&gt;By the end of this tutorial, we'll have a fundamental structure to build more sophisticated transcription and audio analysis applications.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Prerequisites&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ToolJet&lt;/strong&gt; (&lt;a href="https://github.com/ToolJet/ToolJet" rel="noopener noreferrer"&gt;https://github.com/ToolJet/ToolJet&lt;/a&gt;) : An open-source, low-code platform designed for quickly building and deploying internal tools.&amp;nbsp;Sign up for a free ToolJet cloud account &lt;a href="https://app.tooljet.com/" rel="noopener noreferrer"&gt;here&lt;/a&gt;. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open AI Account&lt;/strong&gt; : Register for an Open AI account to utilize AI-powered features in your ToolJet applications. Sign up &lt;a href="https://openai.com/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Here's a quick preview of what we are going to build:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffvahkytka6o04f3d76g7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffvahkytka6o04f3d76g7.png" alt=" " width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Before you begin, go to the &lt;a href="https://platform.openai.com/api-keys" rel="noopener noreferrer"&gt;Open AI Console&lt;/a&gt;, and copy your secret key. Next, login to your &lt;a href="https://app.tooljet.com/" rel="noopener noreferrer"&gt;ToolJet account&lt;/a&gt;, locate the &lt;strong&gt;Data Sources&lt;/strong&gt; section on the left sidebar, and configure Open AI as a data source using the secret key.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnu5xh4zzjm076lsonfdn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnu5xh4zzjm076lsonfdn.png" alt=" " width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the data source is configured, create a new app called "Speech Insight" from the dashboard. And with that, we are ready to start building our application.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1: Building the UI for the Audio Transcriber
&lt;/h2&gt;

&lt;p&gt;Let's use ToolJet's visual app builder to design our UI.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For the app header, drag and drop an &lt;strong&gt;Icon&lt;/strong&gt; component on the canvas. Navigate to its properties panel on the right, and select the &lt;code&gt;IconBrandDingtalk&lt;/code&gt; icon.&lt;/li&gt;
&lt;li&gt;Drop a &lt;strong&gt;Text&lt;/strong&gt; component next to it, and enter "Speech Insight" under its Data property.&lt;/li&gt;
&lt;li&gt;Change the color of both components to blue (&lt;code&gt;#3E63DD&lt;/code&gt;). This will be the primary color scheme of our app, update the color scheme of the remaining components accordingly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftft7as3w2n2hydid22ll.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftft7as3w2n2hydid22ll.png" alt=" " width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Place a &lt;strong&gt;Container&lt;/strong&gt; component below the header. We will organize the upcoming components inside the Container component.&lt;/li&gt;
&lt;li&gt;Rename it to &lt;code&gt;mainContainer&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpxfzmcowcuthl26rgz0w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpxfzmcowcuthl26rgz0w.png" alt=" " width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;On the top left of the Container component, place a &lt;strong&gt;Text&lt;/strong&gt; component with the label "Output".&lt;/li&gt;
&lt;li&gt;Below it, add another &lt;strong&gt;Container&lt;/strong&gt; and place two &lt;strong&gt;Text&lt;/strong&gt; components inside it. We will use these components to display the transcribed text and feedback. Name them &lt;code&gt;transcribedText&lt;/code&gt; and &lt;code&gt;feedback&lt;/code&gt; respectively.&lt;/li&gt;
&lt;li&gt;Add a &lt;strong&gt;File Picker&lt;/strong&gt; component below it and rename it to &lt;code&gt;uploader&lt;/code&gt;. Change its &lt;code&gt;Accept file types&lt;/code&gt; property to &lt;code&gt;"audio/*"&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Place a &lt;strong&gt;Button&lt;/strong&gt; component below it and rename it to &lt;code&gt;analyzeButton&lt;/code&gt; and add an "Analyze Button" label to it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1hn33z3rpcx6v5omuchx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1hn33z3rpcx6v5omuchx.png" alt=" " width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note:&lt;/em&gt; We are renaming key components to make them easier to reference in other parts of our application.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Finally, place four &lt;strong&gt;Statistics&lt;/strong&gt; components on the right for Fluency, Pronunciation, Intonation, and Vocabulary scores.&lt;/li&gt;
&lt;li&gt;Drop a &lt;strong&gt;Button&lt;/strong&gt; component below it labeled "Copy Output" and rename it to &lt;code&gt;copyButton&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbl7w02dtioa3ibwqga8t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbl7w02dtioa3ibwqga8t.png" alt=" " width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The UI is now ready! Time to configure the interactions with Open AI.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2: Interact With Open AI
&lt;/h2&gt;

&lt;p&gt;In the below steps, we will go through the configuration to interact with Open AI using both REST API and ToolJet's native integration.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Expand the query panel at the bottom and click on the &lt;strong&gt;Add&lt;/strong&gt; button to create a new REST API query. Rename the query to &lt;code&gt;transcribe&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Enter the Open AI URL under the &lt;code&gt;URL&lt;/code&gt; property: &lt;code&gt;https://api.openai.com/v1/audio/transcriptions&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Add a new row under &lt;strong&gt;Header&lt;/strong&gt;, enter &lt;code&gt;Content-Type&lt;/code&gt; as the key and &lt;code&gt;multipart/form-data&lt;/code&gt; as the value.&lt;/li&gt;
&lt;li&gt;Add another key for &lt;code&gt;Authorization&lt;/code&gt;. Enter &lt;code&gt;Bearer &amp;lt;OPEN_AI_KEY&amp;gt;&lt;/code&gt; in the value.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg1wpbxyluwy29otqhipr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg1wpbxyluwy29otqhipr.png" alt=" " width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Under &lt;strong&gt;Body&lt;/strong&gt;, add &lt;code&gt;file&lt;/code&gt; as the key and value as &lt;code&gt;{{ components.uploader.file[0] }}&lt;/code&gt;. This will ensure the audio file selected in our uploader/File Picker component is sent.&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;model&lt;/code&gt; as the key and enter &lt;code&gt;whisper-1&lt;/code&gt; as the value.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd4pgec9dp0htbqp0jfbw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd4pgec9dp0htbqp0jfbw.png" alt=" " width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now if we select an audio file in the uploader/File Picker component and click on the &lt;strong&gt;Run&lt;/strong&gt; button, we will see the transcribed audio as the output.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuwgdw0sq52bsbixc1wfs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuwgdw0sq52bsbixc1wfs.png" alt=" " width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the audio is transcribed, we need to analyze it to provide a score. Let's use the native Open AI integration for this query.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Click on the &lt;strong&gt;Add&lt;/strong&gt; button and add a new query. Select &lt;strong&gt;Open AI&lt;/strong&gt; as the data source for this query. This is the same data source that we had set up at the beginning. Rename it to &lt;code&gt;analyze&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Chat&lt;/strong&gt; as the operation and &lt;strong&gt;Message&lt;/strong&gt; as input, and enter the below prompt:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Based&lt;/span&gt; &lt;span class="nx"&gt;on&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;transcribed&lt;/span&gt; &lt;span class="nx"&gt;audio&lt;/span&gt; &lt;span class="nx"&gt;below&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;provide&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; 
&lt;span class="nx"&gt;JSON&lt;/span&gt; &lt;span class="nx"&gt;object&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="kd"&gt;with&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;following&lt;/span&gt; &lt;span class="nx"&gt;details&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

   &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nc"&gt;Fluency &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;out&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nc"&gt;Pronunciation &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;out&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nc"&gt;Vocabulary &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;out&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nc"&gt;Intonation &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;out&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;A&lt;/span&gt; &lt;span class="nx"&gt;paragraph&lt;/span&gt; &lt;span class="nx"&gt;that&lt;/span&gt; &lt;span class="nx"&gt;gives&lt;/span&gt; &lt;span class="nx"&gt;general&lt;/span&gt; &lt;span class="nx"&gt;feedback&lt;/span&gt; 
&lt;span class="nx"&gt;on&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;transcribed&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;s quality and overall improvement suggestions.

   Return the object in the following format:

   {fluency: "...", pronunciation: "...", 
vocabulary: "...", intonation: "...", feedback: "..."}

   Transcribed text:
   {{queries.transcribe.data.text}}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe2qacepaqbz6z5jozaaj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe2qacepaqbz6z5jozaaj.png" alt=" " width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this prompt, we are using Open AI to perform a detailed analysis of the audio transcription. We are referencing the data returned by the &lt;code&gt;transcribe&lt;/code&gt; query in the prompt along with other scoring criteria. &lt;/p&gt;

&lt;p&gt;Running this query will result in the following output:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcfkw4y3qw3gmgrvmh6y1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcfkw4y3qw3gmgrvmh6y1.png" alt=" " width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Both the queries are ready. As a final step, let's automate the process of triggering the &lt;code&gt;analyze&lt;/code&gt; query every time the &lt;code&gt;transcribe&lt;/code&gt; query is successfully executed.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go back to the &lt;code&gt;transcribe&lt;/code&gt; query, navigate to &lt;strong&gt;Events&lt;/strong&gt; and add a new &lt;a href="https://docs.tooljet.com/docs/tooljet-concepts/what-are-events/" rel="noopener noreferrer"&gt;event&lt;/a&gt; handler.&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Query Success&lt;/strong&gt; as the Event, &lt;strong&gt;Run Query&lt;/strong&gt; as the Action, and &lt;code&gt;analyze&lt;/code&gt; as the Query.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdcbwgj5whjp2u3lzkeso.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdcbwgj5whjp2u3lzkeso.png" alt=" " width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By using events, we have set up the process of triggering the &lt;code&gt;analyze&lt;/code&gt; query after the &lt;code&gt;transcribe&lt;/code&gt; query is triggered and is ready with the output for analysis.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3: Binding the Transcripts and Analysis to Components
&lt;/h2&gt;

&lt;p&gt;Onto the final step. We have built our UI and also built queries to interact with Open AI. Now we can connect it all together and see the app in action.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Select the &lt;strong&gt;Analyze Audio&lt;/strong&gt; button, navigate to its properties panel on the right and add a new event.&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;On click&lt;/strong&gt; as the Event, &lt;strong&gt;Run Query&lt;/strong&gt; as the Action, and &lt;code&gt;transcribe&lt;/code&gt; as the Query.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now every time the Analyze Audio button is clicked, it will trigger the &lt;code&gt;transcribe&lt;/code&gt; query.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Select the &lt;strong&gt;Copy Output&lt;/strong&gt; button and add a new event to it.&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;On click&lt;/strong&gt; as the Event, &lt;strong&gt;Copy to clipboard&lt;/strong&gt; as the Action, and &lt;code&gt;{{queries.analyze.data}}&lt;/code&gt; as the Text.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This configuration will ensure that the analyzed output gets copied when you click on the Copy Output button.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Select the &lt;strong&gt;Text&lt;/strong&gt; component that we had placed to display the transcript. Enter the following value under its Data property:&lt;br&gt;
&lt;strong&gt;Transcript:&lt;/strong&gt; &lt;code&gt;{{queries.transcribe.data.text}}&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Select the &lt;strong&gt;Text&lt;/strong&gt; component to display the feedback. Enter the following value under its Data property:&lt;br&gt;
&lt;strong&gt;Feedback:&lt;/strong&gt; &lt;code&gt;{{JSON.parse(queries.analyze.data).feedback}}&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Note:&lt;/em&gt; We received a JSON string in response to our &lt;code&gt;analyze&lt;/code&gt; query. Therefore, we need to parse it to construct a JavaScript object before displaying it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Select the &lt;strong&gt;Statistics&lt;/strong&gt; component for "Fluency" and enter the below value under its &lt;code&gt;Primary value&lt;/code&gt; property:&lt;br&gt;
&lt;code&gt;{{JSON.parse(queries.analyze.data).fluency}}&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Update the rest of the Statistics components using the same logic.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Our audio transcriber and analyzer is now fully complete. Upload an audio file and click on the Analyze Audio button to see the transcription, feedback, and scores getting populated in the UI. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh4qq0bzys8izun9tvfng.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh4qq0bzys8izun9tvfng.png" alt=" " width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this tutorial, we learned how to create a complete audio transcription and analysis tool using ToolJet and OpenAI. We walked through designing an intuitive user interface, setting up API queries to interact with OpenAI, and binding the results to display transcriptions, feedback, and speech analysis scores.&lt;/p&gt;

&lt;p&gt;To further customize the application, experiment with different UI components to enhance the user experience or integrate additional APIs to analyze other aspects of the audio, such as emotion detection or language translation.&lt;/p&gt;

&lt;p&gt;To learn more, check out ToolJet's official &lt;a href="https://docs.tooljet.com/docs/" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; or connect on &lt;a href="https://tooljet.slack.com" rel="noopener noreferrer"&gt;Slack&lt;/a&gt; for questions or queries.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>ai</category>
      <category>programming</category>
    </item>
    <item>
      <title>Build an Advanced Web Scraping Tool Using ToolJet and Scraper API! 🚀 🛠️</title>
      <dc:creator>Karan Rathod</dc:creator>
      <pubDate>Thu, 15 Aug 2024 12:35:05 +0000</pubDate>
      <link>https://dev.to/tooljet/build-an-advanced-web-scraping-tool-using-tooljet-and-scraper-api-fp7</link>
      <guid>https://dev.to/tooljet/build-an-advanced-web-scraping-tool-using-tooljet-and-scraper-api-fp7</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;Scraping data can be tedious, especially when dealing with dynamic content. However, it becomes a lot more convenient if you can instantly preview the data scraped in the UI. ToolJet, combined with &lt;a href="https://www.scraperapi.com/" rel="noopener noreferrer"&gt;Scraper API&lt;/a&gt;, enables exactly that. This tutorial shows how to set up a script to scrape data using ToolJet and display the results in real time.&lt;/p&gt;

&lt;p&gt;If you've worked with web scraping using &lt;a href="https://colab.research.google.com/" rel="noopener noreferrer"&gt;Google Colab&lt;/a&gt; or tools like &lt;a href="https://www.selenium.dev/" rel="noopener noreferrer"&gt;Selenium&lt;/a&gt;, you know the challenges. Here, we'll take a different approach using JavaScript web scraping, utilizing ToolJet’s visual app builder to manage the data flow.&lt;/p&gt;




&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ToolJet&lt;/strong&gt; (&lt;a href="https://github.com/ToolJet/ToolJet" rel="noopener noreferrer"&gt;https://github.com/ToolJet/ToolJet&lt;/a&gt;) : An open-source, low-code platform designed for quickly building and deploying internal tools.&amp;nbsp;Sign up for a free ToolJet cloud account&amp;nbsp;&lt;a href="https://app.tooljet.com/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Basic knowledge of &lt;strong&gt;JavaScript&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Begin by creating an application named &lt;strong&gt;TJ Web Scraper&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1: Designing the UI to Display the Scraped Data
&lt;/h2&gt;

&lt;p&gt;Let's use ToolJet's &lt;a href="https://docs.tooljet.com/docs/#visual-app-builder" rel="noopener noreferrer"&gt;visual app builder&lt;/a&gt; to quickly design the UI.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Drag and drop a &lt;strong&gt;Container&lt;/strong&gt; &lt;a href="https://docs.tooljet.com/docs/tooljet-concepts/what-are-components" rel="noopener noreferrer"&gt;component&lt;/a&gt; onto the canvas.&lt;/li&gt;
&lt;li&gt;Inside the container, place an &lt;strong&gt;Icon&lt;/strong&gt; component on the left for the logo.&lt;/li&gt;
&lt;li&gt;Place a &lt;strong&gt;Text&lt;/strong&gt; component next to it and enter "TJ Web Scraper" under its Data property.&lt;/li&gt;
&lt;li&gt;Place another &lt;strong&gt;Text&lt;/strong&gt; component on the right to display the total count of scraped products.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv0x4w850i94wfxdb2owt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv0x4w850i94wfxdb2owt.png" alt=" " width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;We are using blue (hex code: &lt;code&gt;#075ab9&lt;/code&gt;) as the primary color for this application; change the color scheme of all the components accordingly.&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add a &lt;strong&gt;Table&lt;/strong&gt; component below the header and a &lt;strong&gt;Button&lt;/strong&gt; component on the bottom-right of the canvas.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp163czpzroo6q8f7iqh3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp163czpzroo6q8f7iqh3.png" alt=" " width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With that, our UI is ready in just a couple of minutes.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2: Writing the JavaScript Code to Scrape Data
&lt;/h2&gt;

&lt;p&gt;In this step, we will utilize the Scraper API to scrape the data from a sample eCommerce website. Here's a preview of the website:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcm9hgeo0ylg94vli8nze.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcm9hgeo0ylg94vli8nze.png" alt=" " width="800" height="464"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The website has a few products with their images, titles, and prices. Additionally, it has a load more button to dynamically load more content.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;To start, expand the &lt;a href="https://docs.tooljet.com/docs/tooljet-concepts/what-are-queries" rel="noopener noreferrer"&gt;Query Panel&lt;/a&gt; at the bottom, and click on the &lt;strong&gt;Add&lt;/strong&gt; button to create a new Run JavaScript code query. Rename the query to &lt;strong&gt;scrapeData&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Set up the main scraping function&lt;/strong&gt;: Create the &lt;code&gt;runMainScript()&lt;/code&gt; function that will coordinate all the logic needed for scraping products.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;runMainScript&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;API_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SCRAPER_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 

    &lt;span class="c1"&gt;// Main logic goes here&lt;/span&gt;
    &lt;span class="c1"&gt;// .....&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Create a request helper&lt;/strong&gt;: Build a helper function &lt;code&gt;makeRequest()&lt;/code&gt; using axios to handle API requests, manage responses, and deal with errors efficiently.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;makeRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.scraperapi.com/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;api_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;API_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;404NotFound&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Error making request to &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Extract product details&lt;/strong&gt;: Define the &lt;code&gt;parseProducts()&lt;/code&gt; function to gather relevant information (like title, price, and image) from the HTML content, filtering out incomplete data. This function uses the HTML selectors tailored to the target website.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;parseProducts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DOMParser&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parseFromString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text/html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.product-item&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.product-name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)?.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.product-price&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)?.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;img&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)?.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;N/A&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;a&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)?.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
    &lt;span class="p"&gt;})).&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;price&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Handle dynamic loading&lt;/strong&gt;: The fetchProducts() function manages the initial page load and any additional AJAX requests, collecting all available products. It saves the total count in a ToolJet &lt;a href="https://docs.tooljet.com/docs/tooljet-concepts/variables/#variables-and-page-variables" rel="noopener noreferrer"&gt;variable&lt;/a&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;fetchProducts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pageUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ajaxUrl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;products&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;initialPageHtml&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;makeRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pageUrl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;initialPageHtml&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;products&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;products&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;products&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;concat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;parseProducts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;initialPageHtml&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ajaxHtml&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;makeRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ajaxUrl&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;?offset=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;ajaxHtml&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newProducts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseProducts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ajaxHtml&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newProducts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;products&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;products&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;concat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newProducts&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Scraped &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;products&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; products so far...`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="c1"&gt;// Save the length of the total products fetched in a ToolJet variable&lt;/span&gt;
        &lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setVariable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;totalProductsScraped&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;products&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;products&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Launch the scraping process&lt;/strong&gt;: Implement the &lt;code&gt;scrapeProducts()&lt;/code&gt; function to trigger the scraping and output the final count of products collected.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;scrapeProducts&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pageUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://www.scrapingcourse.com/button-click&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ajaxUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://www.scrapingcourse.com/ajax/products&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;products&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchProducts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pageUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ajaxUrl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`\nTotal products scraped: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;products&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;products&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Run the script and handle results&lt;/strong&gt;: Execute the scraping process, save the data, and log a sample of the products for review and handles potential errors by setting an error variable.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;scrapeProducts&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;products&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Save all the products fetched from the eCommerce website&lt;/span&gt;
    &lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setVariable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;scrapedProducts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;products&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;Scraped products stored in 'scrapedProducts' variable.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Total products: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;products&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;Sample of scraped products:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;products&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Title: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Price: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;price&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Image: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`URL: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;---&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setVariable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;scrapingError&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;An error occurred:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Time to see the code in action&lt;/strong&gt; : Invoke the &lt;code&gt;runMainScript()&lt;/code&gt; function to start the entire process.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;runMainScript&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Main logic&lt;/span&gt;
    &lt;span class="c1"&gt;// .....&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;runMainScript&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;The code to scrape the data is ready. The above code for web scraping will have to be adjusted based on each target website.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Click on the &lt;strong&gt;Run&lt;/strong&gt; button on the Query Panel and check all the logs that will appear in the browser console.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxty9ufcbhgtpxadvpc3z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxty9ufcbhgtpxadvpc3z.png" alt=" " width="800" height="380"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Step 3: Displaying the Scraped Data
&lt;/h1&gt;

&lt;p&gt;With the UI and code set up, we can now focus on displaying the data on the Table component and triggering the code based on Button click.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Select the &lt;strong&gt;Button&lt;/strong&gt; component, navigate to its properties and create a new &lt;a href="https://docs.tooljet.com/docs/tooljet-concepts/what-are-events" rel="noopener noreferrer"&gt;event&lt;/a&gt; handler.&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;On click&lt;/strong&gt; as the event, &lt;strong&gt;Run Query&lt;/strong&gt; as the action, and &lt;strong&gt;scrapeData&lt;/strong&gt; as the query.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjr5m2csnvbphdbmn3wzt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjr5m2csnvbphdbmn3wzt.png" alt=" " width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Select the &lt;strong&gt;Table&lt;/strong&gt; component, and under its Data property, enter &lt;code&gt;{{variables.scrapedProducts}}&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Select the &lt;strong&gt;Text&lt;/strong&gt; component in the header that is created to display the total count of the products that are scraped. Enter the &lt;code&gt;{{"Total Products Scraped: " + variables.totalProductsScraped || 0 }}&lt;/code&gt; code under its Data property.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We've successfully linked the components with the query. Now, just click the Button component and watch as the data is scraped and displayed in the Table component.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsf64qvo6tzumizp1qcfc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsf64qvo6tzumizp1qcfc.png" alt=" " width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Scraping data effectively requires overcoming challenges like dynamic content and pagination, especially when dealing with AJAX-loaded pages. Using ToolJet combined with Scraper API, you can simplify this process and gain the ability to instantly preview and manage your scraped data through a clean UI.&lt;/p&gt;

&lt;p&gt;Unlike traditional approaches like Selenium web scraping or using Google Colab, this method integrates JavaScript web scraping seamlessly into your workflow with real-time visibility of your data. Building on this foundation, you can scale the tool to handle more complex scraping needs while maintaining an intuitive interface.&lt;/p&gt;

&lt;p&gt;To learn more, check out &lt;a href="https://docs.tooljet.com/docs/" rel="noopener noreferrer"&gt;ToolJet's official documentation&lt;/a&gt; or connect on &lt;a href="https://tooljet.slack.com/" rel="noopener noreferrer"&gt;Slack&lt;/a&gt; with any questions.💡&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webscraping</category>
      <category>tutorial</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Build a Grammarly Alternative Using ToolJet and OpenAI in 10 Minutes📝</title>
      <dc:creator>Karan Rathod</dc:creator>
      <pubDate>Wed, 07 Aug 2024 13:42:21 +0000</pubDate>
      <link>https://dev.to/tooljet/build-a-grammarly-alternative-using-tooljet-and-openai-in-10-minutes-ai9</link>
      <guid>https://dev.to/tooljet/build-a-grammarly-alternative-using-tooljet-and-openai-in-10-minutes-ai9</guid>
      <description>&lt;p&gt;This tutorial will walk you through the process of creating a Grammarly alternative using ToolJet and OpenAI. We will use ToolJet's visual app builder to design an elegant user interface for our application. Then we will use the platform's low-code query builder to connect with OpenAI to perform detailed text analysis. The completed application will allow you to perform four operations:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;List all grammatical errors in your text&lt;/li&gt;
&lt;li&gt;Fix all grammatical errors and return the revised text&lt;/li&gt;
&lt;li&gt;Provide a score for the text based on specific parameters: clarity, coherence, grammar, and engagement&lt;/li&gt;
&lt;li&gt;Make the content sound more natural (ideal for transforming generic AI-generated content with a more organic tone)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Feel free to adjust the structure and functionality of the application to match your exact content-enhancement needs.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Check out &lt;a href="https://dev.to/tooljet/creating-a-csv-to-graph-generator-app-using-tooljet-and-python-libraries-18nb"&gt;this tutorial&lt;/a&gt; to learn how to build a QR Code generator using ToolJet and Python.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Here's a final preview of the application:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa7bujbzi20werwcrfnug.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa7bujbzi20werwcrfnug.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Prerequisites:&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;ToolJet&lt;/strong&gt; (&lt;a href="https://github.com/ToolJet/ToolJet" rel="noopener noreferrer"&gt;https://github.com/ToolJet/ToolJet&lt;/a&gt;) : An open-source, low-code platform designed for quickly building and deploying internal tools.&amp;nbsp;Sign up for a free ToolJet cloud account&amp;nbsp;&lt;a href="https://app.tooljet.com/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;OpenAI Account&lt;/strong&gt; : Register for an OpenAI account to utilize AI-powered features in your applications by accessing APIs such as GPT for advanced natural language processing. Sign up&amp;nbsp;&lt;a href="https://openai.com/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Since integration in ToolJet is &lt;a href="https://docs.tooljet.com/docs/tooljet-concepts/what-are-datasources#adding-a-data-source" rel="noopener noreferrer"&gt;very straightforward&lt;/a&gt;, you can use any other service instead of OpenAI to build the same application.&lt;/em&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Step 1. Set up Your First ToolJet Data Source
&lt;/h2&gt;

&lt;p&gt;In the steps below, we will set up the data source and create a new app.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Access the &lt;a href="https://platform.openai.com/api-keys" rel="noopener noreferrer"&gt;OpenAI Console&lt;/a&gt; and generate a new secret key. Remember to copy this key for later use.&lt;/li&gt;
&lt;li&gt;Sign up for a free &lt;a href="https://app.tooljet.com/" rel="noopener noreferrer"&gt;ToolJet cloud account&lt;/a&gt; if you haven't already and log in.&lt;/li&gt;
&lt;li&gt;On the ToolJet dashboard, locate the Data Sources section on the left sidebar. Here, click on the &lt;strong&gt;Add&lt;/strong&gt; button under the OpenAI plugin.&lt;/li&gt;
&lt;li&gt;In the API Key field, paste the API key you copied from OpenAI Console and click on the Save button&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffcdlsy7ew3wlkey546s3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffcdlsy7ew3wlkey546s3.png" alt=" " width="800" height="423"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use the &lt;strong&gt;Test Connection&lt;/strong&gt; button to ensure the OpenAI plugin is correctly connected to your ToolJet account.&lt;/li&gt;
&lt;li&gt;Finally, click on the "Apps" icon in the left sidebar to navigate to the applications section. &lt;/li&gt;
&lt;li&gt;Select "Create new app" and name your application "Wordly" (or anything else based on your use case), and click on "Create app" to finalize the app creation.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Step 2. Building the User Interface
&lt;/h2&gt;

&lt;p&gt;We will start by creating a minimal UI for the application. The UI will take around five minutes to build. First, we will build the header section, then the input section, and finally, the output section, where we will display the results of the text analysis.&lt;/p&gt;
&lt;h3&gt;
  
  
  Header
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;For the Header, drop an &lt;strong&gt;Icon&lt;/strong&gt; &lt;a href="https://docs.tooljet.com/docs/tooljet-concepts/what-are-components" rel="noopener noreferrer"&gt;component&lt;/a&gt; on the empty canvas and place a &lt;strong&gt;Text&lt;/strong&gt; component next to it. Set its Icon property to "IconTextWrap" and change its font size to 28.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi49tirxxj0hv4b8nce3s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi49tirxxj0hv4b8nce3s.png" alt=" " width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;We are using light blue ("#d9e2fc") as the primary color in this application. Update the color scheme of all the components accordingly.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Input Section
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Drag and drop a &lt;strong&gt;Container&lt;/strong&gt; component below the header and rename it to &lt;em&gt;mainContainer&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fza7li6ydzbo72d1s8426.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fza7li6ydzbo72d1s8426.png" alt=" " width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;We will rename important components like containers and input fields to ensure that we can easily refer and access component-related data inside the app builder.&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add a &lt;strong&gt;Text&lt;/strong&gt; component at the top left of the container with the label 'Enter Text'. &lt;/li&gt;
&lt;li&gt;Place a &lt;strong&gt;Dropdown&lt;/strong&gt; component next to it with the label 'Operation'. Rename it to &lt;em&gt;operationDropdown&lt;/em&gt;. &lt;/li&gt;
&lt;li&gt;Set the dropdown's Option labels to: &lt;code&gt;{{['List Grammatical Errors', 'Fix Grammatical Errors', 'Humanize Content', 'Provide Content Score']}}&lt;/code&gt;. &lt;/li&gt;
&lt;li&gt;Set the Option values to: &lt;code&gt;{{['listGrammaticalErrors', 'fixGrammaticalErrors', 'humanizeContent', 'provideAScore']}}&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Add a &lt;strong&gt;Textarea&lt;/strong&gt; component below and rename it to &lt;em&gt;textInput&lt;/em&gt;.  - Add a &lt;strong&gt;Button&lt;/strong&gt; component labeled 'GENERATE OUTPUT ⚡' and rename it to &lt;em&gt;generateButton&lt;/em&gt;. &lt;/li&gt;
&lt;li&gt;Add another &lt;strong&gt;Button&lt;/strong&gt; component next to it labeled 'COPY 🗒️' and rename it to &lt;em&gt;copyButton&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh991ltcpbllhbe5qd6pl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh991ltcpbllhbe5qd6pl.png" alt=" " width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Output Section
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Add a &lt;strong&gt;Text&lt;/strong&gt; component labeled 'Output'. &lt;/li&gt;
&lt;li&gt;Place another &lt;strong&gt;Text&lt;/strong&gt; component below it to display the output. This component will display the data returned by the OpenAI queries.&lt;/li&gt;
&lt;li&gt;Rename the &lt;strong&gt;Text&lt;/strong&gt; component to &lt;em&gt;output&lt;/em&gt;, and under its Data property, set Markdown as the type.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fok06gbzjw19btyh44r0i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fok06gbzjw19btyh44r0i.png" alt=" " width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Step 3. Creating Queries to Interact With OpenAI
&lt;/h2&gt;

&lt;p&gt;Next, we’ll create four OpenAI &lt;a href="https://docs.tooljet.com/docs/tooljet-concepts/what-are-queries" rel="noopener noreferrer"&gt;queries&lt;/a&gt; and a JavaScript query that will conditionally trigger one of the OpenAI queries based on the selected operation.&lt;/p&gt;
&lt;h3&gt;
  
  
  List Grammatical Errors:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Expand the Query Panel at the bottom of the screen and click on the &lt;strong&gt;Add&lt;/strong&gt; button to create a new query &lt;/li&gt;
&lt;li&gt;Rename this query to &lt;em&gt;listGrammaticalErrors&lt;/em&gt;. &lt;/li&gt;
&lt;li&gt;Select OpenAI as the Data Source and select Chat as the Operation. In the Message input field, enter the following prompt:
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Find&lt;/span&gt; &lt;span class="nx"&gt;all&lt;/span&gt; &lt;span class="nx"&gt;grammatical&lt;/span&gt; &lt;span class="nx"&gt;errors&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;given&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="nx"&gt;List&lt;/span&gt; &lt;span class="nx"&gt;each&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="kd"&gt;with&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;heading&lt;/span&gt; &lt;span class="nx"&gt;and&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="nx"&gt;using&lt;/span&gt; &lt;span class="nx"&gt;Markdown&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="nx"&gt;Include&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;following&lt;/span&gt; &lt;span class="nx"&gt;elements&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

&lt;span class="nx"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Use&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;bold&lt;/span&gt; &lt;span class="nx"&gt;header&lt;/span&gt; &lt;span class="kd"&gt;with&lt;/span&gt; &lt;span class="nx"&gt;an&lt;/span&gt; &lt;span class="nx"&gt;appropriate&lt;/span&gt; &lt;span class="nx"&gt;emoji&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;and&lt;/span&gt; &lt;span class="nx"&gt;mention&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="nx"&gt;Sentence&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Provide&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;sentence&lt;/span&gt; &lt;span class="nx"&gt;which&lt;/span&gt; &lt;span class="nx"&gt;contains&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="nx"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Provide&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;brief&lt;/span&gt; &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="nx"&gt;and&lt;/span&gt; &lt;span class="nx"&gt;two&lt;/span&gt; &lt;span class="nx"&gt;line&lt;/span&gt; &lt;span class="nx"&gt;breaks&lt;/span&gt; &lt;span class="nx"&gt;after&lt;/span&gt; &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;

&lt;span class="nx"&gt;Use&lt;/span&gt; &lt;span class="nx"&gt;these&lt;/span&gt; &lt;span class="nx"&gt;emojis&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;different&lt;/span&gt; &lt;span class="nx"&gt;types&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

&lt;span class="nx"&gt;Subject&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;Verb&lt;/span&gt; &lt;span class="nx"&gt;Agreement&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;🧑‍🏫&lt;/span&gt;
&lt;span class="nx"&gt;Verb&lt;/span&gt; &lt;span class="nx"&gt;Tense&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;⏳&lt;/span&gt;
&lt;span class="nx"&gt;Punctuation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;📝&lt;/span&gt;
&lt;span class="nx"&gt;Spelling&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;🔠&lt;/span&gt;
&lt;span class="nx"&gt;Sentence&lt;/span&gt; &lt;span class="nx"&gt;Structure&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;🏗️&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;…&lt;/span&gt;&lt;span class="nx"&gt;please&lt;/span&gt; &lt;span class="nx"&gt;use&lt;/span&gt; &lt;span class="nx"&gt;other&lt;/span&gt; &lt;span class="nx"&gt;more&lt;/span&gt; &lt;span class="nx"&gt;names&lt;/span&gt; &lt;span class="nx"&gt;and&lt;/span&gt; &lt;span class="nx"&gt;related&lt;/span&gt; &lt;span class="nx"&gt;emojis&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;needed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;Ensure&lt;/span&gt; &lt;span class="nx"&gt;that&lt;/span&gt; &lt;span class="nx"&gt;each&lt;/span&gt; &lt;span class="nx"&gt;section&lt;/span&gt; &lt;span class="nx"&gt;clearly&lt;/span&gt; &lt;span class="nx"&gt;identifies&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;and&lt;/span&gt; &lt;span class="nx"&gt;provides&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foc1v1shypccxjwx7krdu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foc1v1shypccxjwx7krdu.png" alt=" " width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Create Three More OpenAI Queries:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Create three more OpenAI queries - &lt;em&gt;fixGrammaticalErrors&lt;/em&gt;, &lt;em&gt;humanizeContent&lt;/em&gt;, and &lt;em&gt;provideAScore&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  Prompt for &lt;em&gt;fixGrammaticalErrors&lt;/em&gt; query:
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Review&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;following&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;identify&lt;/span&gt; &lt;span class="nx"&gt;all&lt;/span&gt; &lt;span class="nx"&gt;grammatical&lt;/span&gt; &lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;and&lt;/span&gt; &lt;span class="nx"&gt;provide&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;corrected&lt;/span&gt; &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; 

&lt;span class="nx"&gt;Provide&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;Markdown&lt;/span&gt; &lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;

&lt;span class="nx"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="nx"&gt;components&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textInput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  Prompt for &lt;em&gt;humanizeContent&lt;/em&gt; query:
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Rewrite&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;following&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;make&lt;/span&gt; &lt;span class="nx"&gt;it&lt;/span&gt; &lt;span class="nx"&gt;sound&lt;/span&gt; &lt;span class="nx"&gt;more&lt;/span&gt; &lt;span class="nx"&gt;natural&lt;/span&gt; &lt;span class="nx"&gt;and&lt;/span&gt; &lt;span class="nx"&gt;human&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;written&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; 

&lt;span class="nx"&gt;Ensure&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;revised&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="nx"&gt;is&lt;/span&gt; &lt;span class="nx"&gt;engaging&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;clear&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;and&lt;/span&gt; &lt;span class="nx"&gt;maintains&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;original&lt;/span&gt; &lt;span class="nx"&gt;meaning&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; 
&lt;span class="nx"&gt;Provide&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;Markdown&lt;/span&gt; &lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;

&lt;span class="nx"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="nx"&gt;components&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textInput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  Prompt for &lt;em&gt;provideAScore&lt;/em&gt; query:
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Analyze&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;following&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="nx"&gt;and&lt;/span&gt; &lt;span class="nx"&gt;provide&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; 
&lt;span class="nx"&gt;each&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;following&lt;/span&gt; &lt;span class="nx"&gt;categories&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; 
&lt;span class="nx"&gt;clarity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;coherence&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;grammar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;engagement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;and&lt;/span&gt; &lt;span class="nx"&gt;overall&lt;/span&gt; &lt;span class="nx"&gt;quality&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; 

&lt;span class="nx"&gt;Present&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;Markdown&lt;/span&gt; &lt;span class="nx"&gt;format&lt;/span&gt; &lt;span class="kd"&gt;with&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;following&lt;/span&gt; &lt;span class="nx"&gt;structure&lt;/span&gt; &lt;span class="nx"&gt;and&lt;/span&gt; &lt;span class="nx"&gt;no&lt;/span&gt; &lt;span class="nx"&gt;header&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="nx"&gt;Overall&lt;/span&gt; &lt;span class="nx"&gt;Quality&lt;/span&gt;&lt;span class="err"&gt;🌟&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="nx"&gt;Show&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt; &lt;span class="nx"&gt;four&lt;/span&gt; &lt;span class="nx"&gt;paremeters&lt;/span&gt; &lt;span class="nx"&gt;inside&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;block&lt;/span&gt; &lt;span class="nx"&gt;quote&lt;/span&gt;
&lt;span class="nx"&gt;Clarity&lt;/span&gt;&lt;span class="err"&gt;💡&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="nx"&gt;Coherence&lt;/span&gt;&lt;span class="err"&gt;🔗&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="nx"&gt;Grammar&lt;/span&gt;&lt;span class="err"&gt;✅&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="nx"&gt;Engagement&lt;/span&gt;&lt;span class="err"&gt;🔥&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;insert&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;line&lt;/span&gt; &lt;span class="k"&gt;break&lt;/span&gt; &lt;span class="nx"&gt;here&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;-------------------------------------------------------------------&lt;/span&gt;

&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="nx"&gt;Comments&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="nx"&gt;Add&lt;/span&gt; &lt;span class="nx"&gt;comments&lt;/span&gt; &lt;span class="nx"&gt;below&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; 

&lt;span class="nx"&gt;Ensure&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;include&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;header&lt;/span&gt; &lt;span class="nx"&gt;before&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; 

&lt;span class="nx"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="nx"&gt;components&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textInput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Create a Query to Conditionally Trigger the OpenAI Queries
&lt;/h3&gt;

&lt;p&gt;Create a JavaScript query named &lt;em&gt;triggerQuery&lt;/em&gt; with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;switch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;components&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;operationDropdown&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;listGrammaticalErrors&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;queries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;listGrammaticalErrors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fixGrammaticalErrors&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;queries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fixGrammaticalErrors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;humanizeContent&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;queries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;humanizeContent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;provideAScore&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;queries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;provideAScore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpp908mipqcljdhytew93.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpp908mipqcljdhytew93.png" alt=" " width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This JavaScript query will check the selected operation from the operationDropdown component and run the relevant OpenAI query.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4. Configuring Events and Other Functionalities
&lt;/h2&gt;

&lt;p&gt;In the final step, we will configure the events and bind queries with components and see how it all comes together.&lt;/p&gt;

&lt;h3&gt;
  
  
  Update the Output on Query Success:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Add a new Query Success &lt;a href="https://docs.tooljet.com/docs/tooljet-concepts/what-are-queries" rel="noopener noreferrer"&gt;event handler&lt;/a&gt; to the &lt;em&gt;listGrammaticalErrors&lt;/em&gt; query and select Action as Control Component. &lt;/li&gt;
&lt;li&gt;Under Action Options, set the Component as &lt;em&gt;output&lt;/em&gt;, Action as Set text, and Text as &lt;code&gt;{{queries.listGrammaticalErrors.data}}&lt;/code&gt;.
&lt;/li&gt;
&lt;li&gt;Follow the same steps for the remaining three OpenAI queries; this will populate the &lt;code&gt;output&lt;/code&gt; component with the returned data from the query.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmcvq6ipxi2t1ymqiytne.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmcvq6ipxi2t1ymqiytne.png" alt=" " width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Generate Button:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Select the Generate Output button, navigate to its properties panel on the right and create a new event under Events. &lt;/li&gt;
&lt;li&gt;For the event configuration, select Action as Control Component. - Under Action Options, set the Component as &lt;em&gt;output&lt;/em&gt;, Action as Clear. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu2kt6xgma7o61kiuiqj2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu2kt6xgma7o61kiuiqj2.png" alt=" " width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This event will clear the output component every time the button is clicked.&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add another event to the Generate Output button to run the JavaScript query when it is clicked.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhhdl871na53eg5pyq2xa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhhdl871na53eg5pyq2xa.png" alt=" " width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This event will trigger the JavaScript query. The JavaScript query will then trigger one of the OpenAI queries based on the selected operation from the dropdown, and the output component will be populated with the latest data.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Copy Button
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Create a new event for the Copy button, select &lt;code&gt;Copy to clipboard&lt;/code&gt; as the event, and enter &lt;code&gt;{{components.output.text}}&lt;/code&gt; under its Text property.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6asx7pjeey5qcu7esi9z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6asx7pjeey5qcu7esi9z.png" alt=" " width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;With this setting, every time you click on the Copy button, the text inside the output component will be copied to the clipboard.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The functionality of our application is now fully configured. Enter some text in the input field, click on the Generate Output button and test the output for the available operations.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh51801z0k2c5we3e16k4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh51801z0k2c5we3e16k4.png" alt=" " width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Congratulations! You've built a Grammarly alternative in less than 10 minutes. Using ToolJet's Continue adjusting the application's functionality to meet your specific text analysis needs. You can create new OpenAI queries for a variety of content enhancement scenarios such as changing the tone of the content, improving SEO, changing the language of the output content, improving clarity, etc.&lt;/p&gt;

&lt;p&gt;To learn more, check out the &lt;a href="https://docs.tooljet.com/docs/" rel="noopener noreferrer"&gt;ToolJet documentation&lt;/a&gt; or connect with us on &lt;a href="https://tooljet.slack.com/" rel="noopener noreferrer"&gt;Slack&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>beginners</category>
      <category>openai</category>
    </item>
    <item>
      <title>Creating a CSV to Graph Generator App Using ToolJet and Python Libraries 🚀</title>
      <dc:creator>Karan Rathod</dc:creator>
      <pubDate>Thu, 25 Jul 2024 12:53:21 +0000</pubDate>
      <link>https://dev.to/tooljet/creating-a-csv-to-graph-generator-app-using-tooljet-and-python-libraries-18nb</link>
      <guid>https://dev.to/tooljet/creating-a-csv-to-graph-generator-app-using-tooljet-and-python-libraries-18nb</guid>
      <description>&lt;p&gt;In this tutorial, we'll create a CSV to Graph Generator app using ToolJet and Python code. This app enables users to upload a CSV file and generate various types of graphs, including line, scatter, bar, histogram, and box plots. Since ToolJet supports Python (and JavaScript) code out of the box, we'll incorporate Python code and the &lt;a href="https://matplotlib.org/" rel="noopener noreferrer"&gt;matplotlib&lt;/a&gt; library to handle the graph generation. Additionally, we'll use ToolJet's customizable pre-built components to design an intuitive user interface in minutes.&lt;/p&gt;

&lt;p&gt;Here's a quick preview of the complete application:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsmwtomyqbpcg31pyg53y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsmwtomyqbpcg31pyg53y.png" alt=" " width="800" height="534"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Check out &lt;a href="https://blog.tooljet.com/create-a-qr-code-generator-using-tooljet-in-5-minutes/" rel="noopener noreferrer"&gt;this&lt;/a&gt; tutorial to learn how to develop a QR code generator using ToolJet.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;ToolJet&lt;/strong&gt;(&lt;a href="https://github.com/ToolJet/ToolJet" rel="noopener noreferrer"&gt;https://github.com/ToolJet/ToolJet&lt;/a&gt;) : An open-source, low-code business application builder. &lt;a href="https://www.tooljet.com/signup" rel="noopener noreferrer"&gt;Sign up&lt;/a&gt; for a free ToolJet cloud account or &lt;a href="https://docs.tooljet.com/docs/setup/try-tooljet/" rel="noopener noreferrer"&gt;run ToolJet on your local machine&lt;/a&gt; using Docker.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Basic knowledge of &lt;strong&gt;Python&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Begin by creating an application named &lt;strong&gt;CSV To Graph Generator&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Designing the User Interface
&lt;/h2&gt;

&lt;p&gt;Let's use ToolJet's &lt;a href="https://blog.tooljet.com/build-graphs-using-csv-files-with-tooljet-and-python-libraries/" rel="noopener noreferrer"&gt;visual app builder&lt;/a&gt; to quickly design the UI.&lt;/p&gt;

&lt;h3&gt;
  
  
  Designing the Header
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Drag and drop a &lt;strong&gt;Text&lt;/strong&gt; &lt;a href="https://docs.tooljet.com/docs/tooljet-concepts/what-are-components" rel="noopener noreferrer"&gt;component&lt;/a&gt; onto the canvas for the header. Rename this component to &lt;code&gt;headerText&lt;/code&gt; and set its text to "CSV TO GRAPH GENERATOR."&lt;/li&gt;
&lt;li&gt;Customize its styling properties to have:

&lt;ul&gt;
&lt;li&gt;Text size: 25&lt;/li&gt;
&lt;li&gt;Font weight: bolder&lt;/li&gt;
&lt;li&gt;Text color: #375fcfff&lt;/li&gt;
&lt;li&gt;Text alignment: right&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;This will serve as the header text of your app.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add an &lt;strong&gt;Icon&lt;/strong&gt; component, naming it &lt;code&gt;logo&lt;/code&gt;. Set the icon to &lt;code&gt;IconChartAreaFilled&lt;/code&gt; and the icon color to #375fcfff. Position it appropriately on the canvas to complement the header text.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9sgdf6ag2pd2ioe042m1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9sgdf6ag2pd2ioe042m1.png" alt=" " width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating the CSV Upload Section
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Drag a &lt;strong&gt;Container&lt;/strong&gt; component onto the canvas. Style this container with:

&lt;ul&gt;
&lt;li&gt;Border radius: 7&lt;/li&gt;
&lt;li&gt;Background color: #fff&lt;/li&gt;
&lt;li&gt;Box shadow: 2px 2px 1px 1px #aec0f5ff&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;This container will hold all the input fields and output preview.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Inside the container, add a &lt;strong&gt;File Picker&lt;/strong&gt; component and rename it to &lt;code&gt;fileUploader&lt;/code&gt;. Set its &lt;strong&gt;Accept file types&lt;/strong&gt; property to &lt;code&gt;{"csv/*"}&lt;/code&gt; to accept CSV files. Enable the &lt;strong&gt;Parse content&lt;/strong&gt; toggle, and select &lt;strong&gt;File type&lt;/strong&gt; as CSV.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Next, add a &lt;strong&gt;Dropdown&lt;/strong&gt; component next to the File Picker, rename it to &lt;code&gt;graphSelectionDropdown&lt;/code&gt;. Set the component's &lt;strong&gt;Option values&lt;/strong&gt; to &lt;code&gt;["line", "scatter", "bar", "histogram", "box"]&lt;/code&gt; and &lt;strong&gt;Option labels&lt;/strong&gt; to &lt;code&gt;["Line", "Scatter", "Bar", "Histogram", "Box"]&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Below the dropdown, add a &lt;strong&gt;Button&lt;/strong&gt; component and rename it to &lt;code&gt;generateGraphButton&lt;/code&gt;. Set its text to "Generate Graph".&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0qbwuxnouuvweuakridt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0qbwuxnouuvweuakridt.png" alt=" " width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href="https://docs.tooljet.com/docs/tooljet-concepts/how-to-access-values" rel="noopener noreferrer"&gt;Double curly braces&lt;/a&gt; in ToolJet are used to pass custom code and refer or access values.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating the Image Preview
&lt;/h3&gt;

&lt;p&gt;To display the generated graph, add an &lt;strong&gt;Image&lt;/strong&gt; component below the File Picker and Button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3ad9s1lhax4czbdvyubc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3ad9s1lhax4czbdvyubc.png" alt=" " width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The UI is now ready! &lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2: Using Python Libraries through Queries
&lt;/h2&gt;

&lt;p&gt;ToolJet's &lt;a href="https://docs.tooljet.com/docs/tooljet-concepts/what-are-queries" rel="noopener noreferrer"&gt;query&lt;/a&gt; builder offers a convenient way to use custom code or connect with external data sources, services, and libraries. We will use it to create a Python query that uses the CSV data and interacts with the matplotlib library to generate graphs.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Expand the query panel at the bottom, create a new query named &lt;code&gt;generateGraph&lt;/code&gt;, and choose &lt;strong&gt;Python&lt;/strong&gt; as the data source.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Enter the below code in your Python query: &lt;br&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;matplotlib.pyplot&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;plt&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pandas&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;numpy&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BytesIO&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_plot&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;csv_string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;components&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fileUploader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;
        &lt;span class="n"&gt;graph_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;components&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;graphSelectionDropdown&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Debug info:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;csv_string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;graph_type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;StringIO&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;csv_string&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;columns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tolist&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CSV must contain at least two columns&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;x_column&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;y_columns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:]&lt;/span&gt;

        &lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;figure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;figsize&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;graph_type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;line&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;y_columns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x_column&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;title&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Line Plot: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;x_column&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; vs &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y_columns&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;legend&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;xticks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rotation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ha&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;right&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;graph_type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;scatter&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y_columns&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scatter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;y_columns&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;y_columns&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]])&lt;/span&gt;
                &lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;title&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Scatter Plot: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;y_columns&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; vs &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;y_columns&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;xlabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y_columns&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
                &lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ylabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y_columns&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scatter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x_column&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;y_columns&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]])&lt;/span&gt;
                &lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;title&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Scatter Plot: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;x_column&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; vs &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;y_columns&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;xlabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x_column&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ylabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y_columns&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;graph_type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;bar&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x_column&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
            &lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.8&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y_columns&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y_columns&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;xi&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;xi&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;xlabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x_column&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ylabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Value&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;title&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Bar Plot: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y_columns&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;xticks&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;xi&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y_columns&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;xi&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x_column&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;rotation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ha&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;right&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;legend&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;graph_type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;histogram&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;y_columns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;bins&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;alpha&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;title&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Histogram: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y_columns&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;xlabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Value&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ylabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Frequency&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;legend&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;graph_type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;box&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;boxplot&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;y_columns&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;title&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Box Plot: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y_columns&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;xticks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y_columns&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;y_columns&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rotation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ha&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;right&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ylabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Value&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Invalid graph type: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;graph_type&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;. Choose &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;line&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;scatter&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;bar&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;histogram&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;, or &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;box&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tight_layout&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="nb"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BytesIO&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;savefig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;png&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;seek&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;img_str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;b64encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getvalue&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;img_str&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Error in create_plot: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

&lt;span class="nf"&gt;create_plot&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fspopgg01n6h5tjbznt29.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fspopgg01n6h5tjbznt29.png" alt=" " width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once this is configured, you can select a CSV file in the File Picker, select the graph type, and click on the &lt;strong&gt;Run&lt;/strong&gt; button in the query panel. You can see the output generated by the query under preview.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3: Connecting the UI Components with the Query
&lt;/h2&gt;

&lt;p&gt;Time to bind the query to the components.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuring the Image Preview
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;In the &lt;strong&gt;Data&lt;/strong&gt; property of the &lt;strong&gt;Image&lt;/strong&gt; component, enter the following code to bind the query's result: &lt;br&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data:image;base64,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;queries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;generateGraph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;For the &lt;strong&gt;Loading state&lt;/strong&gt; property of the Image component, click on the fx button to enter custom code and enter the below code: &lt;br&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="nx"&gt;queries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;generateGraph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;



&lt;h3&gt;
  
  
  Triggering the Query Based on Button Click
&lt;/h3&gt;

&lt;p&gt;Bind the query to the &lt;strong&gt;generateGraphButton&lt;/strong&gt; by adding an &lt;strong&gt;on Click&lt;/strong&gt; &lt;a href="https://docs.tooljet.com/docs/tooljet-concepts/what-are-events" rel="noopener noreferrer"&gt;event&lt;/a&gt; that triggers the &lt;strong&gt;generateGraph&lt;/strong&gt; query. This setup ensures that the script runs and generates the graph when the button is clicked.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fihus5ca5efk5gg60tz6s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fihus5ca5efk5gg60tz6s.png" alt=" " width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After this setup, every time you upload a CSV file, select a graph type, and click on the &lt;strong&gt;generateGraphButton&lt;/strong&gt;, the graph will be created and a preview will be visible in the Image component.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa0cemhi6h8kz9i6ag0b2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa0cemhi6h8kz9i6ag0b2.png" alt=" " width="800" height="532"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;By following these steps, we've successfully built a dynamic and user-friendly CSV to Graph Generator app with ToolJet. We utilized ToolJet's visual app builder to craft an elegant and intuitive UI, used the query builder to integrate a Python library for graph generation, and demonstrated how to customize ToolJet applications to deliver a smooth user experience.&lt;/p&gt;

&lt;p&gt;To learn and explore more about ToolJet, check out the &lt;a href="https://docs.tooljet.io" rel="noopener noreferrer"&gt;ToolJet docs&lt;/a&gt; or connect with us and post your queries on our &lt;a href="https://tooljet.slack.com/" rel="noopener noreferrer"&gt;Slack channel&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>python</category>
      <category>beginners</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Build an HTML To Markdown Converter using ToolJet📋</title>
      <dc:creator>Karan Rathod</dc:creator>
      <pubDate>Thu, 04 Jul 2024 13:05:46 +0000</pubDate>
      <link>https://dev.to/tooljet/build-an-html-to-markdown-converter-using-tooljet-4p7h</link>
      <guid>https://dev.to/tooljet/build-an-html-to-markdown-converter-using-tooljet-4p7h</guid>
      <description>&lt;p&gt;In this tutorial, we will walk through the steps to build an HTML to Markdown Converter using ToolJet. This application allows users to input HTML code and get the corresponding Markdown output which can be copied with a button click. Using ToolJet's intuitive pre-built components, we'll design an elegant UI in just 5 minutes. The HTML to markdown conversion will be managed by the &lt;a href="https://github.com/mixmark-io/turndown?utm_source=cdnjs&amp;amp;utm_medium=cdnjs_link&amp;amp;utm_campaign=cdnjs_library" rel="noopener noreferrer"&gt;Turndown JavaScript library&lt;/a&gt;, while the Markdown preview will be rendered using the &lt;a href="https://www.npmjs.com/package/react-markdown" rel="noopener noreferrer"&gt;react-markdown&lt;/a&gt; library.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;ToolJet's Run JavaScript query and Text component can be utilized to convert HTML to Markdown and preview the content without using external libraries. However, in this tutorial, we are looking to demonstrate the process of using 3rd party libraries in ToolJet.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Here's a preview of the complete application:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnldb333nq4ytyhsfpcyf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnldb333nq4ytyhsfpcyf.png" alt=" " width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Prerequisites:
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;ToolJet&lt;/strong&gt;(&lt;a href="https://github.com/ToolJet/ToolJet" rel="noopener noreferrer"&gt;https://github.com/ToolJet/ToolJet&lt;/a&gt;) : An open-source, low-code business application builder. &lt;a href="https://www.tooljet.com/signup" rel="noopener noreferrer"&gt;Sign up&lt;/a&gt; for a free ToolJet cloud account or &lt;a href="https://docs.tooljet.com/docs/setup/try-tooljet/" rel="noopener noreferrer"&gt;run ToolJet on your local machine&lt;/a&gt; using Docker.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Basics of JavaScript&lt;/strong&gt;: A basic understanding of JavaScript is essential for this tutorial since we will use JavaScript and React libraries to handle the logic for converting HTML to Markdown and previewing the markdown content.&lt;/p&gt;

&lt;p&gt;To begin, create a new application named &lt;strong&gt;HTML To Markdown Converter&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1: Creating the UI
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Header
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Add a Container &lt;a href="https://docs.tooljet.com/docs/tooljet-concepts/what-are-components" rel="noopener noreferrer"&gt;component&lt;/a&gt; at the top of the canvas and rename it to &lt;code&gt;headerContainer&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Add an Icon and a Text component inside the Container. Rename them to &lt;code&gt;logo&lt;/code&gt; and &lt;code&gt;headerText&lt;/code&gt; respectively.&lt;/li&gt;
&lt;li&gt;Set the text to &lt;strong&gt;"HTML to Markdown Converter"&lt;/strong&gt; and select an appropriate icon for the app in the Icon component.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F71o2pr63y20cgtv0uyp5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F71o2pr63y20cgtv0uyp5.png" alt=" " width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Input Section
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Add another Container component on the left and rename it to &lt;code&gt;inputContainer&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Inside the container, add a Text component labeled &lt;strong&gt;"Enter HTML Code:"&lt;/strong&gt;. Rename it to &lt;code&gt;enterHTMLLabel&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Below the label, add a Textarea component for the HTML input and rename it to &lt;code&gt;htmlInput&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Output Section
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Add a container next to the input section and rename it to &lt;code&gt;outputContainer&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Add a Text component labeled &lt;strong&gt;"Markdown Preview:"&lt;/strong&gt; below the input section. Rename it to &lt;code&gt;markdownLabel&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Add a Custom Component to display the Markdown output and rename it to &lt;code&gt;markdownOutput&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Buttons
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Add a Button component labeled &lt;strong&gt;"Convert"&lt;/strong&gt; below the Textarea component. Rename the button to &lt;code&gt;convertButton&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Add another Button component labeled &lt;strong&gt;"Copy Markdown"&lt;/strong&gt; besides the Convert button and rename it to &lt;code&gt;copyButton&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkhmc06dd6h8c0xas7wgn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkhmc06dd6h8c0xas7wgn.png" alt=" " width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The UI of the application is done. Time to focus on the functionality.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2: Adding Queries and Events
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Import Turndown Library
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Create a Run JavaScript code &lt;a href="https://docs.tooljet.com/docs/tooljet-concepts/what-are-queries" rel="noopener noreferrer"&gt;query&lt;/a&gt; named &lt;code&gt;importTurndownLibrary&lt;/code&gt; to import the Turndown library.&lt;/li&gt;
&lt;li&gt;Use the following code in the query to import the library:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Function to add script dynamically&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;addScript&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;scriptTag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;script&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;scriptTag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;scriptTag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;load&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;scriptTag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scriptTag&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Importing turndown&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;addScript&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://cdnjs.cloudflare.com/ajax/libs/turndown/7.0.0/turndown.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Showing a success alert&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;showAlert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;success&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Turndown JS is imported&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;turndownService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;turndown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;components&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textarea1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Enable the &lt;strong&gt;Run this query on application load?&lt;/strong&gt; toggle to automatically run this query every time the app loads.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl99vh8a3m6uwjx92filx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl99vh8a3m6uwjx92filx.png" alt=" " width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Convert HTML to Markdown
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Create another Run JavaScript code query named &lt;code&gt;convertToMarkdown&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Use the following code in the query to convert the text entered in the &lt;code&gt;htmlInput&lt;/code&gt; component to markdown:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;turndownService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TurndownService&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;markdown&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;turndownService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;turndown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;components&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;htmlInput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;markdown&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe60btw0gozshpmnsyagj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe60btw0gozshpmnsyagj.png" alt=" " width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Button Actions
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;For the &lt;strong&gt;Convert&lt;/strong&gt; button, create a new &lt;strong&gt;On click&lt;/strong&gt; &lt;a href="https://docs.tooljet.com/docs/tooljet-concepts/what-are-events" rel="noopener noreferrer"&gt;event&lt;/a&gt;, set the Action as &lt;strong&gt;Run Query&lt;/strong&gt;, and select &lt;code&gt;convertToMarkdown&lt;/code&gt; as the Query.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4yl5ig5foh5nen0z8b2r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4yl5ig5foh5nen0z8b2r.png" alt=" " width="800" height="525"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For the &lt;strong&gt;Copy Markdown&lt;/strong&gt; button, create a new &lt;strong&gt;On click&lt;/strong&gt; event, set the Action as &lt;strong&gt;Copy to clipboard&lt;/strong&gt; the &lt;code&gt;{{queries.convertToMarkdown.data}}&lt;/code&gt; as the Text.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyw3ooa0b9461f6kiri88.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyw3ooa0b9461f6kiri88.png" alt=" " width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Most of the key functionality is now configured. In the next step, we will use a Custom Component to import a React library to preview the markdown content.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3: Creating Markdown Preview
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Add Custom Component
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;In the Custom Component, add the data that will be returned by the &lt;code&gt;convertToMarkdown&lt;/code&gt; query:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{{&lt;/span&gt;
   &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;preview&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;queries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;convertToMarkdown&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Use the following code to render the Markdown preview:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://cdn.skypack.dev/react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;ReactDOM&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://cdn.skypack.dev/react-dom&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Container&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://cdn.skypack.dev/@material-ui/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Markdown&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://esm.sh/react-markdown@9&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MyCustomComponent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Container&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Markdown&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;preview&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Markdown&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Container&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ConnectedComponent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Tooljet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connectComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;MyCustomComponent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;ReactDOM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ConnectedComponent&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz2a6l46zc7mkbivkij35.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz2a6l46zc7mkbivkij35.png" alt=" " width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With this, the HTML to Markdown converter is ready. Time to see it in action!&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4: Preview and Testing
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Test the application by entering various HTML snippets and checking the Markdown output.&lt;/li&gt;
&lt;li&gt;Ensure that the &lt;strong&gt;Copy Markdown&lt;/strong&gt; button works correctly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx52gtfv8b8ntysalaeb5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx52gtfv8b8ntysalaeb5.png" alt=" " width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;We have successfully built an HTML to Markdown Converter using ToolJet in just minutes! This application is designed to simplify your content formatting process, and you can easily expand its functionality based on your specific requirements. Explore the capabilities of ToolJet to create similar projects and enhance your workflow.&lt;/p&gt;

&lt;p&gt;You can check out &lt;a href="https://docs.tooljet.com" rel="noopener noreferrer"&gt;ToolJet docs&lt;/a&gt; for further learning or join the ToolJet Slack community for doubts and queries.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Building a Regex Generator with Gemini AI and ToolJet ⚙️</title>
      <dc:creator>Aman Regu</dc:creator>
      <pubDate>Tue, 02 Jul 2024 13:02:20 +0000</pubDate>
      <link>https://dev.to/tooljet/building-a-regex-generator-with-gemini-ai-and-tooljet-43ng</link>
      <guid>https://dev.to/tooljet/building-a-regex-generator-with-gemini-ai-and-tooljet-43ng</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;This tutorial will guide you through the process of building an AI-powered Regex Generator using &lt;a href="https://github.com/ToolJet/ToolJet" rel="noopener noreferrer"&gt;ToolJet&lt;/a&gt; , a low-code visual app builder, and the Gemini API, a powerful natural language processing API. The resulting application will enable users to input requests in plain English, which will then be translated into Regular Expressions&amp;nbsp;(Regex). We'll use ToolJet's visual app builder to create a user-friendly UI, and ToolJet's low-code query builder to connect it to the Gemini API endpoint.&lt;/p&gt;




&lt;h2&gt;
  
  
  Prerequisites:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ToolJet&lt;/strong&gt; (&lt;a href="https://github.com/ToolJet/ToolJet" rel="noopener noreferrer"&gt;https://github.com/ToolJet/ToolJet&lt;/a&gt; ): An open-source, low-code business application builder.&amp;nbsp;&lt;a href="https://www.tooljet.com/signup" rel="noopener noreferrer"&gt;Sign up&lt;/a&gt; for a free ToolJet cloud account or &lt;a href="https://docs.tooljet.com/docs/setup/try-tooljet/" rel="noopener noreferrer"&gt;run ToolJet on your local machine&lt;/a&gt; using Docker.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gemini API Key&lt;/strong&gt; : Log into &lt;a href="https://aistudio.google.com/app/apikey" rel="noopener noreferrer"&gt;Google AI Studio&lt;/a&gt; using your existing Google credentials. Within the AI Studio interface, you can locate and copy your API key.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is a quick preview of our final application:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0i4mm3nmx59f29efkj7z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0i4mm3nmx59f29efkj7z.png" alt=" " width="800" height="452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Login to your &lt;a href="https://app.tooljet.com/" rel="noopener noreferrer"&gt;ToolJet account&lt;/a&gt;. Navigate to the ToolJet dashboard and click on the Create new app button on the top left corner. ToolJet comes with 45+ built-in components. This will let us set up our UI in no time.&lt;/p&gt;




&lt;h2&gt;
  
  
  Assembling our UI
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Drag and drop the &lt;strong&gt;Container&lt;/strong&gt; component onto the canvas from the component library on the right side. Adjust the height and width of the &lt;strong&gt;Container&lt;/strong&gt; component appropriately.&lt;/li&gt;
&lt;li&gt;Similarly, drag-and-drop the &lt;strong&gt;Icon&lt;/strong&gt; and &lt;strong&gt;Text&lt;/strong&gt; component onto the canvas. We'll use them as our header.&lt;/li&gt;
&lt;li&gt;For the &lt;strong&gt;Icon&lt;/strong&gt; component, navigate to the properties panel on the right and select the appropriate icon under the &lt;strong&gt;Icon&lt;/strong&gt; property.&lt;/li&gt;
&lt;li&gt;Change the colour of the &lt;strong&gt;Icon&lt;/strong&gt; and &lt;strong&gt;Text&lt;/strong&gt; component according to your preference.&lt;/li&gt;
&lt;li&gt;Drag and drop one &lt;strong&gt;Text&lt;/strong&gt; component and one  &lt;strong&gt;Textarea&lt;/strong&gt; component inside your canvas. We'll use these components for the label and input for our text query. Rename the &lt;strong&gt;Textarea&lt;/strong&gt; component to &lt;em&gt;textQueryInput&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Again, drag-and-drop two &lt;strong&gt;Text&lt;/strong&gt; components and two &lt;strong&gt;Text Input&lt;/strong&gt; components inside your container. We'll use them for displaying the generated regex expressions and testing a string against the generated regex expression respectively.&lt;/li&gt;
&lt;li&gt;Rename the &lt;strong&gt;Text Input&lt;/strong&gt; components as &lt;em&gt;generatedRegex&lt;/em&gt; and &lt;em&gt;testString&lt;/em&gt; respectively.&lt;/li&gt;
&lt;li&gt;Lastly, drag and drop two &lt;strong&gt;Button&lt;/strong&gt; components inside your container. We'll use them for initiating the regex expression generation and adding a &lt;strong&gt;copy to clipboard&lt;/strong&gt; functionality respectively.&lt;/li&gt;
&lt;li&gt;Rename the added &lt;strong&gt;Button&lt;/strong&gt; components to &lt;em&gt;generateRegex&lt;/em&gt; and &lt;em&gt;copyToClipboard&lt;/em&gt; respectively.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frulz6yc960geeqvosxwq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frulz6yc960geeqvosxwq.png" alt=" " width="800" height="452"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Setting up Queries
&lt;/h2&gt;

&lt;p&gt;ToolJet allows you to connect to various external data sources, including databases, external APIs, and services using its powerful low code query builder. For this tutorial, We'll be using the REST API query feature to connect with the &lt;strong&gt;Gemini&lt;/strong&gt; API endpoints.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Using ToolJet's Workspace Constants feature, create a new constant named &lt;strong&gt;GEMINI_API_KEY&lt;/strong&gt; with your Gemini API key.&lt;/li&gt;
&lt;li&gt;In the query panel, click on the &lt;strong&gt;+ Add&lt;/strong&gt; button and choose the &lt;strong&gt;REST API&lt;/strong&gt; option.&lt;/li&gt;
&lt;li&gt;Rename the query to &lt;em&gt;getRegexPattern.&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;In the Request parameter, choose &lt;strong&gt;POST&lt;/strong&gt; as the Method from the drop-down and paste the following URL.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-pro:generateContent?key={{constants.GEMINI_API_KEY}}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Navigate to the Body section of &lt;em&gt;getRegexPattern&lt;/em&gt;. Toggle on &lt;strong&gt;Raw JSON&lt;/strong&gt; and enter the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{{
  `{
   "contents": [{
     "parts": [{
       "text": "Text Prompt: Based on pattern, generate a regex without any code highlighting, formatting or backticks, Pattern: ${JSON.stringify(components.textQueryInput.value).replace(/\\?"/g, '\\"')}"
      },],
    },],
  }`
}}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Binding Queries to the UI Components
&lt;/h2&gt;

&lt;p&gt;Now that we have successfully added our UI and the query, the next step is to integrate them.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Select the &lt;em&gt;generateRegex&lt;/em&gt; &lt;strong&gt;Button&lt;/strong&gt; component and navigate to the properties panel on the right. Click on the &lt;strong&gt;+ New event handler&lt;/strong&gt; button. Change the &lt;strong&gt;Action&lt;/strong&gt; to &lt;strong&gt;Run query&lt;/strong&gt; and select the &lt;em&gt;getRegexPattern&lt;/em&gt; query. This will trigger the &lt;em&gt;getRegexPattern&lt;/em&gt; every time we click the &lt;em&gt;generateRegex&lt;/em&gt; button.&lt;/li&gt;
&lt;li&gt;Next, select the &lt;em&gt;generatedRegex&lt;/em&gt; &lt;strong&gt;Text Input&lt;/strong&gt; component and navigate to the properties panel on the right. Paste the following code into the &lt;strong&gt;Default value&lt;/strong&gt; field:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{{queries.getRegexPattern.data.candidates[0].content.parts[0].text.trim()}}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Select the &lt;em&gt;copyToClipboard&lt;/em&gt; &lt;strong&gt;Button&lt;/strong&gt; component and navigate to the properties panel on the right. Click on the &lt;strong&gt;+ New event handler&lt;/strong&gt; button. Change the &lt;strong&gt;Action&lt;/strong&gt; to &lt;strong&gt;Copy to clipboard&lt;/strong&gt;. Paste the following code in the Text field under the Action Options subsection:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{{components.generatedRegex.value}}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Similarly, select the &lt;em&gt;testString&lt;/em&gt; &lt;strong&gt;Text Input&lt;/strong&gt; component and navigate to the properties panel on the right. Paste the following code in the Regex field under the Validation subsection:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{{queries.getRegexPattern.data.candidates[0].content.parts[0].text.trim()}}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have successfully integrated everything together. Now let's test the application with the text query below:&lt;br&gt;
&lt;em&gt;Match any string that is at least 8 characters long, contains at least one lowercase letter, one uppercase letter, and one number&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fasm1eie2ygtxq2ikektt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fasm1eie2ygtxq2ikektt.png" alt=" " width="800" height="452"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Congratulations on successfully creating an AI-powered Regex generator with ToolJet and the Gemini API. You can now input natural language prompts to generate Regular Expressions effortlessly.&lt;/p&gt;

&lt;p&gt;To learn and explore more about ToolJet, check out the&amp;nbsp;&lt;a href="https://docs.tooljet.com/docs/" rel="noopener noreferrer"&gt;ToolJet docs&lt;/a&gt;&amp;nbsp;or connect with us and post your queries on&amp;nbsp;&lt;a href="https://join.slack.com/t/tooljet/shared_invite/zt-2ij7t3rzo-qV7WTUTyDVQkwVxTlpxQqw" rel="noopener noreferrer"&gt;Slack&lt;/a&gt;.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
