Your Cart
Loading
Only -1 left

100% Fully Functional E-Commerce-Website+Frontpage

On Sale
$15.00
$15.00
Seller is unable to receive payments since their PayPal or Stripe account has not yet been connected.

# TikTok Admin Inventory


Shared storefront + admin inventory system built as a browser-first PHP/MySQL application for XAMPP-style hosting.


The project has two user-facing surfaces:


- Customer storefront for browsing products, registering, logging in, managing a cart, checking out, and viewing order history.

- Admin panel for catalog management, customer management, order processing, incoming stock receiving, reporting, notifications, branding, currency, and category management.


## Stack


### Languages


- HTML5

- CSS3

- JavaScript (ES modules, no bundler)

- PHP 8.0+ (plain PHP, no framework)

- MySQL / MariaDB


### Backend Runtime


- Apache with `mod_rewrite`

- PHP sessions for authentication

- PDO for database access

- JSON and multipart/form-data request handling


### Frontend Runtime


- No Node.js build step

- No Composer dependencies

- Uses native browser `fetch()` with same-origin session cookies


## Project Structure


```text

tiktok-admin/

|-- admin/

|  |-- index.html       # admin shell

|  `-- login.html       # admin login page

|-- api/

|  |-- index.php        # action router

|  |-- controllers/      # thin route handlers

|  |-- services/        # business logic

|  `-- core/          # auth, request, response, db, helpers

|-- assets/

|  |-- css/          # storefront and admin styles

|  |-- images/         # fallback brand/product assets

|  `-- js/           # storefront/admin modules

|-- config/

|  `-- app.php         # environment-backed app config

|-- database/

|  |-- main.sql        # schema, seed data, reporting views

|  `-- README.md        # database notes

|-- uploads/

|  |-- products/        # uploaded product images

|  `-- branding/        # uploaded branding logo images

|-- .htaccess          # admin clean-URL rewrites

|-- index.html         # customer storefront

`-- README.md

```


## Main Features


- Shared session-based authentication across storefront and admin.

- Seeded single-admin installation model.

- Customer self-registration.

- Product catalog with variants, variant-level stock, images, categories, and low-stock thresholds.

- Persistent cart tied to logged-in customers.

- Checkout flow with shipping data, payment transaction record creation, and automatic stock deduction.

- Admin order processing with payment/shipment status updates and shipment tracking details.

- Incoming inventory orders from suppliers, partial receiving, and stock reconciliation.

- Manual stock adjustments and inventory movement history.

- Auto-generated low-stock and out-of-stock alerts.

- In-site notification feed.

- Dashboard, inventory, and order reports backed by SQL views.

- Branding, logo, currency, and category management from admin settings.

- Soft-delete archive flows for products, orders, users, incoming orders, and categories.


## Requirements


- XAMPP or equivalent Apache + PHP + MySQL stack

- PHP 8.0 or newer

- MySQL 8+ or MariaDB with InnoDB and `utf8mb4`

- Write access for `uploads/products` and `uploads/branding`


PHP 8.0+ is required because the codebase uses features such as `mixed`, `str_contains()`, and `??=`.


## Configuration


Configuration is loaded from [config/app.php](/c:/xampp/htdocs/tiktok-admin/config/app.php). There is no `.env` file in the repo; the app reads from system env vars, Apache `SetEnv`, `$_SERVER`, or `$_ENV`.


| Key | Default | Purpose |

| --- | --- | --- |

| `DB_HOST` | `127.0.0.1` | MySQL host |

| `DB_PORT` | `3306` | MySQL port |

| `DB_NAME` | `tiktok_admin` | Database name |

| `DB_USER` | `root` | Database username |

| `DB_PASSWORD` | `` | Database password |

| `APP_SESSION_NAME` | `tiktok_admin_session` | PHP session cookie name |


Uploads are configured in code:


- Product images: `uploads/products`

- Branding/logo images: `uploads/branding`


## Setup


1. Put the project in your web root, for example `C:\xampp\htdocs\tiktok-admin`.

2. Start Apache and MySQL in XAMPP.

3. Import [database/main.sql](/c:/xampp/htdocs/tiktok-admin/database/main.sql).

4. If you are not using the default database connection, set `DB_HOST`, `DB_PORT`, `DB_NAME`, `DB_USER`, and `DB_PASSWORD` before loading the app.

5. Make sure Apache can write to:

  - `uploads/products`

  - `uploads/branding`

6. Open the application:

  - Storefront: `http://localhost/tiktok-admin/`

  - Admin login: `http://localhost/tiktok-admin/admin/login`

  - Admin dashboard: `http://localhost/tiktok-admin/admin/?page=dashboard`


