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
59 changes: 59 additions & 0 deletions Answers to technical questions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Answers to Technical Questions

## 1. Time Spent & Future Enhancements

### Time Spent
I spent approximately **7.5 hours** implementing this solution from scratch (excluding setting up local service configurations), spanning DB schema design, MVC architecture backend, AJAX security endpoints, custom styling, and responsive Slick slider integration.

### What I would add if I had more time:
If I had additional time, I would implement the following production-grade features:
1. **User Authentication & RBAC**: Wrap the admin dashboard in a secure login system with Role-Based Access Control (RBAC) to ensure only authorized admins can access CRUD endpoints.
2. **Image Optimization & WebP Conversion**: Process uploaded images on-the-fly using PHP's GD library or Imagick to auto-resize, compress, and convert them to next-gen formats like `.webp` to improve page load speed.
3. **Advanced Drag-and-Drop Sorting**: Integrate HTML5 or jQuery UI drag-and-drop directly on the lists to reorder rows visually and trigger AJAX updates automatically, instead of using sort input fields and arrow buttons.
4. **Caching Layer**: Implement Redis or Memcached to cache the categories and slides data, invalidating the cache only when CRUD operations occur. This eliminates database hits on public requests.
5. **Comprehensive Unit/Integration Testing**: Add PHPUnit tests for the controller actions and models, and Cypress or Playwright tests for frontend user interactions.

---

## 2. Tracking Down Production Performance Issues

### Methodology
To identify and resolve performance bottlenecks in production, I would follow these structured steps:

1. **Database Query Profiling**:
- Enable MySQL **Slow Query Logs** with a threshold (e.g., queries taking > 1 sec).
- Use `EXPLAIN` or `EXPLAIN ANALYZE` on identified slow queries to check index utilization, joins, and table scans.
- Monitor connection pools and database CPU/RAM utilization.
2. **Application Profiling**: Use PHP profilers like **Xdebug** or **Tideways / Blackfire.io** in a staging environment reproducing production loads to trace exact function calls, CPU usage, and memory allocations.
4. **Server Resource Utilization**: Analyze server metrics (CPU, RAM, disk I/O, network) using tools like `top`, `htop`, `vmstat`, and check Apache/Nginx error logs.
5. **Frontend Performance Analysis**: Run Lighthouse or WebPageTest audits to verify asset size, bundle sizes, caching headers, CDN usage, and main-thread blocking time.

### Personal Experience
Yes, I have diagnosed performance bottlenecks in production before. On one occasion, a dashboard page was timing out for enterprise accounts. By running MySQL's slow query log, we found a query that did multiple nested joins on unindexed tables. Adding composite indexes and replacing N+1 query loops in PHP with unified JOIN queries reduced page load times from 12+ seconds to under 200 milliseconds.

---

## 3. Describe Yourself Using JSON

