# ⚡ Test Project: Module A — Speed Test

## 1. Project Overview

This assessment is a **Speed Test** designed to quickly and accurately evaluate practical proficiency across web technologies. Competitors must complete a total of 20 independent tasks across four categories — **A (Design)**, **B (Layout)**, **C (Frontend)**, and **D (Backend)** — within the allotted time. Each task is independent and may be attempted in any order.

---

## 2. Tech Stack & Constraints

| Category | Allowed Technologies | Restrictions |
| :--- | :--- | :--- |
| **A. Design** | GIMP | No other image editors (e.g., Photoshop) permitted |
| **B. Layout** | HTML, CSS | JavaScript is not permitted |
| **C. Frontend** | JavaScript (Vanilla) | No external libraries or frameworks permitted |
| **D. Backend** | PHP, MySQL | Database must be connected using PDO |

* All deliverables will be evaluated in a desktop Chrome browser.
* Save each task's deliverable in a subdirectory named after the task ID under `/module_a/`. (e.g., `/module_a/a1/`, `/module_a/b1/`)
* Design tasks must include the GIMP source file (`.xcf`) in the submission directory.

---

## 3. Tasks

### 🎨 A. Design — GIMP Proficiency

> **Provided files**: Files for A1 (`source.png`), A2 (`base.jpg`), and A3 (`photo.jpg`, `mask.png`) are pre-placed in each task's working directory. A4 and A5 have no provided files; competitors must create all assets from scratch.

#### **A1. Layer Compositing and Text Placement** `Easy`

Create an image in GIMP using a layered structure.

* Canvas size: **800×400px**, Resolution: **72dpi**
* **Layer 1**: Fill the background with the solid color `#2C3E50`.
* **Layer 2**: Open the provided image (`source.png`), place it at the **center** of the canvas, and set its opacity to **70%**.
* **Layer 3**: Add the text **"Hello, GIMP"** in white (`#FFFFFF`) at 36pt, and position it at the top-left of the canvas **(x: 20px, y: 20px)**.
* Save the project as a `.xcf` file first. Then flatten all layers (Flatten Image) and export the result as `result.png`.

#### **A2. Selection and Layer Compositing** `Easy`

Apply shape effects to an image using GIMP's selection tools and layers.

* Open the provided image (`base.jpg`) as the background layer.
* **Effect 1**: Add a new layer. Use the Rectangle Select tool to select the region from **(0, 0) to (200, 200)** in the top-left corner, fill it with `#E74C3C`, and set this layer's opacity to **60%**.
* **Effect 2**: Add another new layer. Create an elliptical selection with a diameter of **150px** at the **center** of the image. Go to `Selection → Border`, set the border width to **3px**, and fill the resulting selection with `#FFFFFF`.
* Save the project as a `.xcf` file first. Then flatten all layers (Flatten Image) and export the result as `result.jpg`.

#### **A3. Color Correction and Layer Mask** `Normal`

Apply color correction and a layer mask to the provided image (`photo.jpg`) in GIMP.

* **Color correction**: Use the Hue-Saturation tool to increase Saturation by **+40**.
* **Curves adjustment**: Use the Curves tool to raise the output value of highlights by **+20 or more** and lower the output value of shadows by **-20 or less** to increase contrast.
* **Layer mask**: Add a layer mask to the image layer. Paste the provided mask image (`mask.png`) into the mask layer so that the background outside the subject is removed.
* Save the project as a `.xcf` file first. Then export the result as `result.png`, ensuring the transparent background is preserved (PNG format).

#### **A4. Text Effect and Glow** `Normal`

Apply a glow visual effect to text in GIMP.

* Canvas size: **600×200px**, Background color: `#1A1A2E`
* Add the text **"SPEED TEST"** in white (`#FFFFFF`), Bold, 60pt, centered on the canvas.
* Duplicate the text layer and place the duplicate **below** the original text layer.
* Rasterize the duplicate layer (Layer → Rasterize). In the Layers panel, set the duplicate layer's **Mode** to **`Color`** first. Then set the foreground color to `#4A90D9` and apply `Edit → Fill with Foreground Color` so the blue color is applied.
* Apply **Gaussian Blur** with a radius of **8px** to the duplicate layer to complete the outer glow effect.
* Save the project as a `.xcf` file first, then export the result as `result.png`.

