Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions .env.testing
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
APP_NAME=Laravel
APP_ENV=local
APP_KEY=base64:F31zYiqSoTb5Cs4r5IJGU0Vp1vHxwRcl3n1qRqdIpNk=
APP_DEBUG=true
APP_URL=http://localhost

APP_LOCALE=en
APP_FALLBACK_LOCALE=en
APP_FAKER_LOCALE=en_US

APP_MAINTENANCE_DRIVER=file
# APP_MAINTENANCE_STORE=database

PHP_CLI_SERVER_WORKERS=4

BCRYPT_ROUNDS=12

LOG_CHANNEL=stack
LOG_STACK=single
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=various
DB_USERNAME=root
DB_PASSWORD=

SESSION_DRIVER=database
SESSION_LIFETIME=120
SESSION_ENCRYPT=false
SESSION_PATH=/
SESSION_DOMAIN=null

BROADCAST_CONNECTION=log
FILESYSTEM_DISK=local
QUEUE_CONNECTION=database

CACHE_STORE=database
# CACHE_PREFIX=

MEMCACHED_HOST=127.0.0.1

REDIS_CLIENT=phpredis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379

MAIL_MAILER=log
MAIL_SCHEME=null
MAIL_HOST=127.0.0.1
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_FROM_ADDRESS="hello@example.com"
MAIL_FROM_NAME="${APP_NAME}"

AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false

VITE_APP_NAME="${APP_NAME}"
137 changes: 101 additions & 36 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,37 +1,102 @@
## Backend dev tech task
### Objective
Demonstrate your backend development skills by implementing a basic user management system.

### Task Description
Build a CRUD (Create, Read, Update, Delete) application for managing users. The system should support the following functionality:
Required User Fields (for views and forms):
Name
Surname
Email
Phone
Country (selected from a predefined list)
Gender
Password
Repeat Password (for validation)
Optional Fields (not required for current implementation):
Selfie
Introduction
Additional Requirements:
Support image upload (e.g., for a user profile picture)
Enable country selection from a predefined country list

### Acceptance Criteria
- Fork the provided Git repository and implement the task within your fork
- Implement full CRUD functionality:
- Create user
- Update user
- View user details
- View user list
- Delete user
- Follow Test-Driven Development (TDD) principles
- Apply Domain-Driven Design (DDD) best practices
- Ensure code quality, readability, and maintainability
## Backend dev tech task - User Management System
A Laravel Livewire CRUD application to manage users with features like image upload, country selection, and gender specification. Built using **TDD**, adheres to **DDD best practices**, and implements **clean, maintainable code** structure.

### πŸš€ Features
βœ… Create, Read, Update, Delete (CRUD) for users
- πŸ“Έ Profile picture upload (with image validation)
- 🌍 Country selection from a predefined list
- πŸ‘¨β€πŸ‘©β€πŸ‘§ Gender selection
- πŸ”’ Password and password confirmation
- πŸ”Ž View individual user details
- πŸ”” Flash messages for create, update, delete actions
- βœ… Livewire-powered UI for smooth interaction
- πŸ§ͺ Fully tested with Feature tests using Laravel’s built-in tools and Livewire testing utilities

### πŸ“‚ Folder Structure
- `App\Livewire\` – Livewire components for User CRUD (CreateUser, EditUser, UserList, UserView, DeleteUser)
- `App\Models\User` – Extended to support profile image storage
- `Database\Factories\UserFactory` – Generates test and dummy users, including images
- `resources/views` – Blade views with Tailwind CSS styling

### πŸ§ͺ Tests
Located in `tests/Feature/UserLivewireTest.php`, covering:
- βœ… User creation
- βœ… User update
- βœ… User deletion (with confirmation)
- βœ… Viewing user details
- βœ… Listing all users

### Notes:
- Your submission will be evaluated based on code quality, adherence to best practices, and completeness of the task
- Thank you and good luck!
### Run Tests
```bash
php artisan test
```
Or sun a specific test:
```bash
php artisan test tests/Feature/UserLivewireTest.php
```

## πŸ“¦ Installation
### 1. Clone the repo
```bash
git clone https://github.com/awizendd/tech-task.git
cd tech-task
```

### 2. Install dependencies
```bash
composer install
npm install
npm run dev
```

### 3. Setup environment variables
```bash
cp .env.example .env
php artisan key:generate
```

### 4. Configure the database
Update .env:
```env