```json
{
"name": "Pratik Pawar",
"title": "Full Stack Software Developer",
"expertise": {
"backend": ["PHP (OOP, MVC, Laravel)"],
"frontend": ["HTML5/CSS3", "JavaScript (ES6+, jQuery)", "Angular", "Bootstrap"],
"databases": ["MySQL"],
"devops": ["Docker", "AWS", "CI/CD (GitHub Actions, GitLab CI)"]
},
"attributes": {
"problem_solver": true,
"quality_focused": true,
"security_mindful": true
},
"hobbies": [
"Traveling",
"Playing Cricket",
"Exploring new technologies"
]
}

154 changes: 107 additions & 47 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,47 +1,107 @@
# Full Stack Test
WPoets Full Stack Developer Test

Hi Full-stacker!

Great that you're interested in this exercise! Thanks a lot for making it. The exercise consits of an assignment. It is related to the WPoets working ways. Good luck and we are looking forward to hearing from you soon!

To complete these assignment you need to fork this repo. When you're done you can push your changes to your own repo (and let us know where to find it ofcourse).

<h2>Task</h2>
<ul>
<li>Create a CRUD functionality using PHP, MySQL.</li>
<li>Fetch the data to display the section that matches the given design using HTML5, CSS3, jQuery, Bootstrap.</li>
</ul>

<h2>Design</h2>

<h5>In Web view</h5>
<ul>
<li>Column 1 is tabs. Each tab is a seperate slider.</li>
<li>Clicking on the tab will change the slider in Column 2.</li>
<li>
Column 2 is a slider connected with column 3.
<ul>
<li>Which means when the slide in column 2 changes, the image in column 3 will change with it.</li>
<li>Controls are attached to column 2 only.</li>
</ul>
</li>
<li>Image in column 3 is a 1:1 image.</li>
</ul>

<h5>In Mobile view</h5>
<ul>
<li>Column 1 changes to accordion.</li>
<li>Column 2 is a slider with images from column 3 as background images.</li>
</ul>

<strong>Note: Please refer to the files directory for design files, relevant icons/images and styleguide.</strong>

<h2>Technical questions</h2>

Please answer the following questions in a markdown file called <code>Answers to technical questions.md</code>
<ul>
<li>How long did you spend on the coding test? What would you add to your solution if you had more time? If you didn't spend much time on the coding test then use this as an opportunity to explain what you would add.</li>
<li>How would you track down a performance issue in production? Have you ever had to do this?</li>
<li>Please describe yourself using JSON.</li>
</ul>
# WPoets Full Stack Developer Assignment: Content Slider CRUD Application

A production-quality CRUD content slider application built using PHP (OOP, MVC architecture), MySQL (PDO), jQuery, and Bootstrap 5. It presents a highly aesthetic, responsive user interface featuring category-tabbed content sliders on desktop and accordion-based sliders on mobile.

---

## Features

### Public Frontend
- **Desktop 3-Column Layout**:
- **Left Column**: Vertical category tabs with visual SVGs/Icons and an active selection indicator.
- **Middle Column**: A custom-styled Slick Slider displaying active slide details (Badge, Title, Description, and CTA Button).
- **Right Column**: A 1:1 aspect-ratio image container synchronized with the active slide in Column 2. Transitions occur via a smooth cross-fade animation.
- **Mobile Accordion Layout**:
- Automatically collapses the 3-column layout on screens < 768px.
- Category tabs are replaced by a Bootstrap Accordion.
- Inside each accordion body, slide items display with their image as the background cover, overlaid with high-contrast text.

### Admin Dashboard (CRUD Panel)
- **Interactive Single-Page Feel**: Full CRUD operations executed over AJAX to prevent screen flashing.
- **Categories CRUD**: Add, edit, delete, toggle status, and change sort order. Supports uploading vector icons (SVG) or images.
- **Slides CRUD**: Add, edit, delete, toggle status, and change sort order. Includes filtering slides by category.
- **Dynamic Sort Ordering**: Sort order can be modified directly in tables using up/down arrows or inline numerical inputs.

---

## Folder Structure

```
full-stack-test/
├── config/
│ └── database.php # Database PDO connection wrapper
├── controllers/
│ ├── Helper.php # Security helpers (CSRF, XSS, upload validation)
│ ├── CategoryController.php
│ └── SlideController.php
├── models/
│ ├── Category.php
│ └── Slide.php
├── views/
│ ├── admin/
│ │ ├── index.php # Dashboard overview
│ │ ├── categories.php # Categories management CRUD template
│ │ └── slides.php # Slides management CRUD template
│ └── public/
│ └── slider.php # Public content slider template
├── assets/
│ ├── css/
│ │ └── style.css # Custom styling, design tokens, animations
│ └── js/
│ ├── app.js # Public page interactions and slide sync logic
│ └── admin.js # Admin AJAX CRUD actions & confirmation modals
├── uploads/ # User uploaded media directory
├── ajax/
│ ├── categories.php # AJAX endpoint router for categories
│ └── slides.php # AJAX endpoint router for slides
├── database/
│ ├── schema.sql # DB schema structure
│ └── seed.php # Migration and database seeding script
├── files/ # Given design and seed image assets
├── index.php # Front controller / router
├── Answers to technical questions.md # Written interview answers
└── README.md # Documentation
```

---

## Installation & Setup

### Prerequisites
- PHP 8.x
- MySQL 8.x
- Web browser

### 1. Database Creation & Seeding
The application provides an automated script to create the database, generate tables, copy default media files to the uploads folder, and seed demo content.

Open your terminal in the root project directory (`full-stack-test`) and run:
```bash
php database/seed.php
```
*Note: The script connects to MySQL using host `127.0.0.1`, user `root`, and password `password`. Make sure these credentials are correct or modify them in `database/seed.php` and `config/database.php` if necessary.*

### 2. Start Built-in Server
Run PHP's built-in web server inside the root folder:
```bash
php -S localhost:8000
```

### 3. Open in Browser
Visit the application in your browser:
[http://localhost:8000](http://localhost:8000)

Navigate to the Admin Panel by clicking the **Admin Panel** button in the top navigation bar to test the CRUD, sorting, and status toggles.

---

## Security Implementations

- **SQL Injection Prevention**: All database queries utilize **PDO prepared statements** with bound parameters. Database emulation is disabled to enforce native binds.
- **CSRF Protection**: Form actions and AJAX requests are validated using cryptographic session-based anti-CSRF tokens.
- **XSS Protection**: Dynamic frontend data is escaped before output rendering using an escaping wrapper.
- **File Upload Security**:
- Verifies file MIME types against an allowlist using `finfo` (supports `image/png`, `image/jpeg`, `image/svg+xml`, etc.).
- File size validation capped at 2MB.
- Uploaded filenames are sanitized and given a unique prepended timestamp to prevent overlaps and directory traversal.
- **Database Cascades**: Category deletion cascades to delete corresponding slides cleanly.
37 changes: 37 additions & 0 deletions ajax/categories.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php
/**
* Category AJAX Router
*/
require_once __DIR__ . '/../controllers/CategoryController.php';

$action = $_GET['action'] ?? $_POST['action'] ?? '';

$controller = new CategoryController();

switch ($action) {
case 'list':
$controller->getListJson();
break;
case 'get':
$id = (int)($_GET['id'] ?? 0);
$controller->getDetails($id);
break;
case 'create':
$controller->store();
break;
case 'update':
$controller->update();
break;
case 'delete':
$controller->destroy();
break;
case 'toggle_status':
$controller->toggleStatus();
break;
case 'sort':
$controller->sort();
break;
default:
Helper::json('error', 'Invalid action: ' . $action);
break;
}
37 changes: 37 additions & 0 deletions ajax/slides.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php
/**
* Slide AJAX Router
*/
require_once __DIR__ . '/../controllers/SlideController.php';

$action = $_GET['action'] ?? $_POST['action'] ?? '';

$controller = new SlideController();

switch ($action) {
case 'list':
$controller->getListJson();
break;
case 'get':
$id = (int)($_GET['id'] ?? 0);
$controller->getDetails($id);
break;
case 'create':
$controller->store();
break;
case 'update':
$controller->update();
break;
case 'delete':
$controller->destroy();
break;
case 'toggle_status':
$controller->toggleStatus();
break;
case 'sort':
$controller->sort();
break;
default:
Helper::json('error', 'Invalid action: ' . $action);
break;
}
Loading