Build a Voting App
In this workshop, we’ll build a voting app, similar to UserVoice, that allows you to create ideas, vote and comment on them, sort and filter the results, and even administer the site. We’ll start from scratch and work through the entire process, including implementing the design, working on all of the features, testing our code, and more.
We’ll be making use of the TALL (Tailwind, Alpine, Livewire, Laravel) stack in this particular series.
Introduction and Demo
Let’s begin with a demo of the final voting app you’ll building in this series, as well as a quick overview of its potential future features.
Initial Setup and Design
We’ll start by installing Laravel Breeze to handle the authentication layer. We’ll also, in the process, install Tailwind and Alpine, and then set up our layouts and basic navigation.
Idea Cards
Let’s work on implementing the design of the idea cards on the main page of the app.
The “Add Idea” Form
We’ll next work on the design of the form that allows users to create and submit new ideas.
The Idea Page
Next, we can implement the design of the single idea page, which includes a section for comments.
Set Status Dialog
Let’s now work on the design for the “set status” and “post comment” dialogs.
Toggle Visibility with Alpine
In this lesson, I’ll show you how to toggle the visibility of a menus using Alpine.js. It’s really simple!
Make it Responsive
Let’s now finish up the design section of this series by making our layout responsive.
Idea Model and Migrations
Let’s begin this new chapter by setting up our Idea model and database migrations. After that, we’ll make the index and show pages dynamic by pulling the relevant data from the database.
Testing: Showing Ideas
Let’s be sure to prepare a series of tests that ensure that showing ideas works on our index and show pages.
Clicking the Idea Box
Let’s work on the ability to click the entire idea box and make it function like a link. Sure, it’s a small feature, but you’ll often find yourself working on little things like this.
Gravatar Images
In this episode, we add Gravatar images for our users with defaults – similar to what you’ll find on the Laracasts forum. Of course, we’ll also write a series of tests for this new functionality.
Add Categories
We’ll now add categories and a corresponding relationship for each idea. We make sure it works in the browser, and then update our current tests.
Add Statuses
Next up, let’s add statuses and a corresponding relationship for each idea. We’ll also take a look at a few approaches for adding the corresponding classes with each status.
Creating Ideas
Let’s make a Livewire component that handles the process of submitting new ideas. We’ll also ensure that success and validation messages are displayed in response.
Testing: Creating Ideas
With the initial code implemented, let’s fill it in with a test to prove that it works the way we expect.
Voting Database Structure
We can finally work on the core functionality of voting for ideas. In this episode, we’ll set up our database table to store votes on ideas, set up the relationship between our models, and display the votes count on both the index and show pages.
Testing: Votes Count
Again, let’s be sure to write tests for this new votes count functionality.
Did the Current User Vote for the Idea
Let’s continue the voting functionality and add some logic to check if a given idea was voted for by the logged-in user. We’ll reach for subqueries to ensure that our queries are optimized and not at risk of the “n 1” issue.
Voting for Ideas
We can now tackle the actual voting functionality that allows a user to vote for any idea.
The Back Button Bug When Voting
In this episode, we discover and fix a bug that throws off our state in Livewire when using the back button in the browser.
Alternative Implementations for Voting
Next up, we’ll discuss a few alternative ways of implementing the voting feature in our application.
Make a Livewire Component to Fetch Ideas
Before we work on filtering, let’s do some refactoring and move the logic for fetching ideas into a dedicated Livewire component.
Status Filters
Let’s now work on the status filters to allow users the ability to filter all submitted ideas based on their associated status.
Status Filters Count
In this video, we’ll add the ability to count our ideas based on their status.
Status Filters Tests
Let’s be sure to write a few tests which ensure that the status filters are working as expected.
Category Filters
Let’s work on the category filters and do some refactoring in order to manage the query string within one component.
Testing Category Filters
Before moving on, let’s be sure to properly test our category filters component.
Other Filters
In this lesson, we’ll work on some other filters: one for showing the top-voted ideas, and another for showing the ideas specifically for the logged-in user. As always, we’ll include a set of tests,
Search Filter
Let’s now work and test a filter that searches for ideas based on the title.
In-App Back Button
Next up, we’ll add a minor, but useful and convenient UX addition to the site: an in-app back button.
Set the Idea Status
In this next section, we’ll add the ability for users administers to set the status of an idea.
Set Status Test
As always, we should write some tests to prove that this new feature works as expected.
Notify All Voters
Next, we should add the ability to notify all voters (via email) when the status for an idea has changed.
Notify All Voters Test
In this lesson, we will test the “notify all voters” feature – and also make use of Horizon for our queues.
Refactoring Tests
Let’s go back and clean up our tests to utilize factories and factory states more efficiently.
Modal CSS
Let’s begin this new chapter by working to allow users to edit an idea. We’ll use Tailwind to make this as quick and easy as possible.
Modal Toggle and Transitions
In this episode, we’ll leverage Alpine.js to toggle the modal and add the necessary transitions.
Edit Policies
In this episode, we’ll work on actually editing the idea using Livewire. We can also take care of policy-based authorization.
Edit Idea Tests
As always, we can’t forget to write a series of tests to ensure that editing works as expected.
Delete an Idea
Next, we’ll add the ability to delete ideas, while making use of Tailwind UI’s modal component in the process.
Delete an Idea Tests
As always, we’ll take some time ensure that our “Delete Idea” feature is working as expected.
Manage Spam
Next up we’ll add some spam management features, such as marking an idea as spam, a spam filter, a spam counter for admins, and resetting spam counts for ideas.
Manage Spam Tests
Once again, we’ll write some tests for our spam management features.
Refactoring Modals
Let’s take some time to refactor our modals into a Blade component. This should help remove all of our repeated code.
Success Message CSS
Let’s work on the success message notifications – specifically, their styling and transitions using Tailwind and Alpine.
Success Message Notification
Let’s now wire up our notification with the actions in our application.
Comment Model and Migration
We get started on the comments feature that allows users to discuss an idea. We create our models and migrations and render the comments on the idea page.
Add Comments
Continuing on, we work on allowing the user to add comments to the idea.
Comments UX
Let’s add a few small UX features, such as scrolling to a comment after it was added and giving it a subtle background color.
Comments Pagination
Next up, we’ll work on allowing comments to be paginated in order to optimize any potential performance issues.
Showing and Adding Comments Tests
As we’ve done several times before, in this episode we’ll take some time to write tests for showing and adding comments.
Edit Comments
Moving on, we add the ability to allow users to edit their comments.
Delete Comments
Okay, next we should ensure that a user can delete their own comments.
Edit and Delete Comments Test
Now that we’ve implemented the functionality to delete a comment, let’s write a set of tests to ensure that it works as expected.
Comment Spam Management
In this episode, we’ll add the ability to mark a comment as spam and reset the spam counter if we are logged in as an administrator.
Admin Set Status Comment Part 1
Let’s revisit the “Set Status” component and allow admins to add a comment when setting a status on the idea. In this first part, we integrate the CSS and make changes to our models and migrations.
Admin Set Status Comment Part 2
In this second part, we’ll implement the ability to add a comment. We’ll also update our tests.
Notification Styling
Let’s change gears and get started on a feature that displays a user notifications if anyone has commented on their ideas. For this episode, we’ll take care of the general styling.
View Notifications
In this lesson, we’ll make use of notifications in Laravel. We’ll reach for the database driver to store them in a dedicated notifications table. Finally, we’ll finish by populating our dialog with the logged-in user’s notifications.
Badge Count
Next, we’ll add a badge count and skeleton loader for when notifications are being loaded.
Mark Notifications as Read
Let’s allow the user to mark their notifications as “read.” We’ll additionally add a feature that visits the correct idea page and highlights the comment that was selected from our notifications dialog.
Notification Polling
Let’s leverage Livewire’s polling ability to automatically refresh our component to check for new notifications.
Notification Tests
Now we can finish up this particular feature by writing a series of tests to confirm it works properly.
Auth Redirects
Let’s now work on redirecting newly signed in users to the page that they were on previously.
Small Fixes
As we near the end of this series, let’s work through a list of minor fixes and updates, including making use of Tailwind’s JIT compiler and updating our database schema to includes cascades.
Conclusion
In this final episode, we wrap up the series with some discussion of potential features for the app that you’re to explore on your own. Thanks so much for watching!


There are no reviews yet.