## URLs and Routing


### Browser Pages


- `/` or `/index.html`: storefront

- `/admin/login` or `/admin/login.html`: admin login

- `/admin/` or `/admin/index.html?page=dashboard`: admin workspace


### API Routing


All API traffic goes through:


```text

api/index.php?action=<action_name>

```


Example:


```text

GET /api/index.php?action=products.list&category=General

POST /api/index.php?action=auth.login

```


This is an action-routed API, not path-based REST. The frontend uses GET for reads and POST for writes, but the router itself does not strictly enforce HTTP verbs.


## Authentication Model


- Authentication uses PHP sessions from [api/core/Auth.php](/c:/xampp/htdocs/tiktok-admin/api/core/Auth.php).

- The frontend sends requests with `credentials: 'same-origin'`.

- `Auth::requireUser()` protects customer/admin actions.

- `Auth::requireAdmin()` protects admin-only actions.

- Admin self-registration is disabled.

- The current admin management flow only supports one seeded admin account.


## Seeded Admin Account


The SQL seed guarantees an admin user with this email:


- Email: `devoryn@gmail.com`


The password is not stored in plaintext anywhere in the repo. The previous README listed `Admin123!`, but that value does not match the hash currently seeded in [database/main.sql](/c:/xampp/htdocs/tiktok-admin/database/main.sql).


If you import the database and need to set a known password, generate a new hash locally and update the admin row:


```powershell

C:\xampp\php\php.exe -r "echo password_hash('ChangeMe123!', PASSWORD_DEFAULT), PHP_EOL;"

```


Then apply the generated hash in MySQL:


```sql

UPDATE users

SET password_hash = 'PASTE_GENERATED_HASH_HERE'

WHERE email = 'devoryn@gmail.com';

```


## Request and Response Contract


### Request Formats


- GET requests use query parameters.

- POST requests use either:

 - JSON body with `Content-Type: application/json`

 - `multipart/form-data` for file uploads


### File Upload Fields


- Product image upload field: `image`

- Branding logo upload field: `logo_image`


Allowed image MIME types:


- `image/jpeg`

- `image/png`

- `image/webp`

- `image/gif`


### Success Response


```json

{

 "success": true,

 "data": {}

}

```


### Error Response


```json

{

 "success": false,

 "message": "Human-readable error message",

 "errors": {}

}

```


Unexpected server errors return HTTP 500 with an extra `detail` field.


## API Reference


Access levels used below:


- `Public`: no login required

- `User`: authenticated customer or admin session

- `Admin`: authenticated admin session


### Auth


| Action | Frontend method | Access | Purpose | Main inputs |

| --- | --- | --- | --- | --- |

| `auth.me` | GET | Public | Return current session user and bootstrap flags | none |

| `auth.login` | POST | Public | Login user and start session | `email`, `password` |

| `auth.logout` | POST | Public/session | Destroy current session | none |

| `auth.register_customer` | POST | Public | Create customer account and auto-login | `first_name`, `last_name`, `email`, `password`, optional `phone` |

| `auth.change_password` | POST | User | Change current user's password | `current_password`, `new_password`, optional `confirm_password` |

| `auth.update_profile` | POST | User | Update profile and optional default shipping address | `first_name`, `last_name`, `email`, optional `phone`, `shipping_*` fields |


Notes:


- Customer registration logs the new user in immediately.

- Passwords must be at least 8 characters.

- First-admin registration exists in code as a disabled path, but it is not routed by `api/index.php`.


### Products


| Action | Frontend method | Access | Purpose | Main inputs |

| --- | --- | --- | --- | --- |

| `products.list` | GET | Public/Admin | List products with variants and stock summaries | query: `search`, `stock_status`, `category`, `visibility` |

| `products.get` | GET | Public/Admin | Get one product with variants | query: `id` |

| `products.save` | POST | Admin | Create or update product and variants | `id` optional, `product_name`, `base_price`, optional `category`, `short_description`, `description`, `low_stock_threshold`, `variants` or `variants_json`, optional `image` |

| `products.delete` | POST | Admin | Soft-delete product | `id` |

| `products.archive` | POST | Admin | Soft-delete product | `id` |

| `products.restore` | POST | Admin | Restore archived product and its variants | `id` |

| `products.purge` | POST | Admin | Permanently delete archived product | `id` |

| `products.restore_all` | POST | Admin | Restore archived products matching filters | `search`, `stock_status`, `category` |

| `products.purge_all` | POST | Admin | Permanently delete archived products matching filters | `search`, `stock_status`, `category` |


