Listing Lab AI

Front End Stack and UX

Front End Stack and UX

For the front end, I want it to feel like you're operating a piece of machinery. It should look and feel like you're doing work, because that's exactly what you're doing, gathering a ton of data on a real estate listing. I opted to aim for interactivity, and to create an easy to see visual flow of what needs to happen next to move you along.

Main user flow

We start with a blank slate, but with almost all controls still accessible if a user wants to explore. The user's eye is likely going to be drawn to a dotted border around a large button, indicating that something is empty and needs to be added. This is where you add a house, the first step in the user flow.

Once this button is clicked, a drawer pops up, allowing a user to search for a house using the search bar, or to select a house they have already gathered data on. The user inputs an address, and waits for a response. This query is handled and managed using tRPC, a type safe router for managing queries. This query returns what I call an "unhydrated house", which means that we found the house, and we are working on getting the data for it. This allows us to quickly search for a house, without also having to wait for all the data aggregation jobs to complete.

Realtime Updates

While we have this unhydrated house, the user is free to move on to other steps. Our UI shows several loading spinners, relating to different data aggregation jobs that are happening. Once a job completes, our backend will send a message to our UI using MQTT over websockets, with the status of the job. Depending on the status, we update the loading spinner to a success or fail state. This allows the user to know if they are good to go to generate their prompt or if they should wait to send a query until all the data aggregation is complete. For example if a user is creating a market analysis, they should wait to send, until the "Recently Sold" job status is a success. Pushing these jobs into a backend queue, and awaiting their completion, allows the UI to stay active, and creates an experience for a user that feels mechanical almost, like they're operating a machine that goes out and finds all the data they need for a listing. Since we have decoupled these jobs from the front end, the user is free to customize their prompt and do other work as they wait.

Local Expertise and Prompt Customization

After a house is found or selected, a user is then shown another empty state button, clicking this allows them to share their additional local expertise on the house. This is a drawer that pops up, with a large text box. A user then saves their expertise and we update the house record on our database. Next a user will move to adding their prompt, selecting a model, and optionally adjusting model settings like Top P and Top K. These options are tucked away into an accordion, so it is available for advanced users, but hidden for users who might not care about these settings. Users can then type in their prompt, and save it, which will open up a modal that allows them to add a name for the prompt. This is then saved to the database. Finally, a user hits generate and the UI is updated accordingly to show a loading state as the generation is created.

Recent genrations

Once a house has some generations, a user can easily view them by clicking the "Recent Generations" accordion underneath the house. Hiding this away initially keeps the UI clean, and reduces database load by only pulling the recent generations when they're needed.

Styling with Tailwind and interactivity with ShadCN

I wanted the UI to feel calming to look at, and provide a clear user flow, so I go with whites and greys to keep things looking clean and maintaining a visual hierarchy of importance. Things that could cause visual clutter are moved into accordions, modals, and drawers provided by ShadCN components.

Things like responsive designs, hover interactions, and theme colors are easily applied using Tailwind's classes. Custom CSS is minimized, keeping code clean and maintainable.

All Type Safe

Managing user state, we use React context, useState, and various other hooks for managing interactivity. Query state management is easy to work with thanks to tRPC. And querying data from the backend, or sending mutations, is easily manageable because our API layer is type safe, bringing convenient auto complete in development.

Now let's hop into the backend.

Previous
Overview