Skip to content

Repository files navigation

Portfolio Website (Laravel + Vue)

This repository contains my personal portfolio website project.

Tech Stack

  • Backend: Laravel 12 (PHP 8.2)
  • Database: MySQL
  • Admin Panel: Filament (served under /admin)
  • Frontend: Inertia.js + Vue 3
  • SSR: Inertia SSR (Node.js)

Current Features

  • 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 /en prefix.
  • 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_en is filled (to avoid duplicate content).
    • SEO fields per locale (title/description) and robots/canonical support.
  • 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 /en routes 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.php
    • lang/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

Navigation (Menu from Pages)

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 menu
  • menu_order — sorting order
  • menu_title_uk / menu_title_en — optional custom labels (fallback to title_*)

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.

Global Layout (Header & Footer)

The project uses Global Blocks to manage shared layout elements such as the site header and footer.

How it works

  • Global blocks are stored in the global_blocks table.
  • 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.

Available global blocks

  • 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

Admin panel

  • Location: Admin → Site Settings → Global Blocks
  • Header and Footer are managed independently from pages.

Site Modules (Home page sections)

Home page sections are built from site modules stored in the database.

How it works

  • Module types are stored in the module_types table (e.g. hero, services, projects, about, testimonials, faq, contact).
  • Modules are stored in the modules table:
    • enabled flag controls visibility.
    • data is 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.

Admin panel

  • 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.

Implemented modules

  • Hero
  • Projects (loads projects from the database; configurable via module settings)
  • About
  • Testimonials
  • FAQ
  • Contact

Assets

  • 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 (Entity + Module)

Services are managed as a separate entity in Filament (multilingual content):

  • title_uk, title_en
  • excerpt_uk, excerpt_en
  • description_uk, description_en
  • sort_order
  • is_featured

Projects

  • 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/en
  • data.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.limit
  • data.sort (order | created_at | updated_at)
  • data.columns (1..4)
  • data.featured_only

The module supports multilingual section content:

  • data.heading_uk/en
  • data.lead_uk/en
  • optional header button: data.button.label_uk/en, data.button.href_uk/en

Theme (dark / light)

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:

  • theme cookie is excluded from encryption in bootstrap/app.php via encryptCookies(except: ['theme']).

About

My Portfolio whith web application based on Laravel 12

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages