SmBernard Tech logo light

SUSAN M BERNARD

Full-Stack Developer

⚜ Work Samples ⚜

Six projects from my work on a farming app, where growers check soil and water conditions across thousands of acres and turn irrigation on and off from a phone in the middle of a field.

The irrigation screen below is a hand-built model, not the real app. It runs on made-up farms, fields, and readings to show how a page I built at work looks and behaves. The layout, buttons, and wording closely follow the original, but none of the company's code, data, or settings appear on this page. The problems described below are real. The numbers shown are not.

Combining pages

Combining Three Irrigation Pages Into One

Problem

Running irrigation meant moving back and forth between three separate pages: one listed the valves, a second held the weekly schedule, and a third produced reports. Every time someone switched pages, whatever they had selected was cleared, so a person watering forty fields kept losing their place. Because the three pages had been built at different times, they also no longer looked or worked alike.

What I did

I moved the scheduler and the reports into side panels that open from the valve list, so the list stays on screen and selections stay put. Both panels still warn you before closing with unsaved changes, and the list updates when a panel closes, so new schedules show up without reloading the page.

I also set up the old page links to send people straight to the new page, so everyone's existing bookmarks kept working, and I left the old pages in the app, switched off, until the change was approved.

Outcome

Operators now work from one page instead of three, and their selections stay in place through everything they do. Two extra pages and their menu links came out of the app. The old pages were ready for rollback if anything went wrong, but they were never needed.

Try the model: select a valve, sort, search, open one, or expand Selected

Irrigation Summary

Valve Control Schedule Stats

The Desktop, Tablet, and Phone buttons resize the model itself rather than your browser, so you can see all three layouts without leaving the page. On smaller screens, the table turns into cards, the bulk action buttons stack into two rows of four, and the toolbar becomes a row of sort, search, and export buttons that stays at the top. Every bulk button opens its own panel from the left: the weekly schedule with a start and stop time for each day, the scheduler that makes you pick days before times, soil moisture thresholds, the run history report, a pause date range, and the delete tool, which asks for a four-digit PIN. Opening the Selected header shows each chosen valve the way it prints. Sorting on a phone opens a list of full field names, and the List tab shows one line per valve. Colors, spacing, and button shapes match the real app.

Stack   Vue 3 · Quasar · TypeScript. Rebuilt here in plain HTML, CSS, and JavaScript.

New hardware

Building a Dashboard for New Irrigation Hardware

Problem

New hardware let a single station control many valves through add-on boards. The existing dashboards were all built on the idea that each station waters one field, so these new valves either didn't show up at all or ended up scattered across pages where nobody could find or use them. Farmers also needed to run the valves one after another in a set order, because opening them all at the same time takes more water pressure than their systems can provide.

What I did

I built a dashboard that shows each station as a card, with a separate panel for each add-on board on that station. The layout builds itself from the hardware's own records, so a board that only holds sensors, with no valves to control, stays out of the way. From there I added a way to open and close each valve on its own, and then a tool for building a watering sequence across several stations, where farmers choose the order and how long each step runs before sending it to the hardware to carry out. While a sequence runs, they can watch its progress live, stop it, and pick it back up, and they can search through past runs whenever they need to.

I also built the server side that saves these sequences and keeps track of each run. The dashboard only appears for accounts that subscribe to it, and people who don't have permission to make changes still see every control, just grayed out with a note explaining why, so nothing seems to be missing.

Outcome

This hardware went from having no usable screen at all to having one built just for it, where farmers can view their valves, control them by hand, set up sequences, watch them run, and look back at their history. Because people can now see why a button isn't available instead of wondering where it went, the steady stream of support questions about missing buttons stopped.

Stack   Vue 3 · Quasar · TypeScript · FastAPI · PostgreSQL

Mobile-friendly design

Redesigning a Desktop App for Phones and Tablets

Problem

The app was originally designed for a desktop computer screen, but the people who use it are usually standing in a field with a phone, often in bright sunlight and often with one hand already busy. The large tables that worked well on a desktop were impossible to use on a phone, and there was no real plan for smaller screens, just a handful of one-off fixes that often worked against each other.

What I did

I started by setting three standard screen sizes for phone, tablet, and desktop, along with one shared piece of code that tells every page which size it's on, so the whole app responds the same way at the same width. With that in place, I made fifty-six pages and about sixty shared pieces, like dialogs, forms, and cards, work well on a phone, turning crowded tables into easy-to-read cards along the way.

Phones don't have room for a table's column headers, so I also built the tools a mobile list needs to make up for them: a menu for choosing how to sort, a search box that opens when you need it, a panel that shows what you've selected, and a button to jump back to the top. Each person's view, sort choice, and sort direction are remembered on their device, so a page opens the way they left it. For accounts with hundreds of valves, I added a compact list view that shares the same sorting as the cards. You can try the sort menu, search, selection panel, cards, and list view yourself in the irrigation model near the top of this page by switching it to Phone.

Outcome

Nearly every page in the app now works on a phone, apart from a few one-of-a-kind custom pages, and it all runs at the same web address with no separate mobile app to maintain. The desktop layouts stayed exactly as they were, and mobile users gained the ability to sort by details that never had room to appear as columns on a desktop. Every new page I build now works on phones from the start.

Stack   Vue 3 · Quasar · TypeScript · local storage

Admin tools

Replacing Manual Database Work With Admin Tools

Problem

Every change to how the app was set up, like adding a new customer account, a station, a zone, or a sensor, had to be made directly in the database by hand. A few people had that access, and nothing checked an entry before it went in, so a single wrong entry could quietly break things across the whole system.

What I did

