diff --git a/public/index.php b/public/index.php index 8134ead..d1bd1da 100644 --- a/public/index.php +++ b/public/index.php @@ -4,6 +4,7 @@ // use app\app; use app\controllers\account; +use app\controllers\address; use app\controllers\admin; use app\controllers\cart; use app\controllers\category; @@ -14,6 +15,7 @@ use app\controllers\lost; use app\controllers\magic_link; use app\controllers\support; use app\controllers\transaction; +use app\models\user_settings; require_once __DIR__ . '/../vendor/autoload.php'; @@ -36,17 +38,25 @@ if (! isset($_SESSION['fingerprint'])) { } // these will be available to use in all twig templates +$user_settings = isset($_SESSION['user_id']) ? user_settings::getByUserId($_SESSION['user_id']) : null; +$theme = 'light'; +if (isset($_SESSION['user_id']) && $user_settings !== null && isset($user_settings['dark_theme'])) { + $theme = $user_settings['dark_theme'] ? 'dark' : 'light'; +} elseif (isset($_COOKIE["theme"])) { + $theme = $_COOKIE["theme"]; +} $defaults = [ 'copyright_year' => date('Y'), 'session' => $_SESSION, 'http_host' => $_SERVER['HTTP_HOST'], 'env' => $_ENV, 'is_user' => isset($_SESSION['user_id']), + 'user_settings' => $user_settings, 'is_admin' => isset($_SESSION['user_id']) && $_SESSION['user_id'] == 1, // uses cookie-js to get the client's preferred theme // used to conditionally deliver image assets // or styles based on theme - 'theme' => isset($_COOKIE["theme"]) ? $_COOKIE["theme"] : 'light', + 'theme' => $theme, // set your tailwind colors here for app themeing // the idea is to avoid using colors in your templates 'colors' => require dirname(__DIR__) . '/src/colors.php', @@ -64,23 +74,25 @@ if (str_starts_with(haystack: $route, needle: '/.well-known/lnurlp/')) { $route = '/lnurlp'; } -// Combined regex to match multiple dynamic routes in one go -if (preg_match('/^\/(transaction|user|order|product)\/([\w-]+)$/', $route, $matches)) { +// Use this controller for routes that include a model ID +if (preg_match('/^\/(address(?:\/edit|\/delete)?|transaction|user|order|quote|product|subscription|cart)\/([\w-]+)$/', $route, $matches)) { [$full, $type, $id] = $matches; - $controllers = [ - 'transaction' => fn($id) => transaction::view($defaults, $id), - 'user' => fn($id) => users::view($id), - 'order' => fn($id) => orders::view($id), - 'quote' => fn($id) => quotes::view($id), - 'product' => fn($id) => products::view($id), - 'subscription' => fn($id) => subscriptions::view($id), - 'cart' => fn($id) => cart::index($id), + $controller = [ + 'address/edit' => fn($id) => address::edit($defaults, $id), + 'address/delete' => fn($id) => address::delete($defaults, $id), + 'transaction' => fn($id) => transaction::view($defaults, $id), + 'user' => fn($id) => users::view($id), + 'order' => fn($id) => orders::view($id), + 'quote' => fn($id) => quotes::view($id), + 'product' => fn($id) => products::view($id), + 'subscription' => fn($id) => subscriptions::view($id), + 'cart' => fn($id) => cart::index($id), ]; - if (isset($controllers[$type])) { - $controller = $controllers[$type]($id); + if (isset($controller[$type])) { + $controller = $controller[$type]($id); } -} else { +} else { // Use this controller for static routes $controller = match ($route) { '/' => home::index($defaults), '/account/login' => account::login($defaults), @@ -95,7 +107,8 @@ if (preg_match('/^\/(transaction|user|order|product)\/([\w-]+)$/', $route, $matc '/account/orders' => $defaults['is_user'] ? account::orders($defaults) : header('Location: /account/login'), '/account/shipping' => $defaults['is_user'] ? account::shipping($defaults) : header('Location: /account/login'), '/account/address/edit' => $defaults['is_user'] ? account::address_edit($defaults) : header('Location: /account/login'), - '/account/address/confirm' => $defaults['is_user'] ? account::address_confirm($defaults) : header('Location: /account/login'), + '/account/address/set-default-shipping' => $defaults['is_user'] ? account::set_default_shipping($defaults) : header('Location: /account/login'), + '/account/address/set-default-billing' => $defaults['is_user'] ? account::set_default_billing($defaults) : header('Location: /account/login'), '/admin' => $defaults['is_admin'] ? admin::index($defaults) : lost::index($defaults), '/admin/users' => $defaults['is_admin'] ? admin::users($defaults) : lost::index($defaults), '/admin/orders' => $defaults['is_admin'] ? admin::orders($defaults) : lost::index($defaults), diff --git a/src/controllers/account.php b/src/controllers/account.php index 30abbed..25a7b40 100644 --- a/src/controllers/account.php +++ b/src/controllers/account.php @@ -10,16 +10,14 @@ class account { public static function index($defaults): void { - $user = users::getById($_SESSION['user_id']); - $addresses = addresses::getByUserId($_SESSION['user_id']); - $user_settings = user_settings::getByUserId($_SESSION['user_id']); + $user = users::getById($_SESSION['user_id']); + $addresses = addresses::getByUserId($_SESSION['user_id']); echo $GLOBALS['twig']->render('lib/pages/index.twig', array_merge($defaults, [ 'child_template' => 'account/index.twig', 'page_title' => 'Manage Account - ' . $_ENV['APP_NAME'], 'user' => $user, 'addresses' => $addresses, - 'user_settings' => $user_settings, 'breadcrumbs' => [ [ 'url' => null, @@ -46,7 +44,6 @@ class account $bill['state'], $bill['zip'], $bill['phone'], - $bill['hash'] ); $_SESSION['success'] = "Billing address saved!"; header('Location: /account/billing'); @@ -75,6 +72,8 @@ class account { if ($_SERVER['REQUEST_METHOD'] == 'POST') { users::updateProfileById($_SESSION['user_id'], $_POST); + $dark_theme = $_POST['dark_theme'] ?? false; + user_settings::update($_SESSION['user_id'], ['dark_theme' => $dark_theme]); header('Location: /account'); } } @@ -140,42 +139,6 @@ class account ])); } - public static function address_edit($defaults) - { - echo $GLOBALS['twig']->render('lib/pages/index.twig', array_merge($defaults, [ - 'child_template' => 'account/address/edit.twig', - 'page_title' => 'Edit Address - ' . $_ENV['APP_NAME'], - 'breadcrumbs' => [ - [ - 'url' => '/account', - 'title' => 'My Account', - ], - [ - 'url' => null, - 'title' => 'Edit Address', - ], - ], - ])); - } - - public static function address_confirm($defaults) - { - echo $GLOBALS['twig']->render('lib/pages/index.twig', array_merge($defaults, [ - 'child_template' => 'account/address/confirm.twig', - 'page_title' => 'Confirm Address - ' . $_ENV['APP_NAME'], - 'breadcrumbs' => [ - [ - 'url' => '/account', - 'title' => 'My Account', - ], - [ - 'url' => null, - 'title' => 'Confirm Address', - ], - ], - ])); - } - public static function login($defaults) { if ($_SERVER['REQUEST_METHOD'] == 'POST') { @@ -264,7 +227,6 @@ class account $ship['state'], $ship['zip'], $ship['phone'], - $ship['hash'] ); $_SESSION['success'] = "Shipping address saved!"; header('Location: /account/shipping'); @@ -289,6 +251,33 @@ class account ], ])); } + public static function set_default_shipping($defaults) + { + if ($_SERVER['REQUEST_METHOD'] == 'POST') { + $address_id = $_POST['address_id'] ?? null; + if ($address_id) { + users::setDefaultShipping($_SESSION['user_id'], $address_id); + $_SESSION['success'] = "Default shipping address set successfully!"; + } else { + $_SESSION['error'] = "Failed to set default shipping address."; + } + header('Location: /account/shipping'); + } + } + + public static function set_default_billing($defaults) + { + if ($_SERVER['REQUEST_METHOD'] == 'POST') { + $address_id = $_POST['address_id'] ?? null; + if ($address_id) { + users::setDefaultBilling($_SESSION['user_id'], $address_id); + $_SESSION['success'] = "Default billing address set successfully!"; + } else { + $_SESSION['error'] = "Failed to set default billing address."; + } + header('Location: /account/billing'); + } + } public static function signup($defaults) { @@ -333,7 +322,6 @@ class account $ship['state'], $ship['zip'], $ship['phone'], - $ship['hash'] ); $bill_id = $ship_id; if (! $useShipping) { @@ -347,7 +335,6 @@ class account $bill['state'], $bill['zip'], $bill['phone'], - $bill['hash'] ); } $opt_in_promotional = $_POST['opt_in_promotional'] ?? false; diff --git a/src/controllers/address.php b/src/controllers/address.php new file mode 100644 index 0000000..fd367ab --- /dev/null +++ b/src/controllers/address.php @@ -0,0 +1,73 @@ +render('lib/pages/index.twig', array_merge($defaults, [ + 'child_template' => 'address/edit.twig', + 'page_title' => 'Edit Address - ' . $_ENV['APP_NAME'], + 'address' => $address, + 'breadcrumbs' => [ + [ + 'url' => '/account', + 'title' => 'My Account', + ], + [ + 'url' => null, + 'title' => 'Edit Address', + ], + ], + ])); + } + + public static function delete($defaults, $address_id) + { + $address = addresses::getById($address_id); + if ($_SESSION['user_id'] != $address['user_id']) { + http_response_code(403); + exit; + } + if ($_SERVER['REQUEST_METHOD'] == 'POST') { + $user = users::getById($address['user_id']); + if ($address_id == $user['shipping_address_id'] || $address_id == $user['billing_address_id']) { + $_SESSION['error'] = "Cannot delete default address."; + header('Location: ' . $_GET['redirect']); + exit; + } + addresses::deleteById($address_id); + $_SESSION['success'] = "Address successfully deleted!"; + header('Location: ' . $_GET['redirect']); + exit; + } + } +} diff --git a/src/models/addresses.php b/src/models/addresses.php index 3f524a5..cefcd90 100644 --- a/src/models/addresses.php +++ b/src/models/addresses.php @@ -18,7 +18,6 @@ class addresses state TEXT NOT NULL, zip TEXT NOT NULL, phone TEXT, - hash TEXT NOT NULL, FOREIGN KEY (user_id) REFERENCES users(id) )"); } @@ -103,12 +102,6 @@ class addresses $_SESSION['error'] = $error_message; return ["error" => $error_message]; } - $result['hash'] = hash("sha256", json_encode($result)); - $existing = self::getByHash($result['hash']); - if ($existing) { - $_SESSION['error'] = "The address already exists."; - return ["error" => $_SESSION['error']]; - } return $result; } @@ -121,15 +114,6 @@ class addresses $stmt->execute(); } - public static function getByHash($hash) - { - $query = "SELECT * FROM addresses WHERE hash = :hash"; - $stmt = app::$db->prepare($query); - $stmt->bindParam(':hash', $hash); - $stmt->execute(); - return $stmt->fetch(\PDO::FETCH_ASSOC); - } - public static function getByUserId($user_id) { $query = "SELECT * FROM addresses WHERE user_id = :user_id"; @@ -139,7 +123,49 @@ class addresses return $stmt->fetchAll(\PDO::FETCH_ASSOC); } - public static function add($user_id, $name, $company, $addressLine1, $addressLine2, $city, $state, $zip, $phone, $hash) + public static function getById($id) + { + $query = "SELECT * FROM addresses WHERE id = :id"; + $stmt = app::$db->prepare($query); + $stmt->bindParam(':id', $id); + $stmt->execute(); + return $stmt->fetch(\PDO::FETCH_ASSOC); + } + public static function updateById($id, $name, $company, $addressLine1, $addressLine2, $city, $state, $zip, $phone) + { + $query = "UPDATE addresses SET + name = :name, + company = :company, + addressLine1 = :addressLine1, + addressLine2 = :addressLine2, + city = :city, + state = :state, + zip = :zip, + phone = :phone + WHERE id = :id"; + + $stmt = app::$db->prepare($query); + $stmt->bindParam(':id', $id); + $stmt->bindParam(':name', $name); + $stmt->bindParam(':company', $company); + $stmt->bindParam(':addressLine1', $addressLine1); + $stmt->bindParam(':addressLine2', $addressLine2); + $stmt->bindParam(':city', $city); + $stmt->bindParam(':state', $state); + $stmt->bindParam(':zip', $zip); + $stmt->bindParam(':phone', $phone); + $stmt->execute(); + } + + public static function deleteById($id) + { + $query = "DELETE FROM addresses WHERE id = :id"; + $stmt = app::$db->prepare($query); + $stmt->bindParam(':id', $id); + $stmt->execute(); + } + + public static function add($user_id, $name, $company, $addressLine1, $addressLine2, $city, $state, $zip, $phone) { $query = "INSERT INTO addresses ( user_id, @@ -150,8 +176,7 @@ class addresses city, state, zip, - phone, - hash + phone ) VALUES ( :user_id, :name, @@ -161,8 +186,7 @@ class addresses :city, :state, :zip, - :phone, - :hash + :phone )"; $stmt = app::$db->prepare($query); $stmt->bindParam(':user_id', $user_id); @@ -174,7 +198,6 @@ class addresses $stmt->bindParam(':state', $state); $stmt->bindParam(':zip', $zip); $stmt->bindParam(':phone', $phone); - $stmt->bindParam(':hash', $hash); $stmt->execute(); return app::$db->lastInsertId(); } diff --git a/src/models/users.php b/src/models/users.php index f40c247..f4750d1 100644 --- a/src/models/users.php +++ b/src/models/users.php @@ -30,6 +30,23 @@ class users created_at DATETIME DEFAULT CURRENT_TIMESTAMP )"); } + public static function setDefaultShipping($user_id, $shipping_address_id) + { + $query = "UPDATE users SET shipping_address_id = :shipping_address_id WHERE id = :user_id"; + $stmt = app::$db->prepare($query); + $stmt->bindParam(':shipping_address_id', $shipping_address_id, \PDO::PARAM_INT); + $stmt->bindParam(':user_id', $user_id, \PDO::PARAM_INT); + $stmt->execute(); + } + + public static function setDefaultBilling($user_id, $billing_address_id) + { + $query = "UPDATE users SET billing_address_id = :billing_address_id WHERE id = :user_id"; + $stmt = app::$db->prepare($query); + $stmt->bindParam(':billing_address_id', $billing_address_id, \PDO::PARAM_INT); + $stmt->bindParam(':user_id', $user_id, \PDO::PARAM_INT); + $stmt->execute(); + } public static function updateReplaceEmailTokenById($user_id, $replace_token) { diff --git a/src/views/account/address/confirm.twig b/src/views/account/address/confirm.twig deleted file mode 100644 index 763ec59..0000000 --- a/src/views/account/address/confirm.twig +++ /dev/null @@ -1 +0,0 @@ -confirm the address \ No newline at end of file diff --git a/src/views/account/address/edit.twig b/src/views/account/address/edit.twig deleted file mode 100644 index 08d43f1..0000000 --- a/src/views/account/address/edit.twig +++ /dev/null @@ -1 +0,0 @@ -edit the address \ No newline at end of file diff --git a/src/views/account/billing.twig b/src/views/account/billing.twig index e7c63d2..5270af5 100644 --- a/src/views/account/billing.twig +++ b/src/views/account/billing.twig @@ -1,5 +1,5 @@
- + {% include 'lib/alert.twig' %}

