
Building Admin Dashboard for Dining Listings | Learning In Public Day 18
So here's the thing about day 18 of my 60-day challenge - I thought I'd have a smooth day after yesterday's chaos. Plot twist: I was completely wrong.
Quick summary
I spent the day building an admin dashboard for dining listings on my Bali travel directory. What should have been straightforward turned into a debugging marathon involving database policies, component reuse, and me questioning my life choices. But hey, that's what learning in public is all about, right?
Why I'm sharing this mess
Living here in Bali and building this travel platform, I'm realizing that the messy, frustrating parts of development are actually the most valuable to share. Everyone shows you the polished final product, but nobody talks about the 3 hours you spend figuring out why your database won't accept new entries.
If you're building something similar or just curious about the real process behind web development, this one's for you.
The challenge I was facing
Yesterday I built the front-end dining section - the filter pages, the individual listing pages, all that good stuff. But here's the problem: I had zero actual dining data. The pages were just showing hotel listings as fallback data, which looked pretty ridiculous.
I needed to build an admin dashboard where Grace (my partner) and I could start adding real restaurants, cafes, and dining experiences. Sounds simple enough, right?
What I tried first (spoiler: it didn't work)
I opened up Cursor and basically told it: "Hey, look at how we structured the stays section and do the same thing for dining."
This is where I made my first mistake. Instead of being specific about reusing components, I let the AI create completely new ones. The result? A dining admin section that looked nothing like the stays section, used different components, and had a totally different structure.
I'm not gonna lie, this frustrated me. The whole point of building modular components is to reuse them, not recreate the wheel every time.
The breakthrough moment (kind of)
After some back and forth with Cursor, I got more specific. I uploaded screenshots of my database structure and said: "Look, use the SAME components as the stays section. Don't reinvent anything."
This worked better. The dining admin started looking consistent with the stays admin. I could see the table view, the form was similar, and everything felt cohesive.
But then I hit the real problem.
Here's how I actually built it (after fixing everything twice)
Step 1: Setting up the basic structure
First, I had to make sure the dining section was properly connected to the database. The stays section filters for listing_type = 'stay'
, so I needed the dining section to filter for listing_type = 'dining'
.
This seems obvious now, but initially, both sections were showing all listings, which created chaos.
Step 2: Creating the admin interface
I reused the table component from stays but made sure it was pulling from the right database table with the right filters. The form structure stayed consistent - basic info, location, categories, all that stuff.
The key was being explicit about component reuse. Instead of saying "make it similar," I said "use the exact same components but adapt the data source."
Step 3: Database policies (the real villain)
Here's where everything went sideways. I could see the interface, I could fill out forms, but nothing was saving to the database.
The error message was cryptic: "new row violates row level security policy for table dining."
Ah. Row Level Security (RLS) policies. I had set them up for the stays table but forgot about dining. Basically, the database was saying "nope, you can't write here" because I hadn't given authenticated users permission.
Quick fix in the SQL editor:
CREATE POLICY "Enable insert for authenticated users" ON dining
FOR INSERT TO authenticated
WITH CHECK (true);
Step 4: Category integration
The stays section pulls categories from a separate categories table. I needed dining to do the same thing, but filter for dining-specific categories.
I set up "dining" as a parent category with children like "breakfast," "lunch," "fine dining," etc. The form now pulls these correctly and stores them properly.
Step 5: Fixing the redirect issue
After saving a new dining listing, the page was redirecting to a weird table view without the proper layout. This was happening because the success redirect wasn't using the same layout component.
Another case of inconsistent component usage that I had to track down and fix.
The stuff that went wrong
Oh boy, where do I start?
Database policies: Completely forgot about RLS policies for the new table. Spent 30 minutes wondering why nothing was saving.
Component inconsistency: Let the AI create new components instead of reusing existing ones, creating a maintenance nightmare.
Filter confusion: Both stays and dining were showing all listings because the filters weren't properly applied on both admin and front-end.
Internet issues: Had a big storm in Bali and the internet has been terrible since. Made everything take twice as long.
How I fixed the problems
Most of these were solved by being more explicit in my prompts to Cursor. Instead of saying "make it work," I started saying "reuse this specific component and only change the data source."
For the database issues, I just had to remember that every new table needs its own RLS policies. It's one of those things you forget until it bites you.
The filter problems required going through both the admin dashboard and front-end pages to make sure everything was properly scoped to the right listing type.
What it looks like now
I've got a functioning admin dashboard for dining that:
Looks consistent with the stays section
Properly saves to the database
Filters categories correctly
Redirects to the right place after saving
It's not perfect - there are still some TypeScript errors I need to clean up - but it works. I can add restaurants, they show up in the database, and they display properly on the front-end.
Lessons I'm taking from this
Be specific with AI prompts: "Make it similar" leads to chaos. "Reuse this exact component" gets results.
Database policies matter: Every new table needs proper RLS policies. No exceptions.
Component reuse is everything: The more consistent your components, the easier maintenance becomes.
Don't rush the foundation: I got excited and wanted to push everything live quickly, but solid foundations matter more than speed.
What I'm working on next
Tomorrow I'm going to clean up those TypeScript errors and make sure both the stays and dining sections are properly filtering on the front-end. Then I want to start adding some real data - a few hotels, restaurants, maybe a villa or two.
My goal is still to have something live by day 25, but I'm learning not to be too stubborn about deadlines. Better to get it right than to rush and create problems later.
I'm also thinking about setting up a staging environment so Grace can start adding listings without messing up the live site. That might be a good topic for a future episode.
Final thoughts
You know what's funny? I used to think these debugging sessions were just obstacles to "real" development. But living here in Bali and doing this challenge, I'm realizing these messy problem-solving sessions are actually where you learn the most.
Every error teaches you something. Every wrong turn shows you a better path. And honestly, the satisfaction of finally getting everything working properly? That's what keeps me coming back to this stuff.
If you're building something similar and running into database issues or component chaos, just know you're not alone. We're all figuring this out as we go.
Following along with the 60-day challenge? I'd love to hear what you're working on - drop a comment and let me know if you're building something cool or just trying to learn. Sometimes the best insights come from comparing notes with other builders.