#### **A5. Text Warp and Filter Effects** `Hard`

Rasterize text and apply a combination of Distorts filters in GIMP to create visual distortion effects.

* Canvas size: **800×300px**, Background color: `#0D0D0D`
* Add the text **"DISTORTION"** in white (`#FFFFFF`), Bold, 72pt, centered on the canvas.
* Rasterize the text layer (Layer → Rasterize).
* Apply the following two Distorts filters to the rasterized text layer in order:
  * **Ripple**: Amplitude **8**, Wavelength **40**, Orientation: Horizontal
  * **Whirl and Pinch**: Whirl angle **20** degrees, Pinch **0**, Radius **1.0**
* Duplicate the distorted text layer. Apply **Gaussian Blur** with a radius of **3px** to the duplicate, then place the duplicate **below** the original layer to create a ghosting effect.
* Save the project as a `.xcf` file first, then export the result as `result.png`.

---

### 📐 B. Layout — HTML / CSS Only

#### **B1. Flexbox Card Layout** `Easy`

Build a card list layout using HTML and CSS only.

* Use **Flexbox** to arrange cards **4 per row**. (Use `flex-wrap: wrap`)
* Each card must contain: an image area (`<div>`, height **160px**, with a background color), a title (`<h3>`), body text (`<p>`), and a button (`<button>`).
* Set the gap between cards to **24px**.
* Create **8** dummy cards. Each card's image area must have a different background color.

#### **B2. Sticky Header and CSS Interaction** `Easy`

Build a header with CSS-only interactions using HTML and CSS only.

* Use **Flexbox** to arrange the header with a logo text on the left, 4 menu links in the center, and 2 buttons on the right.
* Implement a CSS-only interaction where hovering over a menu link reveals a **2px solid underline** with a `transition` effect. (Use `border-bottom` or a `::after` pseudo-element)
* Use `position: sticky; top: 0;` to keep the header fixed at the top while scrolling.
* Place dummy content with a minimum height of **3000px** below the header to enable scrolling.

#### **B3. CSS Grid Two-Column Layout** `Normal`

Build a two-column page layout using CSS Grid with HTML and CSS only.

* Use **CSS Grid** to create a layout with a fixed left sidebar (**250px**) and a right main content area (`1fr`).
* Place a vertical navigation menu (5 items) in the left sidebar. Style the first item to indicate an active state using background color and text color.
* Place a title, body text, and a **3-column card grid** (using CSS Grid, 6 cards) in sequence in the right main area.
* Apply `height: 100vh` and `overflow: hidden` to the layout wrapper. Apply `height: 100vh` and `overflow-y: auto` to both the sidebar and the main area so that each scrolls independently.

#### **B4. CSS Animations and Transitions** `Normal`

Implement various CSS animations and transition effects on a single page using HTML and CSS only.

* **Card hover effect**: Arrange 4 cards in a row using Flexbox. On hover, each card should move up **8px** and have a stronger shadow using a transition. (Use `transform: translateY(-8px)`, `box-shadow`, and `transition`)
* **Loading spinner**: Use `@keyframes` to create a circular loading spinner. The spinner should be **48px** in diameter with a **4px** border. Only the top border (`border-top`) should be colored `#3498DB`; the remaining borders should be `#e0e0e0`. The spinner rotates infinitely.
* **Fade-in text**: On page load, a heading (`<h1>`) should animate from `translateY(20px)` to `translateY(0)` while fading from `opacity: 0` to `opacity: 1` using `@keyframes`. (`animation-duration: 0.8s`, `animation-fill-mode: both`)

#### **B5. CSS Grid and Advanced Selectors** `Hard`

Build a two-column content layout with a CSS-only tab interaction using HTML and CSS only.

