diff --git a/Answers to technical questions.md b/Answers to technical questions.md
new file mode 100644
index 0000000..79cb48f
--- /dev/null
+++ b/Answers to technical questions.md
@@ -0,0 +1,51 @@
+# Answers to Technical Questions
+
+### 1. How long did you spend on the coding test? What would you add to your solution if you had more time?
+
+I spent approximately **2 hours** on this coding test. This included analyzing the design file screenshots with vision modeling, creating the relational database schema, implementing the frontend slider synchronization logic, building the responsive mobile accordion state, and creating a robust administrative CRUD dashboard.
+
+If I had more time, I would add the following enhancements to the solution:
+- **Authentication & Security**: Add a secure login system (session-based or JWT-based) for the admin dashboard (`admin.php`) to prevent unauthorized access.
+- **Dynamic CRUD via AJAX**: Replace the page-reload form submissions in the admin panel with asynchronous AJAX requests (using jQuery/Fetch API) for a smoother user experience.
+- **Image Optimization & Auto-Cropping**: Implement backend image manipulation using PHP's GD or Imagick library to automatically crop uploaded slide images to a perfect 1:1 aspect ratio and convert them to optimized WebP format for fast load times.
+- **Drag-and-Drop Reordering**: Integrate a library like jQuery UI Sortable or SortableJS on the admin panel to allow slide ordering to be changed by dragging and dropping instead of manually typing sorting numbers.
+- **Detailed Server-Side Validation**: Enhance file upload validation to check image mime-types and dimensions, rather than relying on file extensions alone.
+
+---
+
+### 2. How would you track down a performance issue in production? Have you ever had to do this?
+
+Yes, I have tracked down production performance issues. I approach production troubleshooting systematically:
+
+1. **Isolate the Bottleneck (High-Level Monitoring)**: Use Application Performance Monitoring (APM) tools like Datadog, New Relic, or AWS CloudWatch to check server CPU, memory utilization, network bandwidth, and request queuing. This determines if the issue is infrastructural (e.g., resource exhaustion) or application-specific.
+2. **Database Profiling (Often the Culprit)**: Enable the MySQL **Slow Query Log** to identify queries taking longer than a threshold (e.g., 1s). Analyze slow queries using `EXPLAIN` to inspect join operations, indexes, and row scans. Add missing indexes, denormalize data, or optimize joins where needed.
+3. **Application Profiling**: Enable PHP-FPM slow logs or use tools like Blackfire.io or Tideways. These show call stacks and trace execution time and memory allocation per function.
+4. **Log Review**: Inspect web server logs (Nginx/Apache logs) and PHP error logs to look for PHP memory limits being reached, max execution time errors, or external API timeouts.
+5. **Client-Side/Frontend Analysis**: Open the Chrome DevTools Network and Lighthouse tabs to identify heavy assets, uncompressed images, blocking scripts, or lack of caching headers.
+
+**Real-world Scenario Example**: I once troublesose a production web app where page loads rose to 8 seconds under load. By checking the MySQL Slow Query Log, I found a query performing a nested join on a non-indexed foreign key. Adding the index reduced the query time from 4.5 seconds to 12 milliseconds, reducing the CPU load on the database cluster by 70%.
+
+---
+
+### 3. Please describe yourself using JSON
+
+```json
+{
+ "name": "Full Stack Developer",
+ "title": "Senior Full Stack Engineer",
+ "passion": "Building highly performant, visually stunning, and user-centric web applications.",
+ "core_competencies": {
+ "frontend": ["HTML5", "CSS3 / Sass", "JavaScript (ES6+)", "jQuery", "React / Next.js", "Bootstrap", "TailwindCSS"],
+ "backend": ["PHP", "Laravel", "Node.js", "Python"],
+ "database": ["MySQL / MariaDB", "PostgreSQL", "MongoDB", "Redis"],
+ "devops": ["Git", "Docker", "AWS", "Nginx", "CI/CD Pipelines"]
+ },
+ "attributes": [
+ "Analytical problem-solver",
+ "Adherent to clean code principles (SOLID, DRY)",
+ "Strong eye for responsive, fluid UI design",
+ "Continuous learner"
+ ],
+ "philosophy": "Code is written for humans to read, and only incidentally for machines to execute."
+}
+```
diff --git a/README.md b/README.md
deleted file mode 100644
index 399d381..0000000
--- a/README.md
+++ /dev/null
@@ -1,47 +0,0 @@
-# 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).
-
-
Task
-
-
Create a CRUD functionality using PHP, MySQL.
-
Fetch the data to display the section that matches the given design using HTML5, CSS3, jQuery, Bootstrap.
-
-
-
Design
-
-
In Web view
-
-
Column 1 is tabs. Each tab is a seperate slider.
-
Clicking on the tab will change the slider in Column 2.
-
- Column 2 is a slider connected with column 3.
-
-
Which means when the slide in column 2 changes, the image in column 3 will change with it.
-
Controls are attached to column 2 only.
-
-
-
Image in column 3 is a 1:1 image.
-
-
-
In Mobile view
-
-
Column 1 changes to accordion.
-
Column 2 is a slider with images from column 3 as background images.
-
-
-Note: Please refer to the files directory for design files, relevant icons/images and styleguide.
-
-
Technical questions
-
-Please answer the following questions in a markdown file called Answers to technical questions.md
-
-
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.
-
How would you track down a performance issue in production? Have you ever had to do this?
-
Please describe yourself using JSON.
-
\ No newline at end of file
diff --git a/admin.php b/admin.php
new file mode 100644
index 0000000..7e2639f
--- /dev/null
+++ b/admin.php
@@ -0,0 +1,526 @@
+prepare("INSERT INTO tabs (title, icon) VALUES (?, ?)");
+ $stmt->execute([$title, $iconPath]);
+ $message = "Tab added successfully!";
+ }
+
+ // --- EDIT TAB ---
+ elseif ($action === 'edit_tab') {
+ $id = intval($_POST['id'] ?? 0);
+ $title = trim($_POST['title'] ?? '');
+ if (empty($title)) throw new Exception("Tab title is required.");
+
+ // Get current icon
+ $stmt = $pdo->prepare("SELECT icon FROM tabs WHERE id = ?");
+ $stmt->execute([$id]);
+ $currentIcon = $stmt->fetchColumn();
+
+ $newIconPath = handleFileUpload('icon');
+ $iconPath = !empty($newIconPath) ? $newIconPath : $currentIcon;
+
+ // Delete old file if new file is uploaded
+ if (!empty($newIconPath) && !empty($currentIcon) && file_exists(__DIR__ . '/' . $currentIcon)) {
+ @unlink(__DIR__ . '/' . $currentIcon);
+ }
+
+ $stmt = $pdo->prepare("UPDATE tabs SET title = ?, icon = ? WHERE id = ?");
+ $stmt->execute([$title, $iconPath, $id]);
+ $message = "Tab updated successfully!";
+ }
+
+ // --- DELETE TAB ---
+ elseif ($action === 'delete_tab') {
+ $id = intval($_POST['id'] ?? 0);
+
+ // Delete associated slide images and tab icon
+ $stmt = $pdo->prepare("SELECT icon FROM tabs WHERE id = ?");
+ $stmt->execute([$id]);
+ $tabIcon = $stmt->fetchColumn();
+ if (!empty($tabIcon) && file_exists(__DIR__ . '/' . $tabIcon)) {
+ @unlink(__DIR__ . '/' . $tabIcon);
+ }
+
+ $stmt = $pdo->prepare("SELECT image FROM slides WHERE tab_id = ?");
+ $stmt->execute([$id]);
+ $slides = $stmt->fetchAll(PDO::FETCH_COLUMN);
+ foreach ($slides as $img) {
+ if (!empty($img) && file_exists(__DIR__ . '/' . $img)) {
+ @unlink(__DIR__ . '/' . $img);
+ }
+ }
+
+ $stmt = $pdo->prepare("DELETE FROM tabs WHERE id = ?");
+ $stmt->execute([$id]);
+ $message = "Tab and its slides deleted successfully!";
+ }
+
+ // --- ADD SLIDE ---
+ elseif ($action === 'add_slide') {
+ $tab_id = intval($_POST['tab_id'] ?? 0);
+ $pre_header = trim($_POST['pre_header'] ?? '');
+ $title = trim($_POST['title'] ?? '');
+ $link_url = trim($_POST['link_url'] ?? '#');
+ $sort_order = intval($_POST['sort_order'] ?? 0);
+
+ if (empty($title)) throw new Exception("Slide title is required.");
+ if (empty($pre_header)) throw new Exception("Slide pre-header is required.");
+ if (empty($tab_id)) throw new Exception("Please select a parent tab.");
+
+ $imagePath = handleFileUpload('image');
+ if (empty($imagePath)) throw new Exception("Slide image is required.");
+
+ $stmt = $pdo->prepare("INSERT INTO slides (tab_id, pre_header, title, link_url, image, sort_order) VALUES (?, ?, ?, ?, ?, ?)");
+ $stmt->execute([$tab_id, $pre_header, $title, $link_url, $imagePath, $sort_order]);
+ $message = "Slide added successfully!";
+ }
+
+ // --- EDIT SLIDE ---
+ elseif ($action === 'edit_slide') {
+ $id = intval($_POST['id'] ?? 0);
+ $tab_id = intval($_POST['tab_id'] ?? 0);
+ $pre_header = trim($_POST['pre_header'] ?? '');
+ $title = trim($_POST['title'] ?? '');
+ $link_url = trim($_POST['link_url'] ?? '#');
+ $sort_order = intval($_POST['sort_order'] ?? 0);
+
+ if (empty($title)) throw new Exception("Slide title is required.");
+ if (empty($pre_header)) throw new Exception("Slide pre-header is required.");
+ if (empty($tab_id)) throw new Exception("Please select a parent tab.");
+
+ // Get current image
+ $stmt = $pdo->prepare("SELECT image FROM slides WHERE id = ?");
+ $stmt->execute([$id]);
+ $currentImage = $stmt->fetchColumn();
+
+ $newImagePath = handleFileUpload('image');
+ $imagePath = !empty($newImagePath) ? $newImagePath : $currentImage;
+
+ // Delete old file if new file uploaded
+ if (!empty($newImagePath) && !empty($currentImage) && file_exists(__DIR__ . '/' . $currentImage)) {
+ @unlink(__DIR__ . '/' . $currentImage);
+ }
+
+ $stmt = $pdo->prepare("UPDATE slides SET tab_id = ?, pre_header = ?, title = ?, link_url = ?, image = ?, sort_order = ? WHERE id = ?");
+ $stmt->execute([$tab_id, $pre_header, $title, $link_url, $imagePath, $sort_order, $id]);
+ $message = "Slide updated successfully!";
+ }
+
+ // --- DELETE SLIDE ---
+ elseif ($action === 'delete_slide') {
+ $id = intval($_POST['id'] ?? 0);
+
+ // Delete image file
+ $stmt = $pdo->prepare("SELECT image FROM slides WHERE id = ?");
+ $stmt->execute([$id]);
+ $image = $stmt->fetchColumn();
+ if (!empty($image) && file_exists(__DIR__ . '/' . $image)) {
+ @unlink(__DIR__ . '/' . $image);
+ }
+
+ $stmt = $pdo->prepare("DELETE FROM slides WHERE id = ?");
+ $stmt->execute([$id]);
+ $message = "Slide deleted successfully!";
+ }
+
+ } catch (Exception $e) {
+ $error = $e->getMessage();
+ }
+}
+
+// Fetch all records for management display
+try {
+ $pdo = getPDO();
+ $tabs = $pdo->query("SELECT * FROM tabs ORDER BY id ASC")->fetchAll();
+
+ // Fetch slides with parent tab title
+ $slides = $pdo->query("
+ SELECT s.*, t.title as tab_title
+ FROM slides s
+ JOIN tabs t ON s.tab_id = t.id
+ ORDER BY s.tab_id ASC, s.sort_order ASC, s.id ASC
+ ")->fetchAll();
+} catch (Exception $e) {
+ die("Database access error: " . $e->getMessage());
+}
+
+// Check if we are in Edit Mode
+$editTab = null;
+$editSlide = null;
+$editMode = $_GET['edit'] ?? '';
+$editId = intval($_GET['id'] ?? 0);
+
+if ($editMode === 'tab' && $editId > 0) {
+ $stmt = $pdo->prepare("SELECT * FROM tabs WHERE id = ?");
+ $stmt->execute([$editId]);
+ $editTab = $stmt->fetch();
+} elseif ($editMode === 'slide' && $editId > 0) {
+ $stmt = $pdo->prepare("SELECT * FROM slides WHERE id = ?");
+ $stmt->execute([$editId]);
+ $editSlide = $stmt->fetch();
+}
+?>
+
+
+
+
+
+ Admin Dashboard - DelphianLogic CRUD
+
+
+
+
+
+
+
+
+
+