Notes:


- `products.delete` is an alias of `products.archive`.

- If no variants are provided, the backend creates a default variant.

- Variant payload items can include `id`, `variant_name`, `size_label`, `color_label`, `price_override`, `stock_quantity`, and `low_stock_threshold`.

- Product save accepts either JSON fields or multipart form-data.

- Variant stock alerts are synced after saves and stock-affecting changes.


### Cart


| Action | Frontend method | Access | Purpose | Main inputs |

| --- | --- | --- | --- | --- |

| `cart.get` | GET | User | Get current active cart | none |

| `cart.add` | POST | User | Add variant to cart or increase quantity | `product_variant_id`, optional `quantity` |

| `cart.update` | POST | User | Change cart item quantity | `cart_item_id`, `quantity` |

| `cart.remove` | POST | User | Remove cart item | `cart_item_id` |


Notes:


- Cart rows are tied to a single active cart per user.

- The backend validates stock before add/update.


### Orders


| Action | Frontend method | Access | Purpose | Main inputs |

| --- | --- | --- | --- | --- |

| `orders.checkout` | POST | User | Convert active cart into order | `shipping_recipient_name`, `shipping_address_line_1`, `shipping_city`, `shipping_country`, optional `shipping_phone`, `shipping_address_line_2`, `shipping_state_region`, `shipping_postal_code`, `payment_method`, `notes` |

| `orders.my_list` | GET | User | List current user's orders | none |

| `orders.list` | GET | Admin | List all orders | query: `search`, `order_status`, `payment_status`, `shipment_status`, `visibility` |

| `orders.update` | POST | Admin | Update order, payment, and shipment state | `id`, optional `order_status`, `payment_status`, `shipment_status`, `notes`, `status_note`, `payment_method`, `tracking_number`, `courier_name`, `shipment_notes` |

| `orders.archive` | POST | Admin | Archive order | `id` |

| `orders.restore` | POST | Admin | Restore archived order | `id` |

| `orders.purge` | POST | Admin | Permanently delete archived order | `id` |

| `orders.restore_all` | POST | Admin | Restore archived orders matching filters | `search`, `order_status`, `payment_status`, `shipment_status` |

| `orders.purge_all` | POST | Admin | Permanently delete archived orders matching filters | `search`, `order_status`, `payment_status`, `shipment_status` |


Notes:


- Checkout creates:

 - `orders`

 - `order_items`

 - `payment_transactions`

 - `order_status_history`

 - `inventory_movements`

- Checkout deducts stock immediately.

- Cancelling an order through `orders.update` restocks inventory.

- Admin order updates also sync payment transaction and shipment records.


### Users


| Action | Frontend method | Access | Purpose | Main inputs |

| --- | --- | --- | --- | --- |

| `users.list` | GET | Admin | List users | query: `role`, `search`, `visibility` |

| `users.save` | POST | Admin | Create or update customer account | `id` optional, `first_name`, `last_name`, `email`, `role`, optional `phone`, `account_status`, optional `password` |

| `users.delete` | POST | Admin | Archive customer account | `id` |

| `users.archive` | POST | Admin | Archive customer account | `id` |

| `users.restore` | POST | Admin | Restore archived customer account | `id` |

| `users.purge` | POST | Admin | Permanently delete archived customer account | `id` |

| `users.restore_all` | POST | Admin | Restore archived users matching filters | `role`, `search` |

| `users.purge_all` | POST | Admin | Permanently delete archived users matching filters | `role`, `search` |


Notes:


- `users.save` only allows `role=customer`.

- Additional admin accounts are intentionally blocked by the current backend.

- The currently logged-in admin cannot archive or purge their own account.


### Inventory


| Action | Frontend method | Access | Purpose | Main inputs |

| --- | --- | --- | --- | --- |

| `inventory.incoming_list` | GET | Admin | List incoming stock orders | query: `search`, `incoming_status`, `visibility` |

| `inventory.save_incoming` | POST | Admin | Create or update incoming order | `id` optional, `supplier_id` or `supplier_name`, optional `reference_number`, `incoming_status`, `expected_date`, `notes`, `items` or `items_json` |

| `inventory.receive_incoming` | POST | Admin | Receive some or all incoming quantities | `id` or `incoming_order_id`, optional `items` or `items_json` with item `id` and `quantity_received` |

| `inventory.archive_incoming` | POST | Admin | Archive incoming order | `id` |

| `inventory.restore_incoming` | POST | Admin | Restore archived incoming order | `id` |

| `inventory.purge_incoming` | POST | Admin | Permanently delete archived incoming order | `id` |