* Use **CSS Grid** to create a two-column layout: left column (65%) and right column (35%).
* **Left area**: Place a main image area (`<div>`, height **300px**, with a background color) and a horizontal thumbnail list below it (Flexbox, `overflow-x: auto`). Include **5** thumbnails, each as an **80×80px** `<div>` with a different background color.
* **Right area — upper**: Place a title, subtitle, and body text.
* **Right area — lower**: Implement 3 tabs (Overview / Details / Reviews) with their respective content areas.
  * Use `<input type="radio" name="tab">` and the CSS `:checked` selector **without JavaScript** so that only the selected tab's content is shown.
  * Hide the `<input type="radio">` elements with `display: none` and style the corresponding `<label>` elements to look like tab buttons.
* At the bottom of the right area, place a Primary button and a Secondary button side by side. Apply a `@keyframes` animation to the Primary button so its background color transitions smoothly on hover.

---

### ⚡ C. Frontend — JavaScript (Vanilla)

#### **C1. localStorage-based Memo App** `Easy`

Build a simple memo app using Vanilla JavaScript and `localStorage`.

* The page must include a text input (`<textarea>`) and a **"Save"** button.
* Clicking **"Save"** adds the memo to a JSON array stored under the `memos` key in `localStorage`. Empty content must not be saved.
* Render the saved memo list below the input area. Each item must include the memo content and a **"Delete"** button.
* Clicking **"Delete"** removes the item from `localStorage` and immediately updates the rendered list.
* The memo list must persist after a page refresh.

#### **C2. DOM Manipulation and Event Handling** `Easy`

Build a dynamic list with item management using Vanilla JavaScript.

* The page must include a text input (`<input type="text">`) and an **"Add"** button.
* Clicking **"Add"** or pressing Enter adds the input value as a new list item. Empty values must not be added. The input field is cleared after adding.
* Each item must include a **"Done"** button and a **"Delete"** button.
  * Clicking **"Done"** applies a strikethrough (`text-decoration: line-through`) to the item text. Clicking again toggles it back.
  * Clicking **"Delete"** removes the item immediately.
* Display the current **total item count** and **completed item count** at the top of the list in real time.

#### **C3. Dynamic JSON Rendering and Filtering** `Normal`

Build a page that fetches a local JSON file and dynamically renders its content using Vanilla JavaScript and the Fetch API.

* Fetch the provided `posts.json` file at `/module_a/c3/posts.json` using the Fetch API. The file contains an array of objects with the keys `id`, `title`, `body`, and `category`. (20 items total)
* Display a **loading spinner** (CSS animation) while fetching, and remove it when the fetch completes.
* Render all items as cards. Each card must display `id`, `title`, `body`, and `category`.
* Implement a search input that filters cards in real time by `title`. (Case-insensitive; updates immediately on each keystroke)
* If the Fetch request fails, display the error message **"Failed to load data."** on the screen.

