This repository contains my personal portfolio website project.
- Backend: Laravel 12 (PHP 8.2)
- Database: MySQL
- Admin Panel: Filament (served under
/admin) - Frontend: Inertia.js + Vue 3
- SSR: Inertia SSR (Node.js)
- Public website built with Inertia + Vue 3.
- Server-Side Rendering (SSR) enabled for improved SEO and faster first render.
- Locale routing:
- Ukrainian is the default (no prefix).
- English uses the
/enprefix.
- Pages CMS (managed from Filament):
- Separate slugs for Ukrainian and English (
slug_uk,slug_en). - English version is optional: EN pages exist only when
title_enis filled (to avoid duplicate content). - SEO fields per locale (title/description) and robots/canonical support.
- Separate slugs for Ukrainian and English (
- Home page is managed as a regular CMS page:
- A single page can be marked as Home from the admin panel.
- Home is rendered via
/and/enroutes using the same CMS infrastructure.
- Filament admin improvements:
- “Save & close” actions on Create/Edit pages.
- Quick “View (UK/EN)” actions from the Pages list.
- Basic translations structure:
lang/uk/ui.php,lang/en/ui.phplang/uk/admin.php,lang/en/admin.php
- Optimized Inertia SSR payload (only current-locale block data is sent to the client).
- Bootstrap 5 styles are bundled via a dedicated Vite CSS entry for better loading behavior.
- Projects section implemented (admin CRUD + public list page).
- Projects slugs are auto-generated (UK/EN) and can be edited manually
The main navigation is built from published Pages (single source of truth) and shared to the frontend via Inertia:
show_in_menu— whether the page appears in the menumenu_order— sorting ordermenu_title_uk/menu_title_en— optional custom labels (fallback totitle_*)
Strict EN rule: English menu shows only pages that have both title_en and slug_en filled (so /en never leaks Ukrainian content).
Header and Footer render the same nav.pages list, so the menu is consistent across the website.
The project uses Global Blocks to manage shared layout elements such as the site header and footer.
- Global blocks are stored in the
global_blockstable. - Each block has a unique
type(e.g.header,footer). - Only one block per type is allowed.
- Blocks are rendered via Vue components and shared with Inertia.
- Header (Navbar)
- Multilingual brand name and role
- Navigation menu
- Theme switch
- Language switch
- CTA button
- Footer
- Multilingual content
- Navigation and social links
- Dynamic copyright
- “Back to top” button
- Location: Admin → Site Settings → Global Blocks
- Header and Footer are managed independently from pages.
Home page sections are built from site modules stored in the database.
- Module types are stored in the
module_typestable (e.g.hero,services,projects,about,testimonials,faq,contact). - Modules are stored in the
modulestable:enabledflag controls visibility.datais a JSON field containing module-specific content (localized where needed).
- The public renderer (
resources/js/Pages/Page.vue) renders modules by their type in the template order.
- Location:
- Admin → Site Settings → Site Module types
- Admin → Site Settings → Site modules
- When creating a module, you first select its type; on edit the type is locked.
- Hero
- Projects (loads projects from the database; configurable via module settings)
- About
- Testimonials
- FAQ
- Contact
- Profile images are served from
public/assets/img/profile/(used by the About module). - Project preview images are served from
public/assets/img/projects/(currently fixed by card order in the Projects module).
Services are managed as a separate entity in Filament (multilingual content):
title_uk,title_enexcerpt_uk,excerpt_endescription_uk,description_ensort_orderis_featured
- Multilingual projects section (UK / EN)
- Projects list page with cards
- Individual project pages with SEO-friendly URLs
- Completion date and estimated project cost
- Language switch support on project pages
The Services module does not store service cards in module JSON anymore.
Instead, it fetches services from the database based on module settings:
data.limit(default: 4)data.sort(order|created_at|updated_at, default:order)data.columns(1..4, Bootstrap grid mapping, default: 4)data.featured_only(default: true)
The module still supports multilingual section content:
data.heading_uk/endata.lead_uk/en- optional header button:
data.button.label_uk/en,data.button.href_uk/en
Projects are managed as a separate entity in Filament.
The Projects module does not store cards in module JSON anymore. Instead, it fetches projects from the database based on module settings:
data.limitdata.sort(order|created_at|updated_at)data.columns(1..4)data.featured_only
The module supports multilingual section content:
data.heading_uk/endata.lead_uk/en- optional header button:
data.button.label_uk/en,data.button.href_uk/en
Theme is persisted in both localStorage and a theme cookie.
The cookie is used to apply data-theme and data-bs-theme on the server-rendered HTML to avoid theme flash on refresh.
Laravel 12 note:
themecookie is excluded from encryption inbootstrap/app.phpviaencryptCookies(except: ['theme']).