A small ecommerce application built with Laravel and React for a job application exam.
This project serves as a foundation for an ecommerce application, demonstrating a modern full-stack setup using Laravel as the backend API and React with Inertia.js for a seamless single-page application experience. It includes essential features like user authentication, profile management, and a responsive UI, providing a solid starting point for building ecommerce functionalities such as product listings, shopping carts, and order management.
- Backend: Laravel 12.0, PHP 8.2+
- Frontend: React 19.2.0, TypeScript
- SPA Framework: Inertia.js
- Styling: Tailwind CSS 4.0, shadcn/ui component library
- Authentication: Laravel Fortify
- Build Tool: Vite
- Database: SQLite (default), configurable to MySQL/PostgreSQL
- Testing: Pest
- Code Quality: ESLint, Prettier
- User registration and login
- Email verification
- Two-factor authentication (2FA)
- User profile management
- Password change functionality
- Appearance settings (theme customization)
- Responsive design with mobile-friendly navigation
- Server-side rendering (SSR) support
- Modern UI components with Radix UI primitives
-
Clone the repository:
git clone <repository-url> cd ecommerce-test
-
Install PHP dependencies:
composer install
-
Set up environment:
cp .env.example .env php artisan key:generate
-
Configure database (optional, defaults to SQLite): Update
.envwith your database credentials and run:php artisan migrate
-
Install Node.js dependencies:
npm install
-
Build assets:
npm run build
Alternatively, use the provided setup script:
composer run setupStart the development server with hot reloading:
composer run devThis command runs:
- Laravel server on
http://localhost:8000 - Queue worker
- Vite dev server for frontend assets
- Log monitoring
-
Build optimized assets:
npm run build
-
Start the application:
php artisan serve
Run the test suite:
composer run test- Lint code:
npm run lint - Format code:
npm run format - Check types:
npm run types
├── app/ # Laravel application code
│ ├── Actions/ # Custom actions (Fortify)
│ ├── Http/Controllers/ # Controllers
│ ├── Models/ # Eloquent models
│ └── Providers/ # Service providers
├── database/ # Migrations, factories, seeders
├── public/ # Public assets
├── resources/ # Views and frontend assets
│ ├── css/ # Stylesheets
│ ├── js/ # React application
│ │ ├── components/ # Reusable UI components
│ │ ├── layouts/ # Page layouts
│ │ ├── pages/ # Inertia pages
│ │ └── types/ # TypeScript definitions
│ └── views/ # Blade templates
├── routes/ # Route definitions
├── storage/ # File storage
├── tests/ # Test files
├── vite.config.ts # Vite configuration
├── tailwind.config.js # Tailwind CSS configuration
└── composer.json # PHP dependencies
- Laravel: PHP framework for backend logic, routing, and database interactions
- Inertia.js: Bridges Laravel and React, allowing server-side routing with client-side rendering
- React: Frontend library for building interactive user interfaces
- TypeScript: Adds static typing to JavaScript for better code quality
- Tailwind CSS: Utility-first CSS framework for rapid UI development
- shadcn/ui: High-quality React components built on Radix UI
- Vite: Fast build tool and development server
The application includes a Product model with basic ecommerce functionality.
The products table migration (database/migrations/2025_12_27_084123_create_products_table.php):
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('products', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->decimal('price', 10, 2);
$table->integer('stock_quantity');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('products');
}
};The Product model (app/Models/Product.php):
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Product extends Model
{
/** @use HasFactory<\Database\Factories\ProductFactory> */
use HasFactory;
/**
* The attributes that are mass assignable.
*
* @var list<string>
*/
protected $fillable = [
'name',
'price',
'stock_quantity',
];
/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'price' => 'decimal:2',
];
}
}The Product factory (database/factories/ProductFactory.php):
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Product>
*/
class ProductFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
$productTypes = ['Laptop', 'Smartphone', 'Headphones', 'Keyboard', 'Mouse', 'Monitor', 'Tablet', 'Camera', 'Speaker', 'Watch', 'Charger', 'Cable', 'Case', 'Stand', 'Adapter'];
$adjectives = ['Premium', 'Pro', 'Ultra', 'Wireless', 'Bluetooth', 'USB-C', 'Fast', 'Portable', 'Compact', 'Ergonomic'];
return [
'name' => fake()->randomElement($adjectives) . ' ' . fake()->randomElement($productTypes) . ' ' . fake()->numberBetween(1, 5),
'price' => fake()->randomFloat(2, 9.99, 999.99),
'stock_quantity' => fake()->numberBetween(0, 500),
];
}
}The Product seeder (database/seeders/ProductSeeder.php):
<?php
namespace Database\Seeders;
use App\Models\Product;
use Illuminate\Database\Seeder;
class ProductSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
Product::factory(10)->create();
}
}To run the Product seeder and populate the database with sample products:
-
Run migrations (if not already done):
php artisan migrate
-
Run the seeder:
php artisan db:seed --class=ProductSeeder
Or run all seeders (including ProductSeeder):
php artisan db:seed
This will create 10 sample products with random names, prices between $9.99 and $999.99, and stock quantities between 0 and 500.
The application includes an automated daily sales report system that tracks sales and sends email summaries to administrators.
Sales are tracked using the sales table, which records:
user_id: The customer who made the purchaseproduct_id: The product that was soldquantity: Number of units soldprice: Price per unit at time of saletotal: Total sale amount (quantity × price)created_at: Timestamp of the sale
To manually trigger the daily sales report:
-
Run for today (default):
php artisan sales:daily-report
-
Run for a specific date:
php artisan sales:daily-report --date=2025-12-26
-
Run the scheduler manually (to test all scheduled tasks):
php artisan schedule:run
To enable the scheduler in production, add this cron entry to your server:
* * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1This will run the scheduler every minute, and Laravel will execute scheduled tasks at their designated times.
Make sure to set the admin email in your .env file:
ADMIN_EMAIL=admin@example.comThe report will be sent to this email address daily at 9:00 AM UTC.