> **Provided file**: `posts.json` (pre-placed in the competitor's working directory at `/module_a/c3/`)

#### **C4. Form Validation** `Normal`

Implement real-time form validation using Vanilla JavaScript.

* Build a registration form with the following fields: **Name**, **Email**, **Password**, **Confirm Password**
* Validation rules for each field:
  * **Name**: Required, at least 2 characters
  * **Email**: Required, must contain both `@` and `.`
  * **Password**: Required, at least 8 characters, must contain both letters and numbers
  * **Confirm Password**: Required, must match the Password field exactly
* When focus leaves a field (`blur` event), display an error message below that field if the validation rule is not met. Remove the error message immediately once the condition is satisfied.
* The **"Submit"** button must only be enabled when all fields are valid. Clicking it should trigger `alert("Registration complete.")`.

#### **C5. IntersectionObserver and Deferred Rendering** `Hard`

Implement infinite scroll and deferred rendering using `IntersectionObserver`.

* Define an array of 50 dummy items in JavaScript. Each item must have `id`, `title`, and `color`. (`color` is an arbitrary hex color value in `#RRGGBB` format)
* On initial load, render **10** items. Each item consists of a **200px-tall** color `<div>` and its `title` text. The first 10 items should display their `color` value as the background color immediately.
* Place a **sentinel element** (`<div id="sentinel">`) at the bottom of the list. When the sentinel enters the viewport, automatically render the next 10 items.
* While new items are being added, insert a **loading spinner element** directly before the sentinel to display it, and remove it once rendering is complete. (Simulate async behavior using `setTimeout` with 600ms)
* Once all **50 items** have been rendered, stop observing the sentinel (`observer.unobserve`) and display the message **"All items loaded."**
* New items should be added to the DOM with their color `<div>` initially set to `#cccccc` (grey). Use a **separate `IntersectionObserver` instance** to watch each `<div>`, and replace the background color with the real color from the element's `data-color` attribute the moment it enters the viewport.

---

### 🛢️ D. Backend — PHP / MySQL

> **Provided files**: `posts_dump.sql` for D4 and `data_dump.sql` for D5 are pre-placed in each task's working directory.

#### **D1. PDO-based CRUD API** `Easy`

Build a REST API to handle data using PHP and MySQL (PDO).

* Create an `items` table using the structure below. Submit the table creation SQL as `table.sql`.
  * `id` (INT, PK, AUTO_INCREMENT), `title` (VARCHAR 100, NOT NULL), `content` (TEXT), `created_at` (TIMESTAMP DEFAULT CURRENT_TIMESTAMP)
* Implement the following 4 endpoints in a single file `api.php`, branching by HTTP method (`$_SERVER['REQUEST_METHOD']`).
  * `GET /module_a/d1/api.php` → Return all items as a JSON array
  * `POST /module_a/d1/api.php` → Add a new item. Body (JSON): `{"title": "...", "content": "..."}`. Read the request body using `php://input` and parse it with `json_decode()`. On success, return the inserted row as JSON.
  * `PUT /module_a/d1/api.php?id={id}` → Update `title` and `content`. Body (JSON): `{"title": "...", "content": "..."}`. Read the request body using `php://input` and parse it with `json_decode()`. On success, return the updated row as JSON.
  * `DELETE /module_a/d1/api.php?id={id}` → Delete the item. On success, return `{"message": "deleted"}`.
* For PUT/DELETE requests with a non-existent `id`, return HTTP status **404** and `{"error": "Not found"}`.
* All responses must include a `Content-Type: application/json` header.

#### **D2. File Upload Handling** `Easy`

Implement image file upload functionality using PHP.

* In `upload.php`, implement both a file upload HTML form (GET) and the upload processing logic (POST). Set the form's `enctype` to `multipart/form-data`.
* Upload conditions: only **jpg, jpeg, png, gif** extensions are allowed; file size must be **2MB or less**.
* Save files that pass validation to the `uploads/` directory using the naming format **`{time()}_{original_filename}`**. If the `uploads/` directory does not exist, create it manually.
* On successful upload, display the uploaded image using an `<img>` tag.
* For validation failures, display the appropriate error message:
  * Invalid extension: **"File type not allowed."**
  * Size exceeded: **"File size exceeds 2MB."**

#### **D3. Session-based Authentication System** `Normal`

Implement user registration, login, and logout using PHP sessions and MySQL.

* `users` table: `id` (INT, PK, AUTO_INCREMENT), `email` (VARCHAR 100, UNIQUE), `password` (VARCHAR 255), `name` (VARCHAR 50), `created_at` (TIMESTAMP DEFAULT CURRENT_TIMESTAMP)
* **`register.php`**: On GET, render an HTML registration form. On POST, check for duplicate emails and save the password hashed with `password_hash()`, then redirect immediately to `login.php` without displaying a success message. If the email is already in use, display the error **"This email is already registered."** above the form.
* **`login.php`**: On GET, render an HTML login form. On POST, verify the password with `password_verify()`. On success, store `user_id`, `name`, and `email` in the session and redirect to `mypage.php`. On failure, display the error **"Incorrect email or password."** above the form.
* **`logout.php`**: On GET, destroy the session with `session_destroy()` and redirect to `login.php`.
* **`mypage.php`**: If `user_id` is not in the session, redirect to `login.php`. If logged in, display the message **"Welcome, {name}!"** and a logout link.

#### **D4. Pagination** `Normal`

Implement server-side pagination using PHP and MySQL (PDO).

* Create the `posts` table yourself, then import the provided SQL dump (`posts_dump.sql`). Table structure: `id`, `title`, `content`, `created_at`
* In `index.php`, display **10 posts per page**.
* The current page is determined by the URL query string `?page={n}`, defaulting to 1. Use `LIMIT` and `OFFSET` in your SQL query to retrieve only the data for the current page. If `page` is less than 1 or exceeds the total number of pages, redirect to page 1.
* Display pagination links at the bottom of the page. **Show all page numbers**, highlight the current page with an active style, and include Previous/Next buttons. Disable the Previous button on the first page and the Next button on the last page.
* Each post item must show the title and registration date. Clicking the title navigates to `view.php?id={id}`, which displays the full content of that post.

#### **D5. Multi-table Aggregate Queries** `Hard`

Implement a statistics API using multi-table JOINs and aggregate queries.

* Create all 3 tables yourself, then import the provided SQL dump (`data_dump.sql`). **Tables must be created in the order `users` → `posts` → `comments`** (to satisfy foreign key dependencies).
  * `users` (`id`, `name`, `created_at`)
  * `posts` (`id`, `user_id` — references users.id, `title`, `category`, `view_count`, `created_at`)
  * `comments` (`id`, `post_id` — references posts.id, `created_at`)
* In a single file `stats.php`, branch on the `type` query string and return the following 3 responses:
  * `GET /module_a/d5/stats.php?type=daily` → Return the number of new posts per day for the past 7 days, sorted by date ascending. Response format: `[{"date": "2026-04-06", "count": 5}, ...]`
  * `GET /module_a/d5/stats.php?type=category` → Return the total post count and average view count per category, sorted by post count descending. Response format: `[{"category": "tech", "post_count": 12, "avg_views": 340}, ...]`
  * `GET /module_a/d5/stats.php?type=top` → Return the top 5 posts by comment count, sorted by comment count descending. Response format: `[{"post_id": 3, "title": "...", "author": "Alice Johnson", "comment_count": 18}, ...]`
* All queries must use PDO **Prepared Statements**.
* For unknown `type` values, return HTTP **400** and `{"error": "Invalid type"}`.

---

## 4. Submission Guidelines

Upload all deliverables to the server directory `/module_a/`. Each task's output must be saved in a subdirectory named after the task ID. (e.g., `/module_a/a1/`, `/module_a/b1/`, `/module_a/c1/`, `/module_a/d1/`)

---

## 5. Marking Scheme — Total 25 Points

| Category | Task | Points |
| :--- | :--- | :---: |
| **A. Design** | A1. Layer Compositing and Text Placement | 0.5 |
| | A2. Selection and Layer Compositing | 0.5 |
| | A3. Color Correction and Layer Mask | 1.5 |
| | A4. Text Effect and Glow | 1.5 |
| | A5. Text Warp and Filter Effects | 2.0 |
| **B. Layout** | B1. Flexbox Card Layout | 0.5 |
| | B2. Sticky Header and CSS Interaction | 0.5 |
| | B3. CSS Grid Two-Column Layout | 1.5 |
| | B4. CSS Animations and Transitions | 1.5 |
| | B5. CSS Grid and Advanced Selectors | 2.0 |
| **C. Frontend** | C1. localStorage-based Memo App | 0.5 |
| | C2. DOM Manipulation and Event Handling | 0.5 |
| | C3. Dynamic JSON Rendering and Filtering | 1.5 |
| | C4. Form Validation | 1.5 |
| | C5. IntersectionObserver and Deferred Rendering | 2.0 |
| **D. Backend** | D1. PDO-based CRUD API | 0.5 |
| | D2. File Upload Handling | 0.5 |
| | D3. Session-based Authentication System | 1.5 |
| | D4. Pagination | 1.5 |
| | D5. Multi-table Aggregate Queries | 3.0 |
| **Total** | | **25** |