| `inventory.restore_all_incoming` | POST | Admin | Restore archived incoming orders matching filters | `search`, `incoming_status` |

| `inventory.purge_all_incoming` | POST | Admin | Permanently delete archived incoming orders matching filters | `search`, `incoming_status` |

| `inventory.adjust_stock` | POST | Admin | Manual stock adjustment for variant | `product_variant_id`, `adjustment_mode` (`delta` or `set`), `value`, optional `notes` |

| `inventory.movements` | GET | Admin | Read recent movement history | query: `search` |


Notes:


- Suppliers can be auto-created from `supplier_name` if `supplier_id` is not provided.

- Incoming-order item payloads should include `product_variant_id`, `quantity_ordered`, and optional `unit_cost`.

- Receiving inventory updates stock, movement history, stock alerts, and admin notifications.

- `inventory.movements` is limited to the 200 most recent rows.


### Notifications


| Action | Frontend method | Access | Purpose | Main inputs |

| --- | --- | --- | --- | --- |

| `notifications.list` | GET | User | List notifications and unread count | query: `limit` |

| `notifications.mark_read` | POST | User | Mark one or all notifications read | optional `id` |


Notes:


- Admin notification listing also triggers stock-alert synchronization.

- `limit` is capped between 1 and 100.


### Reports


| Action | Frontend method | Access | Purpose | Main inputs |

| --- | --- | --- | --- | --- |

| `reports.dashboard` | GET | Admin | Dashboard metrics, alerts, recent orders, and analytics | none |

| `reports.inventory` | GET | Admin | Inventory status plus daily movement series | query: `search`, `stock_status` |

| `reports.orders` | GET | Admin | Outgoing and incoming order report views | query: `search`, `order_status` |


Report payload details:


- `reports.dashboard` returns `metrics`, `recent_orders`, `low_stock_items`, `notifications`, and `analytics`.

- `reports.inventory` returns `inventory` and `daily_movements`.

- `reports.orders` returns `outgoing_orders` and `incoming_orders`.


### Settings and Branding


| Action | Frontend method | Access | Purpose | Main inputs |

| --- | --- | --- | --- | --- |

| `settings.public_branding` | GET | Public | Get site name, admin title, logo path, and currency | none |

| `settings.public_categories` | GET | Public | Get active and archived category records | none |

| `settings.save_branding` | POST | Admin | Update branding only | optional `site_name`, `admin_panel_title`, `logo_path`, `remove_logo`, `currency_*`, optional `logo_image` |

| `settings.save_everything` | POST | Admin | Update branding and admin credentials | branding fields plus `admin_email`, optional `admin_password`, `admin_password_confirm`, required `current_password` when credentials change |

| `settings.save_categories` | POST | Admin | Bulk save categories | `categories` array or `categories_text` or `categories_json` |

| `settings.create_category` | POST | Admin | Create category record | `label`, optional `details` |

| `settings.update_category` | POST | Admin | Update category record | `id`, `label`, optional `details` |

| `settings.archive_category` | POST | Admin | Archive category record | `id` |

| `settings.restore_category` | POST | Admin | Restore category record | `id` |


Notes:


- Branding/logo changes are stored in `app_settings`.

- Currency is stored as JSON in `app_settings.currency_settings`.

- Categories are stored as JSON in `app_settings.product_categories`, not in a dedicated table.

- Renaming a category updates matching `products.category` values.


## Frontend Surfaces


### Storefront


Primary file: [index.html](/c:/xampp/htdocs/tiktok-admin/index.html)


Backed by:


- [assets/js/shop.js](/c:/xampp/htdocs/tiktok-admin/assets/js/shop.js)

- [assets/js/api-client.js](/c:/xampp/htdocs/tiktok-admin/assets/js/api-client.js)

- [assets/js/branding.js](/c:/xampp/htdocs/tiktok-admin/assets/js/branding.js)

- [assets/css/app.css](/c:/xampp/htdocs/tiktok-admin/assets/css/app.css)


Capabilities:


- Product browsing and filtering

- Customer login/register

- Cart management

- Checkout

- Order history

- Profile and password management

- Branding/currency display from public settings


### Admin Panel


Primary files:


- [admin/login.html](/c:/xampp/htdocs/tiktok-admin/admin/login.html)

- [admin/index.html](/c:/xampp/htdocs/tiktok-admin/admin/index.html)


Backed by:


- [assets/js/admin-login.js](/c:/xampp/htdocs/tiktok-admin/assets/js/admin-login.js)

- [assets/js/admin.js](/c:/xampp/htdocs/tiktok-admin/assets/js/admin.js)