DB_CONNECTION=mysql
DB_DATABASE=your_database
DB_USERNAME=root
DB_PASSWORD=
```

Then:
```bash
php artisan migrate
```

### 5. Run the App
```bash
php artisan serve
```
Visit: http://127.0.0.1:8000

## 🌐 Technologies Used
- Laravel 10+
- Laravel Livewire
- Alpine.js
- Tailwind CSS
- SQLite/MySQL
- PHPUnit & Laravel Test Framework

## ✨ Extras
- πŸ” Animated alert messages using Tailwind transitions
- πŸ§ͺ TDD methodology with red-green cycle
- πŸ“Έ Image storage in storage/app/public/profile_picture
- πŸ§ͺ Factories used for database seeding and testing

## 🀝 Contribution
To contribute:
- Fork the repository
- Create your branch (git checkout -b feature/feature-name)
- Commit your changes (git commit -m 'Add feature')
- Push to the branch (git push origin feature/feature-name)
- Open a Pull Request

## πŸ“ License
MIT License Β© Emmanuel John
66 changes: 66 additions & 0 deletions app/Livewire/CreateUser.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php

namespace App\Livewire;

use App\Models\User;
use Livewire\Component;
use Livewire\WithFileUploads;
use Illuminate\Support\Facades\Hash;

class CreateUser extends Component
{
use WithFileUploads;
public $firstname;
public $lastname;
public $email;
public $phone_number;
public $gender;
public $country;
public $profile_picture;
public $password;
public $password_confirmation;

protected $rules = [
'firstname' => 'required|string|max:255',
'lastname' => 'required|string|max:255',
'email' => 'required|email|unique:users,email',
'phone_number' => 'nullable|string|max:40|min:7',
'gender' => 'nullable|string|max:6|min:4',
'password' => 'required|string|min:8|confirmed',
'country' => 'nullable|string|max:255',
'profile_picture' => 'nullable|image|mimes:jpeg,png,jpg,gif,svg|max:1024',
];

public function submit()
{
// Validate all fields, including the profile picture
$this->validate();

// Handle the profile picture upload if it exists
$profilePicturePath = null;
if ($this->profile_picture) {
$profilePicturePath = $this->profile_picture->store('profile_picture', 'public'); // Store the image in public disk
}
// Create the user with the uploaded profile picture path
User::create([
'firstname' => $this->firstname,
'lastname' => $this->lastname,
'email' => $this->email,
'phone_number' => $this->phone_number,
'gender' => $this->gender,
'password' => Hash::make($this->password),
'country' => $this->country,
'profile_picture' => $profilePicturePath, // Store the path of the image
]);

session()->flash('message', 'User created successfully');
$this->reset(); // Reset the form fields after submission

sleep(1);
}

public function render()
{
return view('livewire.create-user');
}
}
30 changes: 30 additions & 0 deletions app/Livewire/DeleteUser.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

namespace App\Livewire;

use Livewire\Component;
use App\Models\User;

class DeleteUser extends Component
{
public $userId;

public function mount($userId)
{
$this->userId = $userId;
}

public function delete()
{
$user = User::findOrFail($this->userId);
$user->delete();

session()->flash('success', 'User deleted successfully.');
return redirect()->route('users.index');
}

public function render()
{
return view('livewire.delete-user');
}
}
58 changes: 58 additions & 0 deletions app/Livewire/UpdateUser.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php

namespace App\Livewire;

use App\Models\User;
use Livewire\Component;

class UpdateUser extends Component
{
public User $user;

public $firstname;
public $lastname;
public $email;
public $phone_number;
public $gender;
public $country;
public function mount(User $user)
{
$this->firstname = $user->firstname;
$this->lastname = $user->lastname;
$this->email = $user->email;
$this->phone_number = $user->phone_number;
$this->gender = $user->gender;
$this->country = $user->country;
}

public function updated($property)
{
$this->validateOnly($property, [
'firstname' => 'required|string',
'lastname' => 'required|string',
'email' => 'required|email|unique:users,email,' . $this->user->id,
]);
}

public function update()
{
$validated = $this->validate([
'firstname' => 'required|string|max:255',
'lastname' => 'required|string|max:255',
'email' => 'required|email|unique:users,email,' . $this->user->id,
'phone_number' => 'nullable|string|max:40|min:7',
'gender' => 'nullable|in:Male,Female',
'country' => 'nullable|string|max:255',
]);

$this->user->update($validated);

session()->flash('message', 'User updated successfully!');
return redirect()->route('user.view', $this->user->id);
}

public function render()
{
return view('livewire.update-user');
}
}
22 changes: 22 additions & 0 deletions app/Livewire/UserList.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

namespace App\Livewire;

use Livewire\Component;
use App\Models\User;

class UserList extends Component
{
public $users;

public function mount()
{
$this->users = User::all(); // Fetch all users
}


public function render()
{
return view('livewire.user-list');
}
}
Loading