Loading
Projects
  • Client:   CV Media Adhi Persada
  • Company Profile
  • Date:   Jul 2026

Media Adhi Persada: Company Profile and Billboard Catalogue

Cover image of Media Adhi Persada: Company Profile and Billboard Catalogue

A company profile site, billboard rental catalogue, and CMS for Media Adhi Persada in Sukabumi. Prospective clients can check which billboard sites are still free, then get in touch over WhatsApp.

Homepage (desktop)
Billboard catalogue (desktop)

Background

Media Adhi Persada (MAP) is an advertising, event organiser, and IT solutions company in Sukabumi. Their old site held nothing but a profile and a list of past work. The billboard sites they rent out were written as plain text, their status was changed by hand, and prospective renters had no way of seeing which sites were still free. When someone got in touch over WhatsApp, MAP also had no idea which page that person had come from.

Solution

I rebuilt the site with Laravel 13 as a single application serving two domains. The main domain carries the public site, while the cms subdomain carries a Filament admin panel. Public routes are wrapped in Route::domain() and the Filament panel is registered only on the CMS domain, so /admin on the public site genuinely does not exist.

Everything the site shows can be changed from the CMS: billboards, services, past work, gallery, slider, banners, clients, FAQs, right down to the contact numbers and the WhatsApp message templates. Each time content is saved, the cached HTML of the public pages is cleared with it, so changes show up immediately.

Key features

  • A billboard catalogue with filters for area, status, type, and lighting, plus grid, list, and Leaflet map views.
  • Available or Rented is worked out from the rental dates at the moment the page is opened. A billboard returns to available by itself the day after its rental period ends, with no scheduler involved.
  • Every WhatsApp button goes through a /wa route that records where the click came from (a billboard, a service, a piece of past work, or the site in general) and ignores bots.
  • The contact form stores each message as a lead and sends an email notification. It is protected by a honeypot, an encrypted time trap, and a rate limit, with no CAPTCHA.
  • Search across billboards, services, and past work, with suggestions as you type.
  • A CMS dashboard with billboard statistics, a 30-day chart of WhatsApp clicks, rentals about to expire, and the latest messages.
  • A map point picker on the billboard form: click the map, drag the marker, or paste a Google Maps link.
  • A message inbox with statuses, internal notes, a reply-over-WhatsApp button, and CSV export.
  • Per-page SEO (meta, Open Graph, and JSON-LD for LocalBusiness, Service, Place, CreativeWork, and FAQPage), a sitemap with images, and a robots.txt that differs between the public domain and the CMS.
  • 301 redirects for old URLs, including ones created automatically when a slug changes. All 14 portfolio URLs from the old site still work unchanged.

Technical challenges

The raw material was 478 photos taken on the field team's phones, many of them still carrying a camera-app overlay with a phone number and coordinates on it. I wrote a Node script with sharp to compress the photos and build contact sheets, then sorted them one by one into past work, the gallery, or the bin. Ninety-three photos made it through, eighteen of which had to be cropped or pixelated first. Photos that admins upload through the CMS later are cleaned too: EXIF and XMP data, GPS location included, is stripped the moment a file arrives, before the WebP conversion is made.

Caching gave me some trouble. Laravel 13 refuses to unserialise objects from the cache by default, and the database cache driver has no tags. In the end I store the rendered HTML as a plain string, under a key that carries a version number. When content changes, the version is bumped and the old keys expire on their own.

The billboard status rules also had to match exactly in two places: the accessor used for display, and the scope used for filter queries. I tested both against the same cases: no rental at all, a rental in the future, a rental already past, and special statuses such as maintenance.

The map picker in the CMS is a custom Filament field built with Alpine and Leaflet. Coordinates can be read out of a full Google Maps link, but not out of a short maps.app.goo.gl link, because that needs a redirect resolved on the server. For those, the admin is asked to paste the full link instead.

Some things are not finished. A few billboards have no size or coordinates recorded yet, so the site shows "-" for them until the client fills those in through the CMS. One billboard photo carries a GPS stamp that turns out to point somewhere else entirely, so I deliberately left its coordinates empty rather than put a wrong pin on the map.

My role

I did this project alone: gathering the requirements and writing the spec, processing the photos, designing the interface, writing the backend and the CMS, writing the tests, and deploying. The application is packaged with a Dockerfile (a serversideup/php image running PHP 8.5) and runs on Coolify on a VPS with a MySQL 8 database.

Results and what comes next

The site is running on the server with all of its initial content: 14 pieces of past work, 47 gallery photos, 3 services, and 4 active billboard sites. There are 165 Pest tests covering the public pages, the billboard filters, WhatsApp click tracking, the contact form, the CMS, and SEO. There is no usage data such as message or click counts yet, because the site has not been moved to its own domain.

Next I will move the DNS for mediaadhipersada.id to the new server, submit the sitemap to Google Search Console, and help MAP complete their billboard data. Once WhatsApp click data has built up, I want to add a per-billboard click report so MAP can see which sites get asked about most.

Tech stack

PHP Laravel 13 Filament 5 Livewire MySQL Tailwind CSS 4 Alpine.js Leaflet Vite Pest Docker Coolify

Similar projects

Ready to bring your ideas to life? I'm here to help

© 2026 Axa Rajandrya. All rights reserved.

+
+