I built two sets of tools so routine setup could happen inside the app instead of directly in the database, and I added safeguards that check every entry before it's saved. The first is a group of thirteen admin pages, one for each type of record in the app, where administrators can search, filter, and export records, as well as create, edit, delete, and bring back deleted ones. The second is a set of twelve setup wizards that walk an administrator through creating a new customer, station, or other record one step at a time and then save it straight to the database. Along the way, fields only appear when they're relevant, mistakes are flagged as you type, and later answers fill in automatically based on earlier ones.

I also connected the tools to each other. If someone is adding a new zone and the location it belongs to doesn't exist yet, they can create that location right from the zone form, and when they finish, they land back in the zone form with the new location already saved and selected. This works for anything that depends on something else, so a new station that needs a new zone, which in turn needs a new location, can all be set up in one pass without anyone losing their place.

When the same kind of record can be edited from several different places in the app, every one of those places uses the same form, so there's only one version to keep correct. The company's own administrators also see each record's ID number next to its name, so they can match what's on screen to what's stored in the database, while customers only see the names. On top of that, every record keeps a trail of who created it, who last changed it, and who deleted it, along with the date and time of each change.

Outcome

Routine setup work moved out of the database and into the app itself, where every change is checked before it's saved and deleted records can be brought back. No one needs to go into the database directly to add a customer or fix a setting anymore, which protects the whole system from accidental damage, and if something does go wrong, the trail shows who made the change and when. Now, whenever a new type of record is added to the app, its admin tools are built right along with it instead of being requested later.

Stack   Vue 3 · Quasar · TypeScript · FastAPI · PostgreSQL

Debugging

Fixing a Crash Caused by Browser Translation

Problem

Some users reported that the app crashed when they tried to open or close a valve. No one could find a pattern, and the crash never happened when we tested it ourselves.

What I did

The common thread turned out to be language. Spanish-speaking crews were using their browser's built-in translation, which swaps out the words on the page as it translates them. The app didn't expect its text to change underneath it, so when it tried to update a word that had been replaced, it crashed, sometimes right in the middle of opening or closing a valve.

I set up a few rules that let the app work alongside translation. The parts of the page that change constantly are marked so the browser won't translate them, every page tells the browser its language up front so it doesn't have to guess, and chart labels are drawn in a way the translator leaves alone. I also wrote these rules and the reasons behind them into the project's documentation, so the fix holds for any code written after me.

Outcome

The crashes stopped. The tradeoff, which we agreed on up front, is that some text in those protected areas stays in English, which is far better than a control failing while someone is standing at a valve. Since so many of the people using the app in the field speak Spanish, a full Spanish version is something I'd love to build next.

Stack   Vue 3 · Quasar · Highcharts

Security

Making Logins More Secure

Problem

Once someone logged in, they stayed logged in for eight days, so if anyone else got hold of that login, they had more than a week to use it. There was also no limit on password guesses, no two-factor authentication, no way to force someone out, and no record of who changed what.

What I did

I tightened security in two rounds. In the first, I shortened logins to one hour, limited how many password guesses anyone could make, and locked accounts after too many failed tries.

Farmers pushed back on signing in every hour while they were working in the field, so in the second round we settled on 24-hour logins. Along with that, I made logging out or changing a password end a login on the spot, and I added two-factor authentication with an email backup in case the phone is lost, a record of every change, and automatic sign-out whenever someone's access is denied.

Logins are now being redesigned around the workday, so each login will last 15 hours from when someone signs in. A crew member who starts at 7:00 am is signed out by 10:00 pm and starts fresh the next morning, and closing the browser ends the login unless they choose "Stay logged in." Reminders at 30, 15, and 5 minutes before time runs out give people a chance to save their work, and signing back in returns them to the exact page they were on.

Outcome

A stolen login now works for one day at most instead of eight, and logging out ends it right away. Password-guessing attacks now hit a wall, and every change can be traced back to the person who made it. The first round was too strict for people working outdoors all day, and listening to them got us to a setting that was both safer and easy to live with.

Stack   FastAPI · PostgreSQL · JWT · TOTP · Vue 3

⚜ How I Work ⚜

I plan the work, build both the parts people see and the behind-the-scenes server side, test it the way each kind of user would use it, write it up for whoever works on it next, and see it through to release. I came to development from psychology, healthcare, and education, which is why the question I keep asking isn't just whether a feature works, but whether the person using it can actually get their job done.

  • Keeping the Right Customer on Screen Admins could think they were working on one customer's page when they were actually in the area that covers all customers, or the other way around, so I made those pages clearly show whether you're looking at all customers or at one specific customer. I also made the page's web address the one place the app checks to know which customer you're viewing, which fixed a group of bugs where, after switching customers, the page, the customer picker, and the back button didn't always agree on who was selected.
  • Sharing Sensors Across Fields One rain gauge can now feed its readings to many fields, with clear settings for who is allowed to see each one.
  • Consistent Page Layouts Four different ways of spacing pages were narrowed down to two standard layouts used across fifty pages.
  • Fixing a Privacy Gap in Alerts An alert set to watch "all stations" wasn't limited to the customer who owned it, so it could go off for another customer's stations. I closed that gap, blocked alerts from pointing at stations that belong to someone else, and then checked every related part of the app for the same mistake.
  • Exporting a Year of Data Downloading a full year of sensor readings crashed the browser, so I split it into an emailed file of daily averages and a chart that shows monthly summaries.
  • Combining Scheduling Forms Three scheduling forms that had grown apart became one shared form, cutting about 4,100 lines of code down to 1,850.

Open to remote job opportunities

✾✾✾ Let's Build Something!