- [assets/css/admin.css](/c:/xampp/htdocs/tiktok-admin/assets/css/admin.css)


Admin pages:


- Dashboard

- Products

- Categories

- Orders

- Customers

- Inventory

- Reports

- Settings


## Database Overview


Primary schema file: [database/main.sql](/c:/xampp/htdocs/tiktok-admin/database/main.sql)


### Core Tables


#### Identity and Settings


- `roles`

- `users`

- `user_addresses`

- `app_settings`


#### Catalog


- `products`

- `product_images`

- `product_variants`


#### Cart and Checkout


- `carts`

- `cart_items`

- `orders`

- `order_items`

- `payment_transactions`

- `shipments`

- `order_status_history`


#### Inventory and Suppliers


- `suppliers`

- `incoming_orders`

- `incoming_order_items`

- `inventory_movements`

- `stock_alerts`


#### Notifications and Audit


- `notifications`

- `activity_logs`


### Reporting Views


- `vw_product_inventory_status`

- `vw_product_stock_summary`

- `vw_order_overview`

- `vw_incoming_order_overview`

- `vw_daily_inventory_movements`

- `vw_dashboard_metrics`


### Important Schema Notes


- Every sellable product should have at least one row in `product_variants`.

- Simple products use a default variant.

- Order rows store snapshots of product/variant names and pricing in `order_items`.

- Categories are denormalized strings on `products.category`.

- `activity_logs` exists in the schema but is not currently written by the PHP backend.


## Important Status Values


### User Status


- `active`

- `inactive`

- `suspended`


### Incoming Order Status


- `draft`

- `ordered`

- `partially_received`

- `received`

- `cancelled`


### Order Status


- `pending`

- `processing`

- `shipped`

- `completed`

- `cancelled`


### Payment Status


- `pending`

- `paid`

- `failed`

- `refunded`


### Shipment Status


Order-level status:


- `pending`

- `preparing`

- `shipped`

- `delivered`

- `cancelled`


Shipment table status:


- `pending`

- `preparing`

- `shipped`

- `in_transit`

- `delivered`

- `returned`

- `cancelled`


### Inventory Movement Types


- `incoming`

- `sale`

- `manual_adjustment`

- `return`

- `restock`

- `cancellation_restock`

- `damage`

- `write_off`


## Business Rules Implemented by the Backend


- Checkout locks variants, validates live stock, deducts stock, creates movement history, creates payment transaction, records order history, and checks out the cart.

- Receiving incoming stock increments variant stock and creates `incoming` movements.

- Manual stock adjustment can either apply a delta or set an absolute quantity.

- Stock alerts resolve automatically when stock recovers or a product/variant is inactive or archived.

- Admin notifications are created for new orders, incoming-stock receipts, and stock alerts.

- Archived products restore their variants as active.

- Archived users can fail permanent deletion if they are still linked to order history.


## Clean-URL Notes


The root [`.htaccess`](/c:/xampp/htdocs/tiktok-admin/.htaccess) currently provides:


- canonical redirect from `/admin/login.html` to `/admin/login`

- canonical redirect from explicit `index.html` requests to cleaner URLs for supported admin pages


It does not implement a full REST rewrite layer for the API.


## Development Notes


- This is a no-framework PHP application. Controllers are thin and most logic lives in `api/services`.

- The API is same-origin only and session-based. There is no token auth layer.

- There is no migration tool. `database/main.sql` is the source of truth.

- There is no automated test suite in the current repo.

- Admin settings UI uses `settings.save_everything`; `settings.save_branding` still exists as a narrower backend route.

- Product and branding uploads are stored on disk, not in the database.


## Useful Files to Read First


- [api/index.php](/c:/xampp/htdocs/tiktok-admin/api/index.php)

- [api/core/Auth.php](/c:/xampp/htdocs/tiktok-admin/api/core/Auth.php)

- [api/core/Request.php](/c:/xampp/htdocs/tiktok-admin/api/core/Request.php)

- [api/services/ProductService.php](/c:/xampp/htdocs/tiktok-admin/api/services/ProductService.php)

- [api/services/OrderService.php](/c:/xampp/htdocs/tiktok-admin/api/services/OrderService.php)

- [api/services/InventoryService.php](/c:/xampp/htdocs/tiktok-admin/api/services/InventoryService.php)

- [api/services/SettingService.php](/c:/xampp/htdocs/tiktok-admin/api/services/SettingService.php)

- [database/main.sql](/c:/xampp/htdocs/tiktok-admin/database/main.sql)


You will get a ZIP (2MB) file