@@ -23,16 +23,17 @@ {% for address in addresses %} {% if address.id == user.billing_address_id %} {% include 'lib/address.twig' with { - address: address, - edit_url: '/account/billing/edit/' ~ address.id, - delete_url: '/account/billing/delete/' ~ address.id - } %} + address: address, + edit: true, + delete: true, + redirect: '/account/billing' + } %} {% endif %} {% endfor %}

{% include 'lib/forms/address.twig' with { - action: 'billing' + type: 'billing_' } %} {% include 'lib/button.twig' with { label: 'Add Address', @@ -44,11 +45,15 @@ text: 'OR' } %} {% for address in addresses %} - {% include 'lib/address.twig' with { - address: address, - edit_url: '/account/billing/edit/' ~ address.id, - delete_url: '/account/billing/delete/' ~ address.id - } %} + {% if address.id != user.shipping_address_id %} + {% include 'lib/address.twig' with { + address: address, + edit: true, + delete: true, + redirect: '/account/billing', + set_default: 'billing' + } %} + {% endif %} {% endfor %} {% endif %}
diff --git a/src/views/account/index.twig b/src/views/account/index.twig index 29a2ad7..0361b55 100644 --- a/src/views/account/index.twig +++ b/src/views/account/index.twig @@ -10,7 +10,8 @@ name: user.name, company_name: user.company_name, company_type: user.company_type, - company_size: user.company_size + company_size: user.company_size, + dark_theme: defaults.user_settings.dark_theme } %} {% include 'lib/button.twig' with { label: 'Save Profile', @@ -51,10 +52,8 @@ {% for address in addresses %} {% if address.id == user.shipping_address_id %} {% include 'lib/address.twig' with { - address: address, - edit_url: '/account/shipping/edit/' ~ address.id, - delete_url: '/account/shipping/delete/' ~ address.id - } %} + address: address, + } %} {% endif %} {% endfor %} @@ -71,10 +70,8 @@ {% for address in addresses %} {% if address.id == user.billing_address_id %} {% include 'lib/address.twig' with { - address: address, - edit_url: '/account/billing/edit/' ~ address.id, - delete_url: '/account/billing/delete/' ~ address.id - } %} + address: address, + } %} {% endif %} {% endfor %} @@ -138,7 +135,7 @@ {% include 'lib/inputs/toggle.twig' with { label: 'Recieve coupons & more', name: 'opt_in_promotional', - on: user_settings.opt_in_promotional + on: defailts.user_settings.opt_in_promotional } %} {% include 'lib/button.twig' with { label: 'Save', diff --git a/src/views/account/shipping.twig b/src/views/account/shipping.twig index e163a41..d31e88f 100644 --- a/src/views/account/shipping.twig +++ b/src/views/account/shipping.twig @@ -1,4 +1,5 @@
+ {% include 'lib/alert.twig' %}

@@ -13,15 +14,16 @@ {% if address.id == user.shipping_address_id %} {% include 'lib/address.twig' with { address: address, - edit_url: '/account/shipping/edit/' ~ address.id, - delete_url: '/account/shipping/delete/' ~ address.id + edit: true, + delete: true, + redirect: '/account/shipping' } %} {% endif %} {% endfor %}

{% include 'lib/forms/address.twig' with { - action: 'shipping' + type: 'shipping_' } %} {% include 'lib/button.twig' with { label: 'Add Address', @@ -33,12 +35,15 @@ text: 'OR' } %} {% for address in addresses %} - {% include 'lib/address.twig' with { + {% if address.id != user.shipping_address_id %} + {% include 'lib/address.twig' with { address: address, - edit_url: '/account/billing/edit/' ~ address.id, - delete_url: '/account/billing/delete/' ~ address.id + edit: true, + delete: true, + redirect: '/account/shipping', + set_default: 'shipping' } %} + {% endif %} {% endfor %} {% endif %} -
diff --git a/src/views/account/signup.twig b/src/views/account/signup.twig index a0d73ac..ec934e7 100644 --- a/src/views/account/signup.twig +++ b/src/views/account/signup.twig @@ -44,7 +44,7 @@ {% include 'lib/forms/address.twig' with { - action: 'shipping', + type: 'shipping_', name: session.last_post.shipping_name, addressLine1: session.last_post.shipping_addressLine1, company: session.last_post.shipping_company, @@ -73,23 +73,23 @@ {% include 'lib/rule.twig' with { text: 'ALL DONE!' } %} {% include 'lib/button.twig' with { - label: 'Register', - onclick: 'this.parentNode.submit()', - captcha: true - } %} + label: 'Register', + onclick: 'this.parentNode.submit()', + captcha: true + } %}