gitea init
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
|
||||
require_once __DIR__ . '/../../../../includes/init.php';
|
||||
require_once __DIR__ . '/../../models/invoicesmodel.php';
|
||||
require_once __DIR__ . '/../../../../../vendor/autoload.php';
|
||||
|
||||
|
||||
|
||||
InvoicesModel::setDb($pdo);
|
||||
|
||||
// ინვოისის ნომრის გენერაცია
|
||||
$generatedInvoiceNumber = InvoicesModel::generateNextInvoiceNumber();
|
||||
|
||||
// კლიენტების სია dropdown-სთვის
|
||||
$clients = InvoicesModel::getClientsList();
|
||||
|
||||
// პროდუქტების სია
|
||||
$products = InvoicesModel::getProductList();
|
||||
|
||||
require_once __DIR__ . '/../../views/invoices/create.php';
|
||||
|
||||
?>
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
// კონტროლერი: ინვოისის წაშლა
|
||||
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
|
||||
require_once __DIR__ . '/../../../../includes/init.php';
|
||||
require_once __DIR__ . '/../../models/invoicesmodel.php';
|
||||
require_once __DIR__ . '/../../../../../vendor/autoload.php';
|
||||
|
||||
use App\Config;
|
||||
|
||||
// ბაზასთან დაკავშირება
|
||||
InvoicesModel::setDb($pdo);
|
||||
|
||||
// GET ID გადამოწმება
|
||||
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
|
||||
exit('არასწორი ID.');
|
||||
}
|
||||
|
||||
$id = (int)$_GET['id'];
|
||||
|
||||
// წაშლა
|
||||
InvoicesModel::deleteInvoice($id);
|
||||
|
||||
// გადამისამართება ინვოისების სიაზე
|
||||
|
||||
require_once __DIR__ . '/../../views/invoices/list.php';
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
// 📄 რედაქტირება
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
|
||||
require_once __DIR__ . '/../../../../includes/init.php';
|
||||
require_once __DIR__ . '/../../models/invoicesmodel.php';
|
||||
require_once __DIR__ . '/../../../../../vendor/autoload.php';
|
||||
|
||||
InvoicesModel::setDb($pdo);
|
||||
|
||||
// ID შემოწმება
|
||||
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
|
||||
exit('არასწორი ID.');
|
||||
}
|
||||
|
||||
$id = (int)$_GET['id'];
|
||||
|
||||
// ინვოისის წამოღება
|
||||
$invoice = InvoicesModel::getInvoiceById($id);
|
||||
if (!$invoice) {
|
||||
exit('ინვოისი ვერ მოიძებნა.');
|
||||
}
|
||||
|
||||
// სელექტისთვის საჭირო მონაცემები
|
||||
$clients = InvoicesModel::getClientsList();
|
||||
$products = InvoicesModel::getProductList();
|
||||
|
||||
// ვიუ ჩატვირთვა
|
||||
require_once __DIR__ . '/../../views/invoices/edit.php';
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
// 📄 ინვოისის PDF გენერაცია - Controller
|
||||
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
|
||||
require_once __DIR__ . '/../../../../includes/init.php';
|
||||
require_once __DIR__ . '/../../models/invoicesmodel.php';
|
||||
require_once __DIR__ . '/../../../../vendor/autoload.php';
|
||||
|
||||
use Dompdf\Dompdf;
|
||||
use Dompdf\Options;
|
||||
use App\Config;
|
||||
|
||||
InvoicesModel::setDb($pdo);
|
||||
|
||||
// 🧾 ინვოისის ID გადმოსვლა GET-ით
|
||||
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
|
||||
exit('არასწორი ID');
|
||||
}
|
||||
|
||||
$id = (int)$_GET['id'];
|
||||
|
||||
// 🧾 ინვოისის მონაცემების წამოღება
|
||||
$invoice = InvoicesModel::getInvoiceById($id);
|
||||
if (!$invoice) {
|
||||
exit('ინვოისი ვერ მოიძებნა.');
|
||||
}
|
||||
|
||||
$client_name = $invoice['first_name'] . ' ' . $invoice['last_name'];
|
||||
|
||||
// 📄 PDF კონფიგურაცია
|
||||
$options = new Options();
|
||||
$options->set('isHtml5ParserEnabled', true);
|
||||
$options->set('isRemoteEnabled', true);
|
||||
|
||||
$dompdf = new Dompdf($options);
|
||||
|
||||
// 📄 შაბლონის HTML ჩატვირთვა
|
||||
ob_start();
|
||||
require_once realpath(Config::basePath() . '/admin/modules/billing/models/invoices/invoice_template.php');
|
||||
$html = ob_get_clean();
|
||||
|
||||
$dompdf->loadHtml($html, 'UTF-8');
|
||||
$dompdf->setPaper('A4', 'portrait');
|
||||
$dompdf->render();
|
||||
|
||||
// 📄 PDF-ის შენახვა დროებით
|
||||
$pdfOutput = $dompdf->output();
|
||||
$pdfPath = '/tmp/invoice_' . $invoice['id'] . '.pdf';
|
||||
file_put_contents($pdfPath, $pdfOutput);
|
||||
|
||||
// გადამისამართება ან ჩამოტვირთვა შეგიძლიათ დაამატოთ აქ
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
|
||||
require_once __DIR__ . '/../../../../includes/init.php';
|
||||
require_once __DIR__ . '/../../models/invoicesmodel.php';
|
||||
require_once __DIR__ . '/../../../../../vendor/autoload.php';
|
||||
|
||||
use App\Config;
|
||||
|
||||
// ვუკავშირებთ ბაზას მოდელს
|
||||
InvoicesModel::setDb($pdo);
|
||||
|
||||
// ინვოისების წამოღება
|
||||
$invoices = InvoicesModel::getAllInvoices();
|
||||
|
||||
// ვიუს ჩატვირთვა
|
||||
require_once __DIR__ . '/../../views/invoices/list.php';
|
||||
@@ -0,0 +1,104 @@
|
||||
<!-- გაგზავნის ლოგიკა -->
|
||||
<?php
|
||||
|
||||
// ინვოისის გაგზავნა ელფოსტაზე PDF-ით
|
||||
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
|
||||
require_once __DIR__ . '/../../../../includes/init.php';
|
||||
require_once __DIR__ . '/../../models/invoicesmodel.php';
|
||||
require_once __DIR__ . '/../../../../../vendor/autoload.php';
|
||||
require_once __DIR__ . '/../../../../libs/phpmailer/src/PHPMailer.php';
|
||||
require_once __DIR__ . '/../../../../libs/phpmailer/src/SMTP.php';
|
||||
require_once __DIR__ . '/../../../../libs/phpmailer/src/Exception.php';
|
||||
|
||||
use PHPMailer\PHPMailer\PHPMailer;
|
||||
use PHPMailer\PHPMailer\Exception;
|
||||
|
||||
InvoicesModel::setDb($pdo);
|
||||
|
||||
// use Dompdf\Dompdf;
|
||||
|
||||
// use Dompdf\Options;
|
||||
|
||||
// $options = new Options();
|
||||
// $options->set('isRemoteEnabled', true);
|
||||
|
||||
// // ️➡️ უთხარი Dompdf-ს სად არის შენი შრიფტი
|
||||
// $options->setChroot(__DIR__ . '/../../'); // აქედან იმუშავებს relative path-ებით
|
||||
// $options->set('defaultFont', 'bpg_glaho');
|
||||
|
||||
// $dompdf = new Dompdf($options);
|
||||
|
||||
|
||||
InvoicesModel::setDb($pdo);
|
||||
|
||||
// 1. აიდი
|
||||
$id = isset($_GET['id']) ? (int)$_GET['id'] : 0;
|
||||
if (!$id) {
|
||||
exit("არასწორი ინვოისის ID.");
|
||||
}
|
||||
|
||||
// 2. მოიტანე ინვოისი და ნივთები
|
||||
$invoice = InvoicesModel::getInvoiceWithItems($id);
|
||||
if (!$invoice) {
|
||||
exit("ინვოისი ვერ მოიძებნა.");
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 3. გენერაცია PDF ფაილის
|
||||
$pdfPath = InvoicesModel::generateInvoicePDF($invoice);
|
||||
|
||||
// Email გაგზავნა
|
||||
$mail = new PHPMailer(true);
|
||||
|
||||
try {
|
||||
$mail->isSMTP();
|
||||
$mail->Host = 'vps-7146dd3a.vps.ovh.ca'; // შეცვალე
|
||||
$mail->SMTPAuth = true;
|
||||
$mail->Username = 'noreply@selfhosting.ge'; // შეცვალე
|
||||
$mail->Password = 'FSZtTIIIlubk'; // შეცვალე
|
||||
$mail->SMTPSecure = 'ssl'; // ან ssl
|
||||
$mail->Port = 465; // ან 465
|
||||
|
||||
$mail->setFrom('noreply@selfhosting.ge', 'ბილინგ სერვისი');
|
||||
$mail->addAddress($invoice['email'], $invoice['first_name'] . ' ' . $invoice['last_name']);
|
||||
|
||||
$mail->CharSet = 'UTF-8'; // ✅ ეს არის მთავარი!
|
||||
$mail->Encoding = 'base64'; // ხშირად დაეხმარება UTF-8-ის სწორ გადაცემას
|
||||
|
||||
$mail->isHTML(true);
|
||||
$mail->Subject = "ინვოისი #" . $invoice['invoice_number'];
|
||||
$mail->Body = "
|
||||
გამარჯობა {$invoice['first_name']},<br><br>
|
||||
თქვენთვის შემუშავებულია ახალი ინვოისი ჯამური თანხით <strong>{$invoice['total_amount']} ₾</strong>.<br>
|
||||
გადახდის ვადა: {$invoice['due_date']}<br><br>
|
||||
იხილეთ დეტალურად: ინვოისის სანახავად იხილეთ მიმაგრებული ფაილი<br><br>
|
||||
მადლობა თანამშრომლობისთვის.
|
||||
";
|
||||
|
||||
$invoice = InvoicesModel::getInvoiceWithClientById($id);
|
||||
|
||||
$invoice['company_name'] = $invoice['client_company_name'];
|
||||
$invoice['vat_number'] = $invoice['client_vat_number'];
|
||||
$invoice['address1'] = $invoice['client_address1'];
|
||||
$client_name = $invoice['first_name'] . ' ' . $invoice['last_name'];
|
||||
// ინვოისის HTML
|
||||
$html = InvoicesModel::renderInvoiceHTML($invoice);
|
||||
|
||||
|
||||
$mail->addAttachment($pdfPath, 'Invoice_' . $invoice['invoice_number'] . '.pdf');
|
||||
$mail->send();
|
||||
|
||||
// ✅ წარმატებული გაგზავნის შემდეგ
|
||||
header("Location: dashboard.php?module=billing&submodule=invoices&action=view&id={$invoice['id']}&sent=1");
|
||||
exit;
|
||||
} catch (Exception $e) {
|
||||
echo "შეცდომა გაგზავნისას: {$mail->ErrorInfo}";
|
||||
}
|
||||
|
||||
|
||||
?>
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
// 📬 გადახდის შეხსენების გაგზავნა
|
||||
|
||||
ini_set('display_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
|
||||
require_once __DIR__ . '/../../../../includes/init.php';
|
||||
require_once __DIR__ . '/../../models/invoicesmodel.php';
|
||||
require '../../libs/phpmailer/src/PHPMailer.php';
|
||||
require '../../libs/phpmailer/src/SMTP.php';
|
||||
require '../../libs/phpmailer/src/Exception.php';
|
||||
|
||||
use PHPMailer\PHPMailer\PHPMailer;
|
||||
use PHPMailer\PHPMailer\Exception;
|
||||
|
||||
foreach ($invoices as $invoice) {
|
||||
$mail = new PHPMailer(true);
|
||||
|
||||
try {
|
||||
// SMTP პარამეტრები
|
||||
$mail->isSMTP();
|
||||
$mail->Host = 'vps-7146dd3a.vps.ovh.ca';
|
||||
$mail->SMTPAuth = true;
|
||||
$mail->Username = 'levan@arabuli.info';
|
||||
$mail->Password = 'Aqsxcs@1211';
|
||||
$mail->SMTPSecure = 'ssl';
|
||||
$mail->Port = 465;
|
||||
|
||||
$clientName = $invoice['first_name'] . ' ' . $invoice['last_name'];
|
||||
|
||||
$mail->setFrom('levan@arabuli.info', 'Billing System');
|
||||
$mail->addAddress($invoice['email'], $clientName);
|
||||
|
||||
$mail->CharSet = 'UTF-8';
|
||||
$mail->isHTML(true);
|
||||
$mail->Subject = "გადახდის შეხსენება ინვოისზე #{$invoice['invoice_number']}";
|
||||
$mail->Body = "
|
||||
გამარჯობა {$clientName},<br><br>
|
||||
გთხოვთ გადაიხადოთ ინვოისი #{$invoice['invoice_number']} <strong>{$invoice['total_amount']} ₾</strong><br>
|
||||
გადახდის ბოლო ვადაა: <strong>{$invoice['due_date']}</strong><br><br>
|
||||
იხილეთ ინვოისი: <a href='https://yourdomain.com/dashboard.php?module=billing&submodule=invoices&action=view&id={$invoice['id']}'>იხილეთ ინვოისი</a>
|
||||
";
|
||||
|
||||
$mail->send();
|
||||
echo "✅ შეხსენება გაიგზავნა {$clientName} ({$invoice['email']})<br>";
|
||||
} catch (Exception $e) {
|
||||
echo "❌ შეცდომა: {$mail->ErrorInfo}<br>";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
|
||||
require_once __DIR__ . '/../../../../includes/init.php';
|
||||
require_once __DIR__ . '/../../models/invoicesmodel.php';
|
||||
|
||||
InvoicesModel::setDb($pdo);
|
||||
|
||||
// POST მონაცემების დამუშავება
|
||||
$data = $_POST;
|
||||
$data['recurring'] = isset($data['recurring']) ? 1 : 0;
|
||||
|
||||
// სტატუსის ვალიდაცია
|
||||
if (!InvoicesModel::isValidStatus($data['status'])) {
|
||||
die('არასწორი სტატუსის მნიშვნელობა');
|
||||
}
|
||||
|
||||
// 🔢 ინვოისის ნომრის გენერაცია
|
||||
if (empty($data['invoice_number'])) {
|
||||
$data['invoice_number'] = InvoicesModel::generateInvoiceNumber();
|
||||
}
|
||||
|
||||
|
||||
// დუბლიკატის შემოწმება
|
||||
if (InvoicesModel::isDuplicateInvoiceNumber($data['invoice_number'])) {
|
||||
die('ინვოისის ნომერი უკვე გამოიყენება!');
|
||||
}
|
||||
|
||||
// ჩასმა მოდელის მეშვეობით
|
||||
$invoice_id = InvoicesModel::createInvoiceWithItems($data);
|
||||
|
||||
// გადამისამართება
|
||||
header("Location: dashboard.php?module=billing&submodule=invoices&action=view&id=" . $invoice_id);
|
||||
exit;
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
// ⚙️ ინვოისის განახლება
|
||||
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
|
||||
require_once __DIR__ . '/../../../../includes/init.php';
|
||||
require_once __DIR__ . '/../../models/invoicesmodel.php';
|
||||
require_once __DIR__ . '/../../../../../vendor/autoload.php';
|
||||
|
||||
InvoicesModel::setDb($pdo);
|
||||
|
||||
// 📨 მონაცემების მიღება
|
||||
if (!isset($_POST['id']) || !is_numeric($_POST['id'])) {
|
||||
exit('არასწორი ID.');
|
||||
}
|
||||
|
||||
$id = (int)$_POST['id'];
|
||||
$data = $_POST;
|
||||
|
||||
// ინვოისის განახლება
|
||||
InvoicesModel::updateInvoice($id, $data);
|
||||
|
||||
// ↪️ გადამისამართება
|
||||
header("Location: dashboard.php?module=billing&submodule=invoices&action=view&id=$id");
|
||||
exit;
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
|
||||
require_once __DIR__ . '/../../../../includes/init.php';
|
||||
require_once __DIR__ . '/../../models/invoicesmodel.php';
|
||||
require_once __DIR__ . '/../../../../../vendor/autoload.php';
|
||||
|
||||
use App\Config;
|
||||
|
||||
// ბაზასთან დაკავშირება
|
||||
InvoicesModel::setDb($pdo);
|
||||
|
||||
// ID შემოწმება
|
||||
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
|
||||
echo "არასწორი ID.";
|
||||
exit;
|
||||
}
|
||||
|
||||
$id = (int) $_GET['id'];
|
||||
$showAlert = isset($_GET['sent']) && $_GET['sent'] == 1;
|
||||
|
||||
// ინვოისის წამოღება
|
||||
$invoice = InvoicesModel::getInvoiceWithClient($id);
|
||||
|
||||
if (!$invoice) {
|
||||
echo "ინვოისი ვერ მოიძებნა.";
|
||||
exit;
|
||||
}
|
||||
|
||||
// პროდუქტის items
|
||||
$productItems = InvoicesModel::getInvoiceItems($id);
|
||||
|
||||
// ვიუ ფაილის ჩატვირთვა
|
||||
require_once __DIR__ . '/../../views/invoices/view.php';
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
|
||||
require_once __DIR__ . '/../../../../includes/init.php';
|
||||
require_once __DIR__ . '/../../models/transactionsmodel.php';
|
||||
require_once __DIR__ . '/../../../../../vendor/autoload.php';
|
||||
|
||||
|
||||
TransactionsModel::setDb($pdo);
|
||||
|
||||
// მომხმარებლები და ინვოისები ფორმის select-ებისთვის
|
||||
$clients = TransactionsModel::getClients();
|
||||
$invoices = TransactionsModel::getInvoices();
|
||||
|
||||
// POST დამუშავება
|
||||
$errors = TransactionsModel::handleTransactionFormSubmission();
|
||||
|
||||
|
||||
require_once __DIR__ . '/../../views/transactions/create.php';
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
|
||||
require_once __DIR__ . '/../../../../includes/init.php';
|
||||
require_once __DIR__ . '/../../models/transactionsmodel.php';
|
||||
require_once __DIR__ . '/../../../../../vendor/autoload.php';
|
||||
|
||||
TransactionsModel::setDb($pdo);
|
||||
|
||||
// ID გადმოსვლის შემოწმება
|
||||
$id = $_GET['id'] ?? null;
|
||||
|
||||
if (!$id || !is_numeric($id)) {
|
||||
exit('არასწორი ID.');
|
||||
}
|
||||
|
||||
// წაშლის მცდელობა
|
||||
TransactionsModel::deleteTransaction((int)$id);
|
||||
|
||||
// გადამისამართება
|
||||
header('Location: dashboard.php?module=billing&submodule=transactions&action=list&deleted=1');
|
||||
exit;
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
|
||||
require_once __DIR__ . '/../../../../includes/init.php';
|
||||
require_once __DIR__ . '/../../models/transactionsmodel.php';
|
||||
require_once __DIR__ . '/../../../../../vendor/autoload.php';
|
||||
|
||||
|
||||
TransactionsModel::setDb($pdo);
|
||||
|
||||
$id = $_GET['id'] ?? null;
|
||||
|
||||
$transaction = TransactionsModel::getTransactionById($id);
|
||||
|
||||
if (!$transaction) {
|
||||
echo "ტრანზაქცია ვერ მოიძებნა.";
|
||||
exit;
|
||||
}
|
||||
|
||||
// შენახვა
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$data = [
|
||||
'status' => $_POST['status'],
|
||||
'method' => $_POST['method'],
|
||||
'notes' => $_POST['notes'],
|
||||
];
|
||||
|
||||
TransactionsModel::updateTransaction($id, $data);
|
||||
|
||||
header("Location: dashboard.php?module=billing&submodule=transactions&action=list&updated=1");
|
||||
exit;
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/../../views/transactions/edit.php';
|
||||
@@ -0,0 +1,25 @@
|
||||
<!-- გადახდების ისტორია -->
|
||||
|
||||
<?php
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
|
||||
require_once __DIR__ . '/../../../../includes/init.php';
|
||||
require_once __DIR__ . '/../../models/transactionsmodel.php';
|
||||
require_once __DIR__ . '/../../../../../vendor/autoload.php';
|
||||
|
||||
|
||||
TransactionsModel::setDb($pdo);
|
||||
|
||||
$clientId = $_GET['client_id'] ?? null;
|
||||
|
||||
$client = TransactionsModel::getClientInfo($clientId);
|
||||
if (!$client) {
|
||||
echo "კლიენტი ვერ მოიძებნა.";
|
||||
exit;
|
||||
}
|
||||
|
||||
$transactions = TransactionsModel::getClientTransactions($clientId);
|
||||
|
||||
require_once __DIR__ . '/../../views/transactions/history.php';
|
||||
@@ -0,0 +1,29 @@
|
||||
<!-- ტრანზაქციების სია -->
|
||||
|
||||
<?php
|
||||
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
|
||||
require_once __DIR__ . '/../../../../includes/init.php';
|
||||
require_once __DIR__ . '/../../models/transactionsmodel.php';
|
||||
require_once __DIR__ . '/../../../../../vendor/autoload.php';
|
||||
|
||||
|
||||
// წაკითხვა GET-პარამეტრებიდან
|
||||
$successMessage = null;
|
||||
|
||||
if (isset($_GET['added']) && $_GET['added'] == 1) {
|
||||
$successMessage = "ტრანზაქცია წარმატებით დაემატა.";
|
||||
} elseif (isset($_GET['updated']) && $_GET['updated'] == 1) {
|
||||
$successMessage = "ტრანზაქცია წარმატებით განახლდა.";
|
||||
} elseif (isset($_GET['deleted']) && $_GET['deleted'] == 1) {
|
||||
$successMessage = "ტრანზაქცია წარმატებით წაიშალა.";
|
||||
}
|
||||
|
||||
TransactionsModel::setDb($pdo);
|
||||
|
||||
$transactions = TransactionsModel::getAllTransactions();
|
||||
|
||||
require_once __DIR__ . '/../../views/transactions/list.php';
|
||||
@@ -0,0 +1 @@
|
||||
<!-- გენერირება, გამოთვლები -->
|
||||
@@ -0,0 +1,2 @@
|
||||
<!-- გენერირება, გამოთვლები -->
|
||||
|
||||
@@ -0,0 +1,323 @@
|
||||
<?php
|
||||
|
||||
class InvoicesModel
|
||||
{
|
||||
protected static $db;
|
||||
|
||||
public static function setDb($pdo)
|
||||
{
|
||||
self::$db = $pdo;
|
||||
}
|
||||
|
||||
// ყველა ინვოისის წამოღება
|
||||
public static function getAllInvoicesWithClientNames()
|
||||
{
|
||||
$stmt = self::$db->query("
|
||||
SELECT i.*, CONCAT(c.first_name, ' ', c.last_name) AS client_name
|
||||
FROM invoices i
|
||||
JOIN clients c ON i.client_id = c.id
|
||||
ORDER BY i.id DESC
|
||||
");
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
|
||||
// ყველა ინვოისის წამოღება
|
||||
public static function getAllInvoices()
|
||||
{
|
||||
$stmt = self::$db->query("SELECT i.*, c.first_name, c.last_name FROM invoices i JOIN clients c ON i.client_id = c.id ORDER BY i.id DESC");
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
|
||||
// ერთი ინვოისის წამოღება დეტალურად
|
||||
public static function getInvoiceById($id)
|
||||
{
|
||||
$stmt = self::$db->prepare("SELECT * FROM invoices WHERE id = ?");
|
||||
$stmt->execute([$id]);
|
||||
return $stmt->fetch();
|
||||
}
|
||||
|
||||
// ინვოისის დამატება
|
||||
public static function createInvoice($data)
|
||||
{
|
||||
$stmt = self::$db->prepare("INSERT INTO invoices (invoice_number, client_id, description, payment_method, status, total_amount, is_recurring, issue_date, due_date, payment_date, recurring) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
|
||||
|
||||
$stmt->execute([
|
||||
$data['invoice_number'],
|
||||
$data['client_id'],
|
||||
$data['description'],
|
||||
$data['payment_method'],
|
||||
$data['status'],
|
||||
$data['total_amount'],
|
||||
$data['is_recurring'],
|
||||
$data['issue_date'],
|
||||
$data['due_date'],
|
||||
$data['payment_date'],
|
||||
$data['recurring']
|
||||
]);
|
||||
|
||||
return self::$db->lastInsertId();
|
||||
}
|
||||
|
||||
// ინვოისის განახლება
|
||||
public static function updateInvoice($id, $data)
|
||||
{
|
||||
$stmt = self::$db->prepare("UPDATE invoices SET
|
||||
client_id = ?, invoice_number = ?, description = ?, status = ?,
|
||||
total_amount = ?, payment_method = ?, issue_date = ?, due_date = ?, recurring = ?
|
||||
WHERE id = ?");
|
||||
|
||||
return $stmt->execute([
|
||||
$data['client_id'],
|
||||
$data['invoice_number'],
|
||||
$data['description'],
|
||||
$data['status'],
|
||||
$data['total_amount'],
|
||||
$data['payment_method'],
|
||||
$data['issue_date'],
|
||||
$data['due_date'],
|
||||
isset($data['recurring']) ? 1 : 0,
|
||||
$id
|
||||
]);
|
||||
}
|
||||
|
||||
// ინვოისის წაშლა
|
||||
public static function deleteInvoice($id)
|
||||
{
|
||||
$stmt = self::$db->prepare("DELETE FROM invoices WHERE id = ?");
|
||||
return $stmt->execute([$id]);
|
||||
}
|
||||
|
||||
// ინვოისთან დაკავშირებული ნივთების წამოღება
|
||||
public static function getInvoiceItems2($invoice_id)
|
||||
{
|
||||
$stmt = self::$db->prepare("SELECT * FROM invoice_items WHERE invoice_id = ?");
|
||||
$stmt->execute([$invoice_id]);
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
|
||||
// ინვოისის ნივთების დამატება
|
||||
public static function addInvoiceItem($invoice_id, $item)
|
||||
{
|
||||
$stmt = self::$db->prepare("INSERT INTO invoice_items (invoice_id, product_id, description, amount) VALUES (?, ?, ?, ?)");
|
||||
return $stmt->execute([
|
||||
$invoice_id,
|
||||
$item['product_id'],
|
||||
$item['description'],
|
||||
$item['amount']
|
||||
]);
|
||||
}
|
||||
|
||||
// ინვოისის ნივთების წაშლა
|
||||
public static function deleteInvoiceItems($invoice_id)
|
||||
{
|
||||
$stmt = self::$db->prepare("DELETE FROM invoice_items WHERE invoice_id = ?");
|
||||
return $stmt->execute([$invoice_id]);
|
||||
}
|
||||
|
||||
public static function getInvoiceWithClient($id)
|
||||
{
|
||||
$stmt = self::$db->prepare("
|
||||
SELECT i.*, c.first_name, c.last_name, c.email
|
||||
FROM invoices i
|
||||
JOIN clients c ON c.id = i.client_id
|
||||
WHERE i.id = ?
|
||||
");
|
||||
$stmt->execute([$id]);
|
||||
return $stmt->fetch();
|
||||
}
|
||||
|
||||
public static function getInvoiceItems($invoiceId)
|
||||
{
|
||||
$stmt = self::$db->prepare("
|
||||
SELECT ii.*, p.name
|
||||
FROM invoice_items ii
|
||||
JOIN products p ON p.id = ii.product_id
|
||||
WHERE ii.invoice_id = ?
|
||||
");
|
||||
$stmt->execute([$invoiceId]);
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
|
||||
public static function generateNextInvoiceNumber(): string
|
||||
{
|
||||
$currentYear = date('Y');
|
||||
$prefix = 'INV-' . $currentYear . '-';
|
||||
|
||||
$stmt = self::$db->prepare("SELECT invoice_number FROM invoices WHERE invoice_number LIKE ? ORDER BY invoice_number DESC LIMIT 1");
|
||||
$stmt->execute([$prefix . '%']);
|
||||
$lastInvoice = $stmt->fetchColumn();
|
||||
|
||||
if ($lastInvoice) {
|
||||
$lastNumber = (int) substr($lastInvoice, strrpos($lastInvoice, '-') + 1);
|
||||
$newNumber = str_pad($lastNumber + 1, 3, '0', STR_PAD_LEFT);
|
||||
} else {
|
||||
$newNumber = '001';
|
||||
}
|
||||
|
||||
return $prefix . $newNumber;
|
||||
}
|
||||
|
||||
public static function getClientsList(): array
|
||||
{
|
||||
$stmt = self::$db->query("SELECT id, first_name, last_name FROM clients ORDER BY first_name");
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
|
||||
public static function getProductList(): array
|
||||
{
|
||||
$stmt = self::$db->query("SELECT id, name, price FROM products ORDER BY name");
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
|
||||
public static function createInvoiceWithItems($data)
|
||||
{
|
||||
$stmt = self::$db->prepare("INSERT INTO invoices
|
||||
(client_id, invoice_number, description, total_amount, payment_method, status, issue_date, due_date, recurring)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)");
|
||||
|
||||
$stmt->execute([
|
||||
$data['client_id'],
|
||||
$data['invoice_number'],
|
||||
$data['description'],
|
||||
$data['total_amount'],
|
||||
$data['payment_method'],
|
||||
$data['status'],
|
||||
$data['issue_date'],
|
||||
$data['due_date'],
|
||||
$data['recurring']
|
||||
]);
|
||||
|
||||
$invoice_id = self::$db->lastInsertId();
|
||||
|
||||
$stmtItem = self::$db->prepare("INSERT INTO invoice_items (invoice_id, product_id, amount, description) VALUES (?, ?, ?, ?)");
|
||||
|
||||
for ($i = 0; $i < count($data['products']); $i++) {
|
||||
$stmtItem->execute([
|
||||
$invoice_id,
|
||||
$data['products'][$i],
|
||||
$data['amounts'][$i],
|
||||
$data['descriptions'][$i]
|
||||
]);
|
||||
}
|
||||
|
||||
return $invoice_id;
|
||||
}
|
||||
|
||||
public static function generateInvoiceNumber(): string
|
||||
{
|
||||
$year = date('Y');
|
||||
$prefix = 'INV-' . $year . '-';
|
||||
|
||||
$stmt = self::$db->prepare("SELECT invoice_number FROM invoices WHERE invoice_number LIKE ? ORDER BY invoice_number DESC LIMIT 1");
|
||||
$stmt->execute([$prefix . '%']);
|
||||
$last = $stmt->fetchColumn();
|
||||
|
||||
$lastNumber = $last ? (int) substr($last, strrpos($last, '-') + 1) : 0;
|
||||
$newNumber = str_pad($lastNumber + 1, 3, '0', STR_PAD_LEFT);
|
||||
|
||||
return $prefix . $newNumber;
|
||||
}
|
||||
|
||||
|
||||
public static function isValidStatus(string $status): bool
|
||||
{
|
||||
$allowed = ['დრაფტი', 'გადაუხდელი', 'გადასახდელი', 'გადახდილი', 'გაუქმებული'];
|
||||
return in_array($status, $allowed);
|
||||
}
|
||||
|
||||
|
||||
public static function isDuplicateInvoiceNumber(string $invoice_number): bool
|
||||
{
|
||||
$stmt = self::$db->prepare("SELECT COUNT(*) FROM invoices WHERE invoice_number = ?");
|
||||
$stmt->execute([$invoice_number]);
|
||||
return $stmt->fetchColumn() > 0;
|
||||
}
|
||||
|
||||
public static function getInvoicesDueTomorrow()
|
||||
{
|
||||
$tomorrow = date('Y-m-d', strtotime('+1 day'));
|
||||
$stmt = self::$db->prepare("
|
||||
SELECT invoices.*, clients.first_name, clients.last_name, clients.email
|
||||
FROM invoices
|
||||
JOIN clients ON invoices.client_id = clients.id
|
||||
WHERE invoices.due_date = ? AND invoices.status IN ('გადაუხდელი', 'გადასახდელი')
|
||||
");
|
||||
$stmt->execute([$tomorrow]);
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
|
||||
public static function getInvoiceWithItems($id)
|
||||
{
|
||||
$stmt = self::$db->prepare("
|
||||
SELECT invoices.*, clients.email, clients.first_name, clients.last_name
|
||||
FROM invoices
|
||||
JOIN clients ON invoices.client_id = clients.id
|
||||
WHERE invoices.id = ?
|
||||
");
|
||||
$stmt->execute([$id]);
|
||||
$invoice = $stmt->fetch();
|
||||
|
||||
if (!$invoice) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$stmtItems = self::$db->prepare("
|
||||
SELECT products.name, ii.description, ii.amount
|
||||
FROM invoice_items ii
|
||||
JOIN products ON products.id = ii.product_id
|
||||
WHERE ii.invoice_id = ?
|
||||
");
|
||||
$stmtItems->execute([$id]);
|
||||
$invoice['items'] = $stmtItems->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
return $invoice;
|
||||
}
|
||||
|
||||
|
||||
public static function generateInvoicePDF($invoice)
|
||||
{
|
||||
$html = self::renderInvoiceHTML($invoice);
|
||||
|
||||
$options = new \Dompdf\Options();
|
||||
$options->set('isRemoteEnabled', true);
|
||||
$options->set('defaultFont', 'DejaVu Sans');
|
||||
|
||||
$dompdf = new \Dompdf\Dompdf($options);
|
||||
$dompdf->loadHtml($html, 'UTF-8');
|
||||
$dompdf->setPaper('A4', 'portrait');
|
||||
$dompdf->render();
|
||||
|
||||
$pdfPath = '/tmp/invoice_' . $invoice['id'] . '.pdf';
|
||||
file_put_contents($pdfPath, $dompdf->output());
|
||||
|
||||
return $pdfPath;
|
||||
}
|
||||
|
||||
public static function renderInvoiceHTML($invoice)
|
||||
{
|
||||
ob_start();
|
||||
include __DIR__ . '/../views/invoices/pdf_template.php'; // თუ გინდა გამოყავი ცალკე template ფაილად
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
// მოაქვს პდფ ფაილში კლიენტის ბაზიდან დამატებითი მონაცემები
|
||||
public static function getInvoiceWithClientById($id)
|
||||
{
|
||||
$stmt = self::$db->prepare("
|
||||
SELECT
|
||||
invoices.*,
|
||||
clients.company_name,
|
||||
clients.vat_number,
|
||||
clients.address1,
|
||||
clients.first_name AS client_first_name,
|
||||
clients.last_name AS client_last_name
|
||||
FROM invoices
|
||||
LEFT JOIN clients ON invoices.client_id = clients.id
|
||||
WHERE invoices.id = ?
|
||||
");
|
||||
$stmt->execute([$id]);
|
||||
return $stmt->fetch();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
<?php
|
||||
class TransactionsModel
|
||||
{
|
||||
private static $db;
|
||||
|
||||
public static function setDb($pdo)
|
||||
{
|
||||
self::$db = $pdo;
|
||||
}
|
||||
|
||||
public static function getClients()
|
||||
{
|
||||
$stmt = self::$db->query("SELECT id, first_name, last_name FROM clients ORDER BY first_name ASC");
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
|
||||
public static function getInvoices()
|
||||
{
|
||||
$stmt = self::$db->query("SELECT id, invoice_number FROM invoices ORDER BY invoice_number ASC");
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
|
||||
public static function handleTransactionFormSubmission()
|
||||
{
|
||||
$errors = [];
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$invoice_id = $_POST['invoice_id'] ?? '';
|
||||
$client_id = $_POST['client_id'] ?? '';
|
||||
$amount = $_POST['amount'] ?? '';
|
||||
$method = $_POST['method'] ?? '';
|
||||
$status = $_POST['status'] ?? '';
|
||||
$notes = $_POST['notes'] ?? '';
|
||||
|
||||
if (!$invoice_id || !$client_id || !$amount || !$method || !$status) {
|
||||
$errors[] = "გთხოვ შეავსო ყველა სავალდებულო ველი.";
|
||||
}
|
||||
|
||||
if (empty($errors)) {
|
||||
$stmt = self::$db->prepare("INSERT INTO transactions (invoice_id, client_id, amount, method, status, notes) VALUES (?, ?, ?, ?, ?, ?)");
|
||||
$stmt->execute([$invoice_id, $client_id, $amount, $method, $status, $notes]);
|
||||
|
||||
header("Location: dashboard.php?module=billing&submodule=transactions&action=list&added=1");
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
public static function getTransactionById($id)
|
||||
{
|
||||
if (!$id || !is_numeric($id)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$stmt = self::$db->prepare("
|
||||
SELECT t.*, c.first_name, c.last_name, i.invoice_number
|
||||
FROM transactions t
|
||||
JOIN clients c ON c.id = t.client_id
|
||||
JOIN invoices i ON i.id = t.invoice_id
|
||||
WHERE t.id = ?
|
||||
");
|
||||
$stmt->execute([$id]);
|
||||
return $stmt->fetch();
|
||||
}
|
||||
|
||||
public static function updateTransaction($id, $data)
|
||||
{
|
||||
$stmt = self::$db->prepare("
|
||||
UPDATE transactions
|
||||
SET status = ?, method = ?, notes = ?
|
||||
WHERE id = ?
|
||||
");
|
||||
return $stmt->execute([
|
||||
$data['status'],
|
||||
trim($data['method']),
|
||||
trim($data['notes']),
|
||||
$id
|
||||
]);
|
||||
}
|
||||
|
||||
public static function getClientInfo($clientId)
|
||||
{
|
||||
if (!$clientId || !is_numeric($clientId)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$stmt = self::$db->prepare("SELECT first_name, last_name FROM clients WHERE id = ?");
|
||||
$stmt->execute([$clientId]);
|
||||
return $stmt->fetch();
|
||||
}
|
||||
|
||||
public static function getClientTransactions($clientId)
|
||||
{
|
||||
if (!$clientId || !is_numeric($clientId)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$stmt = self::$db->prepare("
|
||||
SELECT t.*, i.invoice_number
|
||||
FROM transactions t
|
||||
JOIN invoices i ON i.id = t.invoice_id
|
||||
WHERE t.client_id = ?
|
||||
ORDER BY t.created_at DESC
|
||||
");
|
||||
$stmt->execute([$clientId]);
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
|
||||
public static function getAllTransactions()
|
||||
{
|
||||
$stmt = self::$db->query("
|
||||
SELECT t.*, c.first_name, c.last_name, i.invoice_number
|
||||
FROM transactions t
|
||||
JOIN clients c ON t.client_id = c.id
|
||||
JOIN invoices i ON t.invoice_id = i.id
|
||||
ORDER BY t.created_at DESC
|
||||
");
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
|
||||
public static function deleteTransaction($id)
|
||||
{
|
||||
$stmt = self::$db->prepare("DELETE FROM transactions WHERE id = ?");
|
||||
return $stmt->execute([$id]);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../../models/invoicesmodel.php';
|
||||
|
||||
use App\Config;
|
||||
|
||||
require_once Config::includePath('head.php');
|
||||
require_once Config::includePath('navbar.php');
|
||||
require_once Config::includePath('pageheader.php');
|
||||
require_once Config::includePath('pagebodystart.php');
|
||||
?>
|
||||
|
||||
|
||||
<!-- CONTENT START -->
|
||||
|
||||
<div class="container-xl mt-4">
|
||||
<h2 class="mb-4">ახალი ინვოისის დამატება</h2>
|
||||
|
||||
<form action="dashboard.php?module=billing&submodule=invoices&action=store" method="POST">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">კლიენტი</label>
|
||||
<select name="client_id" class="form-select" required>
|
||||
<option value="">აირჩიე კლიენტი</option>
|
||||
<?php foreach ($clients as $client): ?>
|
||||
<option value="<?= $client['id'] ?>">
|
||||
<?= htmlspecialchars($client['first_name'] . ' ' . $client['last_name']) ?>
|
||||
</option>
|
||||
<?php endforeach ?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="form-label">პროდუქტები</label>
|
||||
|
||||
<div id="product-container">
|
||||
<div class="row product-item mb-2">
|
||||
<div class="col-md-5">
|
||||
<select name="products[]" class="form-select" required>
|
||||
<option value="">აირჩიე პროდუქტი</option>
|
||||
<?php foreach ($products as $product): ?>
|
||||
<option value="<?= $product['id'] ?>">
|
||||
<?= htmlspecialchars($product['name']) ?> (<?= $product['price'] ?> ₾)
|
||||
</option>
|
||||
<?php endforeach ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<input type="number" name="amounts[]" step="0.01" class="form-control" placeholder="თანხა ₾" required>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<input type="text" name="descriptions[]" class="form-control" placeholder="აღწერა (არასავალდებულო)">
|
||||
</div>
|
||||
<div class="col-md-1">
|
||||
<button type="button" class="btn btn-danger btn-remove">-</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="button" id="add-product" class="btn btn-secondary btn-sm mt-2">+ პროდუქტის დამატება</button>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">ინვოისის ნომერი (არასავალდებულო)</label>
|
||||
<input type="text" name="invoice_number" class="form-control"
|
||||
placeholder="მაგ: INV-2025-001"
|
||||
value="<?= htmlspecialchars($generatedInvoiceNumber) ?>" />
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">ინვოისის აღწერა</label>
|
||||
<textarea name="description" class="form-control" rows="3"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">გადასახდელი თანხა (₾)</label>
|
||||
<input type="number" step="0.01" name="total_amount" class="form-control" required>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">გადახდის მეთოდი</label>
|
||||
<input type="text" name="payment_method" class="form-control" value="საბანკო გადმორიცხვა" required>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">სტატუსი</label>
|
||||
<select name="status" class="form-select" required>
|
||||
<option value="დრაფტი">დრაფტი</option>
|
||||
<option value="გადაუხდელი">გადაუხდელი</option>
|
||||
<option value="გადასახდელი">გადასახდელი</option>
|
||||
<option value="გადახდილი">გადახდილი</option>
|
||||
<option value="გაუქმებული">გაუქმებული</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="mb-3 row">
|
||||
<div class="col">
|
||||
<label class="form-label">ინვოისის თარიღი</label>
|
||||
<input type="date" name="issue_date" class="form-control" value="<?= date('Y-m-d') ?>" required>
|
||||
</div>
|
||||
<div class="col">
|
||||
<label class="form-label">გადახდის ბოლო ვადა</label>
|
||||
<input type="date" name="due_date" class="form-control" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-check">
|
||||
<input type="checkbox" name="recurring" value="1" class="form-check-input">
|
||||
<span class="form-check-label">გადახდა განმეორებით (ყოველთვიურად)</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
<button type="submit" class="btn btn-primary">ინვოისის შექმნა</button>
|
||||
<a href="list.php" class="btn btn-secondary">გაუქმება</a>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div></div>
|
||||
|
||||
|
||||
<?php require_once Config::includePath('footer.php'); ?>
|
||||
|
||||
<script>
|
||||
document.getElementById('add-product').addEventListener('click', function () {
|
||||
const container = document.getElementById('product-container');
|
||||
const item = container.querySelector('.product-item');
|
||||
const clone = item.cloneNode(true);
|
||||
|
||||
// reset values
|
||||
clone.querySelectorAll('input, select').forEach(el => el.value = '');
|
||||
container.appendChild(clone);
|
||||
});
|
||||
|
||||
// remove button
|
||||
document.addEventListener('click', function (e) {
|
||||
if (e.target.classList.contains('btn-remove')) {
|
||||
const allItems = document.querySelectorAll('.product-item');
|
||||
if (allItems.length > 1) {
|
||||
e.target.closest('.product-item').remove();
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . '/../../models/invoicesmodel.php';
|
||||
|
||||
|
||||
use App\Config;
|
||||
|
||||
require_once Config::includePath('head.php');
|
||||
require_once Config::includePath('navbar.php');
|
||||
require_once Config::includePath('pageheader.php');
|
||||
require_once Config::includePath('pagebodystart.php');
|
||||
|
||||
?>
|
||||
|
||||
<!-- CONTENT START -->
|
||||
<div class="page-wrapper">
|
||||
<div class="container-xl mt-4">
|
||||
<h2 class="mb-4">ინვოისის რედაქტირება</h2>
|
||||
|
||||
<form action="dashboard.php?module=billing&submodule=invoices&action=update" method="POST">
|
||||
<input type="hidden" name="id" value="<?= $invoice['id'] ?>">
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">კლიენტი</label>
|
||||
<select name="client_id" class="form-select" required>
|
||||
<?php foreach ($clients as $client): ?>
|
||||
<option value="<?= $client['id'] ?>" <?= $invoice['client_id'] == $client['id'] ? 'selected' : '' ?>>
|
||||
<?= htmlspecialchars($client['first_name'] . ' ' . $client['last_name']) ?>
|
||||
</option>
|
||||
<?php endforeach ?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">ინვოისის ნომერი</label>
|
||||
<input type="text" name="invoice_number" class="form-control" value="<?= htmlspecialchars($invoice['invoice_number']) ?>" required>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">აღწერა</label>
|
||||
<textarea name="description" class="form-control"><?= htmlspecialchars($invoice['description']) ?></textarea>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">გადახდის მეთოდი</label>
|
||||
<input type="text" name="payment_method" class="form-control" value="<?= htmlspecialchars($invoice['payment_method']) ?>" required>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">სტატუსი</label>
|
||||
<select name="status" class="form-select">
|
||||
<?php foreach (['დრაფტი', 'გადაუხდელი', 'გადასახდელი', 'გადახდილი', 'გაუქმებული'] as $status): ?>
|
||||
<option value="<?= $status ?>" <?= $invoice['status'] == $status ? 'selected' : '' ?>><?= $status ?></option>
|
||||
<?php endforeach ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">თანხა (₾)</label>
|
||||
<input type="number" name="total_amount" class="form-control" step="0.01" value="<?= $invoice['total_amount'] ?>" required>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">ინვოისის თარიღი</label>
|
||||
<input type="date" name="issue_date" class="form-control" value="<?= $invoice['issue_date'] ?>" required>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">გადახდის ბოლო ვადა</label>
|
||||
<input type="date" name="due_date" class="form-control" value="<?= $invoice['due_date'] ?>" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="recurring" value="1" <?= $invoice['recurring'] ? 'checked' : '' ?>>
|
||||
<span class="form-check-label">გადახდა განმეორებით (ყოველთვიურად)</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
<button type="submit" class="btn btn-primary">შენახვა</button>
|
||||
<a href="dashboard.php?module=billing&submodule=invoices&action=view&id=<?= $invoice['id'] ?>" class="btn btn-secondary">უკან</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php require_once Config::includePath('footer.php'); ?>
|
||||
@@ -0,0 +1,64 @@
|
||||
<!-- ინვოისების სია + ფილტრები -->
|
||||
<?php require_once __DIR__ . '/../../models/invoicesmodel.php'; ?>
|
||||
<?php
|
||||
use App\Config;
|
||||
|
||||
InvoicesModel::setDb($pdo);
|
||||
$invoices = InvoicesModel::getAllInvoicesWithClientNames();
|
||||
|
||||
require_once Config::includePath('head.php');
|
||||
require_once Config::includePath('navbar.php');
|
||||
require_once Config::includePath('pageheader.php');
|
||||
require_once Config::includePath('pagebodystart.php');
|
||||
?>
|
||||
|
||||
<div class="container-xl mt-4">
|
||||
<div class="d-flex justify-content-between mb-3">
|
||||
<h2>ინვოისების სია</h2>
|
||||
<a href="dashboard.php?module=billing&submodule=invoices&action=create" class="btn btn-primary">+ ახალი ინვოისი</a>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>კლიენტი</th>
|
||||
<th>თანხა</th>
|
||||
<th>სტატუსი</th>
|
||||
<th>თარიღი</th>
|
||||
<th>ვადა</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($invoices as $invoice): ?>
|
||||
<tr>
|
||||
<td><?= $invoice['id'] ?></td>
|
||||
<td><?= htmlspecialchars($invoice['client_name']) ?></td>
|
||||
<td><?= number_format($invoice['total_amount'], 2) ?> ₾</td>
|
||||
<td>
|
||||
<span class="badge bg-light">
|
||||
<?= htmlspecialchars($invoice['status']) ?>
|
||||
</span>
|
||||
</td>
|
||||
<td><?= $invoice['issue_date'] ?></td>
|
||||
<td><?= $invoice['due_date'] ?></td>
|
||||
<td>
|
||||
<a href="dashboard.php?module=billing&submodule=invoices&action=view&id=<?= $invoice['id'] ?>" class="btn btn-sm btn-info">ნახვა</a>
|
||||
<a href="dashboard.php?module=billing&submodule=invoices&action=edit&id=<?= $invoice['id'] ?>" class="btn btn-sm btn-warning">რედაქტირება</a>
|
||||
<a href="dashboard.php?module=billing&submodule=invoices&action=delete&id=<?= $invoice['id'] ?>" class="btn btn-sm btn-danger" onclick="return confirm('ნამდვილად გსურს წაშლა?')">წაშლა</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Content END -->
|
||||
</div>
|
||||
</div>
|
||||
<?php require_once Config::includePath('footer.php'); ?>
|
||||
@@ -0,0 +1,167 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ka">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>ინვოისი</title>
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: DejaVu Sans;
|
||||
}
|
||||
body {
|
||||
font-family: DejaVu Sans;
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
}
|
||||
.invoice-box {
|
||||
max-width: 800px;
|
||||
margin: auto;
|
||||
padding: 30px;
|
||||
border: 1px solid #eee;
|
||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
.top-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
}
|
||||
.top-header img {
|
||||
max-width: 250px;
|
||||
}
|
||||
.bank-info {
|
||||
text-align: right;
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
.green-banner {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
background: #4CAF50;
|
||||
color: white;
|
||||
padding: 5px 20px;
|
||||
transform: rotate(45deg);
|
||||
transform-origin: top right;
|
||||
font-size: 16px;
|
||||
}
|
||||
.section {
|
||||
margin-top: 20px;
|
||||
}
|
||||
.gray-box {
|
||||
background: #eee;
|
||||
padding: 10px;
|
||||
font-weight: bold;
|
||||
}
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-top: 15px;
|
||||
}
|
||||
table, th, td {
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
th, td {
|
||||
padding: 10px;
|
||||
text-align: left;
|
||||
}
|
||||
th {
|
||||
background: #f9f9f9;
|
||||
}
|
||||
.footer {
|
||||
margin-top: 30px;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
color: #777;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="invoice-box">
|
||||
<!-- <div class="green-banner">გადახდილია</div> -->
|
||||
<div class="top-header">
|
||||
<div>
|
||||
<h3>SELFHOSTING.GE</h3>
|
||||
</div>
|
||||
<div class="bank-info">
|
||||
ი/მ ლევან არაბული<br>
|
||||
პ/ნ: 01001080490<br><br>
|
||||
ბანკი: თიბისი ბანკი<br>
|
||||
ა/ნ: GE79TB7902736010100047<br><br>
|
||||
ბანკი: საქართველოს ბანკი<br>
|
||||
ა/ნ: GE85BG0000000534211842<br>
|
||||
იდენტიფიკატორი: 01001080490
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section gray-box">
|
||||
ინვოისი #: <?= htmlspecialchars($invoice['invoice_number']) ?><br>
|
||||
ინვოისის თარიღი: <?= htmlspecialchars($invoice['issue_date']) ?><br>
|
||||
გადახდის თარიღი: <?= htmlspecialchars($invoice['due_date']) ?>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<strong>მიმღები:</strong><br>
|
||||
<?= htmlspecialchars($invoice['first_name'] . ' ' . $invoice['last_name']) ?><br>
|
||||
<?php if (!empty($invoice['company_name'])): ?>
|
||||
<?= htmlspecialchars($invoice['company_name']) ?><br>
|
||||
<?php endif; ?>
|
||||
<?php if (!empty($invoice['address1'])): ?>
|
||||
<?= htmlspecialchars($invoice['address1']) ?><br>
|
||||
<?php endif; ?>
|
||||
<?php if (!empty($invoice['vat_number'])): ?>
|
||||
VAT: <?= htmlspecialchars($invoice['vat_number']) ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>დასახელება</th>
|
||||
<th>აღწერა</th>
|
||||
<th>თანხა</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($invoice['items'] as $item): ?>
|
||||
<tr>
|
||||
<td><?= htmlspecialchars($item['name']) ?></td>
|
||||
<td><?= htmlspecialchars($item['description']) ?></td>
|
||||
<td><?= number_format($item['amount'], 2) ?> ლარი</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h3>ტრანზაქციები</h3>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ინვოისის ნომერი</th>
|
||||
<th>გადახდის მეთოდი</th>
|
||||
<th>ტრანზაქციის ID</th>
|
||||
<th>თანხა</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td># <?= htmlspecialchars($invoice['invoice_number']) ?></td>
|
||||
<td><?= htmlspecialchars($invoice['payment_method']) ?></td>
|
||||
<td>-</td>
|
||||
<td><?= number_format($invoice['total_amount'], 2) ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="3">ბალანსი</td>
|
||||
<td>0.00 GEL</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
Powered By Stack.ge | შექმნილია Stack.ge-ს მიერ
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,69 @@
|
||||
<!-- კონკრეტული ინვოისის ხილვა -->
|
||||
<?php require_once __DIR__ . '/../../models/invoicesmodel.php'; ?>
|
||||
|
||||
<?php
|
||||
use App\Config;
|
||||
|
||||
require_once Config::includePath('head.php');
|
||||
require_once Config::includePath('navbar.php');
|
||||
require_once Config::includePath('pageheader.php');
|
||||
require_once Config::includePath('pagebodystart.php');
|
||||
?>
|
||||
|
||||
|
||||
<!-- CONTENT START -->
|
||||
<?php if ($showAlert): ?>
|
||||
<div class="alert alert-success alert-dismissible fade show" role="alert" id="invoiceAlert">
|
||||
✅ ინვოისი წარმატებით გაიგზავნა!
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
<script>
|
||||
setTimeout(() => {
|
||||
const alertBox = document.getElementById('invoiceAlert');
|
||||
if (alertBox) alertBox.remove();
|
||||
}, 5000);
|
||||
</script>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="container-xl mt-4">
|
||||
<h2>ინვოისი # <?= $invoice['invoice_number'] ? htmlspecialchars($invoice['invoice_number']) : '' ?></h2>
|
||||
<p><strong>კლიენტი:</strong> <?= htmlspecialchars($invoice['first_name'] . ' ' . $invoice['last_name']) ?></p>
|
||||
<p><strong>სტატუსი:</strong> <?= htmlspecialchars($invoice['status']) ?></p>
|
||||
<p><strong>თარიღი:</strong> <?= $invoice['issue_date'] ?></p>
|
||||
<p><strong>ბოლო ვადა:</strong> <?= $invoice['due_date'] ?></p>
|
||||
<p><strong>გადასახდელი თანხა:</strong> <?= number_format($invoice['total_amount'], 2) ?> ₾</p>
|
||||
<p><strong>გადახდის მეთოდი:</strong> <?= htmlspecialchars($invoice['payment_method']) ?></p>
|
||||
<p><strong>აღწერა:</strong> <?= nl2br(htmlspecialchars($invoice['description'])) ?></p>
|
||||
|
||||
<?php if (!empty($productItems)): ?>
|
||||
<h4 class="mt-4">პროდუქტები</h4>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>პროდუქტი</th>
|
||||
<th>თანხა</th>
|
||||
<th>აღწერა</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($productItems as $item): ?>
|
||||
<tr>
|
||||
<td><?= htmlspecialchars($item['name']) ?></td>
|
||||
<td><?= number_format($item['amount'], 2) ?> ₾</td>
|
||||
<td><?= htmlspecialchars($item['description']) ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="mt-4">
|
||||
<a href="dashboard.php?module=billing&submodule=invoices&action=send&id=<?= $invoice['id'] ?>" class="btn btn-primary">გაგზავნა</a>
|
||||
<a href="dashboard.php?module=billing&submodule=invoices&action=edit&id=<?= $invoice['id'] ?>" class="btn btn-secondary">რედაქტირება</a>
|
||||
<a href="dashboard.php?module=billing&submodule=invoices&action=delete&id=<?= $invoice['id'] ?>" class="btn btn-danger">წაშლა</a>
|
||||
<a href="dashboard.php?module=billing&submodule=invoices&action=list" class="btn btn-light">უკან</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<?php require_once Config::includePath('footer.php'); ?>
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../../models/transactionsmodel.php';
|
||||
|
||||
use App\Config;
|
||||
|
||||
require_once Config::includePath('head.php');
|
||||
require_once Config::includePath('navbar.php');
|
||||
require_once Config::includePath('pageheader.php');
|
||||
require_once Config::includePath('pagebodystart.php');
|
||||
|
||||
?>
|
||||
|
||||
<div class="container-xl mt-4">
|
||||
<h2>ტრანზაქციის დამატება</h2>
|
||||
|
||||
<?php if (!empty($errors)): ?>
|
||||
<div class="alert alert-danger"><?= implode('<br>', $errors) ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form method="post">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">ინვოისი</label>
|
||||
<select name="invoice_id" class="form-select" required>
|
||||
<option value="">აირჩიე</option>
|
||||
<?php foreach ($invoices as $inv): ?>
|
||||
<option value="<?= $inv['id'] ?>"><?= htmlspecialchars($inv['invoice_number']) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">კლიენტი</label>
|
||||
<select name="client_id" class="form-select" required>
|
||||
<option value="">აირჩიე</option>
|
||||
<?php foreach ($clients as $cl): ?>
|
||||
<option value="<?= $cl['id'] ?>"><?= htmlspecialchars($cl['first_name'] . ' ' . $cl['last_name']) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">თანხა</label>
|
||||
<input type="number" name="amount" step="0.01" class="form-control" required>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">მეთოდი</label>
|
||||
<input type="text" name="method" class="form-control" required>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">სტატუსი</label>
|
||||
<select name="status" class="form-select" required>
|
||||
<option value="success">წარმატებული</option>
|
||||
<option value="failed">წარუმატებელი</option>
|
||||
<option value="pending">მოლოდინში</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">შენიშვნა</label>
|
||||
<textarea name="notes" class="form-control"></textarea>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-success">შენახვა</button>
|
||||
<a href="list.php" class="btn btn-secondary">გაუქმება</a>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<?php require_once Config::includePath('footer.php'); ?>
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../../models/transactionsmodel.php';
|
||||
|
||||
use App\Config;
|
||||
|
||||
require_once Config::includePath('head.php');
|
||||
require_once Config::includePath('navbar.php');
|
||||
require_once Config::includePath('pageheader.php');
|
||||
require_once Config::includePath('pagebodystart.php');
|
||||
|
||||
?>
|
||||
|
||||
|
||||
<div class="container-xl mt-4">
|
||||
<h2>ტრანზაქციის რედაქტირება</h2>
|
||||
<form method="post">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">ინვოისი</label>
|
||||
<input type="text" class="form-control" value="#<?= $transaction['invoice_number'] ?>" disabled>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">კლიენტი</label>
|
||||
<input type="text" class="form-control" value="<?= $transaction['first_name'] . ' ' . $transaction['last_name'] ?>" disabled>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">თანხა</label>
|
||||
<input type="text" class="form-control" value="<?= number_format($transaction['amount'], 2) ?> ₾" disabled>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">სტატუსი</label>
|
||||
<select name="status" class="form-select" required>
|
||||
<option value="success" <?= $transaction['status'] === 'success' ? 'selected' : '' ?>>დადასტურებული</option>
|
||||
<option value="failed" <?= $transaction['status'] === 'failed' ? 'selected' : '' ?>>ჩავარდა</option>
|
||||
<option value="pending" <?= $transaction['status'] === 'pending' ? 'selected' : '' ?>>მოლოდინში</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">გადახდის მეთოდი</label>
|
||||
<input type="text" name="method" class="form-control" value="<?= htmlspecialchars($transaction['method']) ?>">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">შენიშვნა</label>
|
||||
<textarea name="notes" class="form-control"><?= htmlspecialchars($transaction['notes']) ?></textarea>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">შენახვა</button>
|
||||
<a href="list.php" class="btn btn-secondary">უკან</a>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<?php require_once Config::includePath('footer.php'); ?>
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../../models/transactionsmodel.php';
|
||||
|
||||
use App\Config;
|
||||
|
||||
require_once Config::includePath('head.php');
|
||||
require_once Config::includePath('navbar.php');
|
||||
require_once Config::includePath('pageheader.php');
|
||||
require_once Config::includePath('pagebodystart.php');
|
||||
|
||||
?>
|
||||
|
||||
<div class="container-xl mt-4">
|
||||
<h2>ტრანზაქციების ისტორია - <?= htmlspecialchars($client['first_name'] . ' ' . $client['last_name']) ?></h2>
|
||||
|
||||
<?php if (empty($transactions)): ?>
|
||||
<div class="alert alert-info">ტრანზაქცია არ მოიძებნა.</div>
|
||||
<?php else: ?>
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ინვოისი</th>
|
||||
<th>თანხა</th>
|
||||
<th>მეთოდი</th>
|
||||
<th>სტატუსი</th>
|
||||
<th>შენიშვნა</th>
|
||||
<th>თარიღი</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($transactions as $t): ?>
|
||||
<tr>
|
||||
<td>#<?= htmlspecialchars($t['invoice_number']) ?></td>
|
||||
<td><?= number_format($t['amount'], 2) ?> ₾</td>
|
||||
<td><?= htmlspecialchars($t['method']) ?></td>
|
||||
<td>
|
||||
<span class="badge bg-<?= $t['status'] === 'success' ? 'success' : ($t['status'] === 'failed' ? 'danger' : 'warning') ?>">
|
||||
<?= htmlspecialchars($t['status']) ?>
|
||||
</span>
|
||||
</td>
|
||||
<td><?= nl2br(htmlspecialchars($t['notes'])) ?></td>
|
||||
<td><?= $t['created_at'] ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php endif; ?>
|
||||
|
||||
<a href="/admin/clients/view.php?id=<?= $clientId ?>" class="btn btn-light">🔙 უკან კლიენტის პროფილში</a>
|
||||
</div>
|
||||
|
||||
<?php require_once Config::includePath('footer.php'); ?>
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../../models/transactionsmodel.php';
|
||||
|
||||
use App\Config;
|
||||
|
||||
require_once Config::includePath('head.php');
|
||||
require_once Config::includePath('navbar.php');
|
||||
require_once Config::includePath('pageheader.php');
|
||||
require_once Config::includePath('pagebodystart.php');
|
||||
|
||||
?>
|
||||
|
||||
<?php if (!empty($successMessage)): ?>
|
||||
<div class="alert alert-success">
|
||||
<?= htmlspecialchars($successMessage) ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="container-xl mt-4">
|
||||
<h2>ტრანზაქციები</h2>
|
||||
<a href="dashboard.php?module=billing&submodule=transactions&action=create" class="btn btn-primary mb-3">➕ დამატება</a>
|
||||
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ინვოისი</th>
|
||||
<th>კლიენტი</th>
|
||||
<th>თანხა</th>
|
||||
<th>მეთოდი</th>
|
||||
<th>სტატუსი</th>
|
||||
<th>თარიღი</th>
|
||||
<th>ქმედებები</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($transactions as $tx): ?>
|
||||
<tr>
|
||||
<td><?= htmlspecialchars($tx['invoice_number']) ?></td>
|
||||
<td><?= htmlspecialchars($tx['first_name'] . ' ' . $tx['last_name']) ?></td>
|
||||
<td><?= number_format($tx['amount'], 2) ?> ₾</td>
|
||||
<td><?= htmlspecialchars($tx['method']) ?></td>
|
||||
<td><?= htmlspecialchars($tx['status']) ?></td>
|
||||
<td><?= $tx['created_at'] ?></td>
|
||||
<td>
|
||||
<a href="dashboard.php?module=billing&submodule=transactions&action=edit&id=<?= $tx['id'] ?>" class="btn btn-sm btn-warning">✏️</a>
|
||||
<a href="dashboard.php?module=billing&submodule=transactions&action=delete&id=<?= $tx['id'] ?>" class="btn btn-sm btn-danger" onclick="return confirm('დარწმუნებული ხარ?')">🗑️</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<?php require_once Config::includePath('footer.php'); ?>
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../../../includes/init.php'; // ბაზასთან კავშირი
|
||||
require_once __DIR__ . '/../models/client.php'; // Client მოდელის ჩასმა
|
||||
|
||||
Client::setDb($pdo); // აქ ვუკავშირებთ PDO ობიექტს მოდელს
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
// ყველა მონაცემის შეგროვება ერთი მასივში
|
||||
$data = [
|
||||
'first_name' => trim($_POST['first_name'] ?? ''),
|
||||
'last_name' => trim($_POST['last_name'] ?? ''),
|
||||
'company_name' => trim($_POST['company'] ?? null),
|
||||
'vat_number' => trim($_POST['vat_number'] ?? null),
|
||||
'email' => trim($_POST['email'] ?? ''),
|
||||
'password' => password_hash($_POST['password'], PASSWORD_BCRYPT),
|
||||
'address1' => trim($_POST['address1'] ?? null),
|
||||
'address2' => trim($_POST['address2'] ?? null),
|
||||
'city' => trim($_POST['city'] ?? null),
|
||||
'state' => trim($_POST['state'] ?? null),
|
||||
'postcode' => trim($_POST['postcode'] ?? null),
|
||||
'country' => trim($_POST['country'] ?? null),
|
||||
'phone' => trim($_POST['phone'] ?? null),
|
||||
'payment_method' => trim($_POST['payment_method'] ?? null),
|
||||
'billing_contact' => trim($_POST['billing_contact'] ?? null),
|
||||
'currency' => trim($_POST['currency'] ?? 'USD'),
|
||||
'language' => trim($_POST['language'] ?? 'default'),
|
||||
'status' => trim($_POST['status'] ?? 'active'),
|
||||
'client_group' => trim($_POST['client_group'] ?? 'none'),
|
||||
'admin_notes' => trim($_POST['admin_notes'] ?? null),
|
||||
];
|
||||
|
||||
// მონაცემის დამატება მოდელიდან
|
||||
Client::create($data);
|
||||
|
||||
header("Location: dashboard.php?module=clients&action=list");
|
||||
exit;
|
||||
}
|
||||
// view-ის ჩატვირთვა
|
||||
require_once __DIR__ . '/../views/clients_add.php';
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../../../includes/init.php';
|
||||
require_once __DIR__ . '/../models/client.php';
|
||||
|
||||
Client::setDb($pdo); // ვუთითებთ PDO ობიექტს
|
||||
|
||||
if (isset($_GET['id']) && is_numeric($_GET['id'])) {
|
||||
$id = (int)$_GET['id'];
|
||||
|
||||
// გადავცემთ ID-ს მოდელს წასაშლელად
|
||||
Client::delete($id);
|
||||
}
|
||||
|
||||
header("Location: dashboard.php?module=clients&action=list");
|
||||
exit;
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../../../includes/init.php';
|
||||
require_once __DIR__ . '/../models/client.php';
|
||||
|
||||
// შემოწმება GET id-ზე
|
||||
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
|
||||
header("Location: dashboard.php?module=clients&action=list");
|
||||
exit;
|
||||
}
|
||||
|
||||
$id = (int)$_GET['id'];
|
||||
|
||||
// მომხმარებლის წამოღება
|
||||
Client::setDb($pdo);
|
||||
$client = Client::find($id);
|
||||
|
||||
if (!$client) {
|
||||
echo "კლიენტი ვერ მოიძებნა.";
|
||||
exit;
|
||||
}
|
||||
|
||||
// თუ შენახვის ფორმა გამოიგზავნა
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$data = [
|
||||
'first_name' => trim($_POST['first_name'] ?? ''),
|
||||
'last_name' => trim($_POST['last_name'] ?? ''),
|
||||
'company_name' => trim($_POST['company'] ?? null),
|
||||
'email' => trim($_POST['email'] ?? ''),
|
||||
'phone' => trim($_POST['phone'] ?? null),
|
||||
'status' => trim($_POST['status'] ?? 'active'),
|
||||
'client_group' => trim($_POST['client_group'] ?? 'none'),
|
||||
'admin_notes' => trim($_POST['admin_notes'] ?? null),
|
||||
];
|
||||
|
||||
Client::update($id, $data);
|
||||
header("Location: dashboard.php?module=clients&action=profile&id=$id");
|
||||
exit;
|
||||
}
|
||||
|
||||
// view-ის ჩატვირთვა
|
||||
require_once __DIR__ . '/../views/clients_edit.php';
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../../../includes/init.php';
|
||||
require_once __DIR__ . '/../models/client.php';
|
||||
|
||||
Client::setDb($pdo);
|
||||
$clients = Client::all(); // მოდელიდან იღებ ყველა კლიენტს
|
||||
|
||||
include __DIR__ . '/../views/clients_list.php'; // აჩვენე View
|
||||
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . '/../../../includes/init.php';
|
||||
|
||||
// ID გადმოწოდებულია URL-იდან
|
||||
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
|
||||
header("Location: clients_list.php");
|
||||
exit;
|
||||
}
|
||||
|
||||
$id = (int)$_GET['id'];
|
||||
$stmt = $pdo->prepare("SELECT * FROM clients WHERE id = ?");
|
||||
$stmt->execute([$id]);
|
||||
$client = $stmt->fetch();
|
||||
|
||||
if (!$client) {
|
||||
echo "კლიენტი ვერ მოიძებნა.";
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Ჩვენ ვიღებთ კლიენტის ID-ს
|
||||
$client_id = $_GET['id'];
|
||||
|
||||
// ყველა ინვოისი ამ მომხმარებლისთვის
|
||||
$stmt = $pdo->prepare("SELECT status, total_amount FROM invoices WHERE client_id = ?");
|
||||
$stmt->execute([$client_id]);
|
||||
$invoices = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
// ინიციალიზაცია
|
||||
$paid = $draft = $unpaid_due = $cancelled = $refunded = 0;
|
||||
|
||||
// გამოთვლა
|
||||
foreach ($invoices as $inv) {
|
||||
$amount = (float)$inv['total_amount'];
|
||||
switch ($inv['status']) {
|
||||
case 'გადახდილი':
|
||||
$paid += $amount;
|
||||
break;
|
||||
case 'დრაფტი':
|
||||
$draft += $amount;
|
||||
break;
|
||||
case 'გადაუხდელი':
|
||||
case 'გადასახდელი':
|
||||
$unpaid_due += $amount;
|
||||
break;
|
||||
case 'გაუქმებული':
|
||||
$cancelled += $amount;
|
||||
break;
|
||||
case 'დაბრუნებული':
|
||||
$refunded += $amount;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$gross_revenue = $paid + $unpaid_due + $draft;
|
||||
$net_income = $paid;
|
||||
$credit_balance = 0.00; // შეგიძლია დაამატო ცალკე ცხრილიდან
|
||||
|
||||
|
||||
|
||||
$client_id = $_GET['id'];
|
||||
|
||||
// სერვისების მიღება
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT p.name AS product_name, ii.amount, ii.description, i.issue_date
|
||||
FROM invoice_items ii
|
||||
JOIN invoices i ON ii.invoice_id = i.id
|
||||
JOIN products p ON ii.product_id = p.id
|
||||
WHERE i.client_id = ?
|
||||
ORDER BY i.issue_date DESC
|
||||
");
|
||||
$stmt->execute([$client_id]);
|
||||
$services = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
|
||||
|
||||
require_once __DIR__ . '/../views/client_profile.php';
|
||||
|
||||
?>
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
<?php
|
||||
|
||||
class Client
|
||||
{
|
||||
// ➤ ბაზასთან კავშირის ცვლადი (გლობალური ან Dependency Injection)
|
||||
protected static $db;
|
||||
|
||||
public static function setDb($pdo)
|
||||
{
|
||||
self::$db = $pdo;
|
||||
}
|
||||
|
||||
// ➤ კლიენტის დამატება
|
||||
public static function create($data)
|
||||
{
|
||||
$sql = "INSERT INTO clients (
|
||||
first_name, last_name, company_name, vat_number, email, password,
|
||||
address1, address2, city, state, postcode, country, phone,
|
||||
payment_method, billing_contact, currency, language, status,
|
||||
client_group, admin_notes
|
||||
) VALUES (
|
||||
:first_name, :last_name, :company_name, :vat_number, :email, :password,
|
||||
:address1, :address2, :city, :state, :postcode, :country, :phone,
|
||||
:payment_method, :billing_contact, :currency, :language, :status,
|
||||
:client_group, :admin_notes
|
||||
)";
|
||||
|
||||
$stmt = self::$db->prepare($sql);
|
||||
|
||||
$stmt->execute([
|
||||
':first_name' => $data['first_name'],
|
||||
':last_name' => $data['last_name'],
|
||||
':company_name' => $data['company_name'] ?? null,
|
||||
':vat_number' => $data['vat_number'] ?? null,
|
||||
':email' => $data['email'],
|
||||
':password' => $data['password'],
|
||||
':address1' => $data['address1'] ?? null,
|
||||
':address2' => $data['address2'] ?? null,
|
||||
':city' => $data['city'] ?? null,
|
||||
':state' => $data['state'] ?? null,
|
||||
':postcode' => $data['postcode'] ?? null,
|
||||
':country' => $data['country'] ?? null,
|
||||
':phone' => $data['phone'] ?? null,
|
||||
':payment_method' => $data['payment_method'] ?? null,
|
||||
':billing_contact' => $data['billing_contact'] ?? null,
|
||||
':currency' => $data['currency'] ?? 'USD',
|
||||
':language' => $data['language'] ?? 'default',
|
||||
':status' => $data['status'] ?? 'active',
|
||||
':client_group' => $data['client_group'] ?? 'none',
|
||||
':admin_notes' => $data['admin_notes'] ?? null
|
||||
]);
|
||||
}
|
||||
|
||||
// ➤ ყველა კლიენტის წამოღება
|
||||
public static function all()
|
||||
{
|
||||
$stmt = self::$db->query("SELECT * FROM clients ORDER BY id DESC");
|
||||
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
// ➤ ერთი კლიენტის წამოღება ID-ით
|
||||
public static function find($id)
|
||||
{
|
||||
$stmt = self::$db->prepare("SELECT * FROM clients WHERE id = ?");
|
||||
$stmt->execute([$id]);
|
||||
return $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
// ➤ კლიენტის განახლება
|
||||
public static function update($id, $data)
|
||||
{
|
||||
$sql = "UPDATE clients SET
|
||||
first_name = :first_name,
|
||||
last_name = :last_name,
|
||||
company_name = :company_name,
|
||||
vat_number = :vat_number,
|
||||
email = :email,
|
||||
address1 = :address1,
|
||||
address2 = :address2,
|
||||
city = :city,
|
||||
state = :state,
|
||||
postcode = :postcode,
|
||||
country = :country,
|
||||
phone = :phone,
|
||||
payment_method = :payment_method,
|
||||
billing_contact = :billing_contact,
|
||||
currency = :currency,
|
||||
language = :language,
|
||||
status = :status,
|
||||
client_group = :client_group,
|
||||
admin_notes = :admin_notes
|
||||
WHERE id = :id";
|
||||
|
||||
$stmt = self::$db->prepare($sql);
|
||||
|
||||
// აუცილებელია ყველა პარამეტრი იყოს განსაზღვრული
|
||||
$params = [
|
||||
':first_name' => $data['first_name'] ?? '',
|
||||
':last_name' => $data['last_name'] ?? '',
|
||||
':company_name' => $data['company_name'] ?? null,
|
||||
':vat_number' => $data['vat_number'] ?? null,
|
||||
':email' => $data['email'] ?? '',
|
||||
':address1' => $data['address1'] ?? null,
|
||||
':address2' => $data['address2'] ?? null,
|
||||
':city' => $data['city'] ?? null,
|
||||
':state' => $data['state'] ?? null,
|
||||
':postcode' => $data['postcode'] ?? null,
|
||||
':country' => $data['country'] ?? null,
|
||||
':phone' => $data['phone'] ?? null,
|
||||
':payment_method' => $data['payment_method'] ?? null,
|
||||
':billing_contact' => $data['billing_contact'] ?? null,
|
||||
':currency' => $data['currency'] ?? 'USD',
|
||||
':language' => $data['language'] ?? 'default',
|
||||
':status' => $data['status'] ?? 'active',
|
||||
':client_group' => $data['client_group'] ?? 'none',
|
||||
':admin_notes' => $data['admin_notes'] ?? null,
|
||||
':id' => $id
|
||||
];
|
||||
|
||||
$stmt->execute($params);
|
||||
}
|
||||
|
||||
|
||||
// ➤ კლიენტის წაშლა
|
||||
public static function delete($id)
|
||||
{
|
||||
$stmt = self::$db->prepare("DELETE FROM clients WHERE id = ?");
|
||||
$stmt->execute([$id]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
|
||||
|
||||
<?php
|
||||
require_once __DIR__ . '/../../../../vendor/autoload.php';
|
||||
use App\Config;
|
||||
|
||||
require_once Config::includePath('head.php');
|
||||
require_once Config::includePath('navbar.php');
|
||||
require_once Config::includePath('pageheader.php');
|
||||
require_once Config::includePath('pagebodystart.php');
|
||||
|
||||
?>
|
||||
|
||||
<!-- CONTENT START -->
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h2>კლიენტის პროფილი: <?= htmlspecialchars($client['first_name'] . ' ' . $client['last_name']) ?></h2>
|
||||
<a href="dashboard.php?module=clients&action=list" class="btn btn-secondary">← უკან სიისკენ</a>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="row row-cards">
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header"><strong>Clients Information</strong></div>
|
||||
<div class="card-body">
|
||||
<p><strong>First Name:</strong> <?= htmlspecialchars($client['first_name']) ?></p>
|
||||
<p><strong>Last Name:</strong> <?= htmlspecialchars($client['last_name']) ?></p>
|
||||
<p><strong>Company:</strong> <?= htmlspecialchars($client['company_name']) ?></p>
|
||||
<p><strong>Email:</strong> <?= htmlspecialchars($client['email']) ?></p>
|
||||
<p><strong>Phone:</strong> <?= htmlspecialchars($client['phone']) ?></p>
|
||||
<p><strong>Address 1:</strong> <?= htmlspecialchars($client['address1']) ?></p>
|
||||
<p><strong>Address 2:</strong> <?= htmlspecialchars($client['address2']) ?></p>
|
||||
<p><strong>City:</strong> <?= htmlspecialchars($client['city']) ?></p>
|
||||
<p><strong>State/Region:</strong> <?= htmlspecialchars($client['state']) ?></p>
|
||||
<p><strong>Postcode:</strong> <?= htmlspecialchars($client['postcode']) ?></p>
|
||||
<p><strong>Country:</strong> <?= htmlspecialchars($client['country']) ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
|
||||
|
||||
?>
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header"><strong>Invoices / Billing</strong></div>
|
||||
<div class="card-body">
|
||||
<p><strong>Paid:</strong> ₾<?= number_format($paid, 2) ?></p>
|
||||
<p><strong>Draft:</strong> ₾<?= number_format($draft, 2) ?></p>
|
||||
<p><strong>Unpaid / Due:</strong> ₾<?= number_format($unpaid_due, 2) ?></p>
|
||||
<p><strong>Cancelled:</strong> ₾<?= number_format($cancelled, 2) ?></p>
|
||||
<p><strong>Refunded:</strong> ₾<?= number_format($refunded, 2) ?></p>
|
||||
<hr>
|
||||
<p><strong>Gross Revenue:</strong> ₾<?= number_format($gross_revenue, 2) ?></p>
|
||||
<p><strong>Net Income:</strong> ₾<?= number_format($net_income, 2) ?></p>
|
||||
<p><strong>Credit Balance:</strong> ₾<?= number_format($credit_balance, 2) ?></p>
|
||||
|
||||
<div class="mt-3">
|
||||
<a href="#" class="btn btn-outline-primary btn-sm">➕ Create Invoice</a>
|
||||
<a href="#" class="btn btn-outline-success btn-sm">➕ Add Funds Invoice</a>
|
||||
<a href="#" class="btn btn-outline-info btn-sm">🔁 Generate Due Invoices</a>
|
||||
<a href="#" class="btn btn-outline-warning btn-sm">💳 Manage Credits</a>
|
||||
<a href="/admin/billing/transactions/history.php?client_id=<?= $client['id'] ?>" class="btn btn-outline-warning btn-sm">ტრანზაქციების ისტორია</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row row-cards mt-4">
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header"><strong>Admin Notes</strong></div>
|
||||
<div class="card-body">
|
||||
<p><?= nl2br(htmlspecialchars($client['admin_notes'])) ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header"><strong>Products / Services</strong></div>
|
||||
<div class="card-body">
|
||||
<?php if (count($services) > 0): ?>
|
||||
<ul class="list-group list-group-flush">
|
||||
<?php foreach ($services as $service): ?>
|
||||
<li class="list-group-item">
|
||||
<strong><?= htmlspecialchars($service['product_name']) ?></strong>
|
||||
- <?= number_format($service['amount'], 2) ?> ₾
|
||||
<br>
|
||||
<small class="text-muted"> <?= htmlspecialchars($service['description']) ?> </small>
|
||||
<br>
|
||||
<small class="text-muted"> თარიღი: <?= htmlspecialchars($service['issue_date']) ?> </small>
|
||||
</li>
|
||||
<?php endforeach ?>
|
||||
</ul>
|
||||
<?php else: ?>
|
||||
<p class="text-muted">სერვისები არ მოიძებნა.</p>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<?php require_once Config::includePath('footer.php'); ?>
|
||||
@@ -0,0 +1,173 @@
|
||||
|
||||
<?php
|
||||
require_once __DIR__ . '/../../../../vendor/autoload.php';
|
||||
use App\Config;
|
||||
|
||||
require_once Config::includePath('head.php');
|
||||
require_once Config::includePath('navbar.php');
|
||||
require_once Config::includePath('pageheader.php');
|
||||
require_once Config::includePath('pagebodystart.php');
|
||||
|
||||
?>
|
||||
|
||||
|
||||
<!-- CONTENT START -->
|
||||
|
||||
<h2 class="mb-4">ახალი კლიენტის დამატება</h2>
|
||||
<form action="dashboard.php?module=clients&action=add" method="post">
|
||||
<div class="row">
|
||||
<!-- მარცხენა სვეტი -->
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">სახელი</label>
|
||||
<input type="text" class="form-control" name="first_name" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">გვარი</label>
|
||||
<input type="text" class="form-control" name="last_name" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">კომპანიის სახელი <span class="text-muted">(სურვილისამებრ)</span></label>
|
||||
<input type="text" class="form-control" name="company">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">საიდ. ნომ <span class="text-muted">(სურვილისამებრ)</span></label>
|
||||
<input type="text" class="form-control" name="vat_number">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">ელ-ფოსტა</label>
|
||||
<input type="email" class="form-control" name="email" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">პაროლი</label>
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" id="password" name="password" required>
|
||||
<button class="btn btn-outline-secondary" type="button" onclick="generatePassword()">გენერირება</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">ენა</label>
|
||||
<select class="form-select" name="language">
|
||||
<option value="default">ნაგულისხმევი</option>
|
||||
<option value="en">English</option>
|
||||
<option value="ka">Georgian</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">სტატუსი</label>
|
||||
<select class="form-select" name="status">
|
||||
<option value="active">აქტიური</option>
|
||||
<option value="inactive">გაუქმებული</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">ჯგუფი</label>
|
||||
<select class="form-select" name="client_group">
|
||||
<option value="none">ცარიელი</option>
|
||||
<option value="vip">VIP</option>
|
||||
<option value="reseller">Reseller</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Email შეტყობინებები</label>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="emails[]" value="general" checked>
|
||||
<label class="form-check-label">General Emails</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="emails[]" value="invoice" checked>
|
||||
<label class="form-check-label">Invoice Emails</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="emails[]" value="support">
|
||||
<label class="form-check-label">Support Emails</label>
|
||||
</div>
|
||||
<!-- სხვა Checkbox-ები შეგიძლია დაამატო ასე -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- მარჯვენა სვეტი -->
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">მისამართი 1</label>
|
||||
<input type="text" class="form-control" name="address1">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">მისამართი 2</label>
|
||||
<input type="text" class="form-control" name="address2">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">ქალაქი</label>
|
||||
<input type="text" class="form-control" name="city">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">შტატი/რეგიონი</label>
|
||||
<input type="text" class="form-control" name="state">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">საფოსტო ინდექსი</label>
|
||||
<input type="text" class="form-control" name="postcode">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">ქვეყანა</label>
|
||||
<select class="form-select" name="country">
|
||||
<option value="US">United States</option>
|
||||
<option value="GE">საქართველო</option>
|
||||
<!-- სხვა ქვეყნები -->
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">ტელეფონი</label>
|
||||
<input type="text" class="form-control" name="phone">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">გადახდის მეთოდი</label>
|
||||
<select class="form-select" name="payment_method">
|
||||
<option value="default">Default</option>
|
||||
<option value="paypal">PayPal</option>
|
||||
<option value="bank">საბანკო გადმორიცხვა</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Billing Contact</label>
|
||||
<select class="form-select" name="billing_contact">
|
||||
<option value="default">Default</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">ვალუტა</label>
|
||||
<select class="form-select" name="currency">
|
||||
<option value="USD">USD</option>
|
||||
<option value="GEL">GEL</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Admin ჩანაწერი</label>
|
||||
<textarea class="form-control" name="admin_notes" rows="3"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-footer">
|
||||
<button type="submit" class="btn btn-primary">დამატება</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php require_once Config::includePath('footer.php'); ?>
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
function generatePassword(length = 10) {
|
||||
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()";
|
||||
let password = "";
|
||||
for (let i = 0; i < length; i++) {
|
||||
const randomIndex = Math.floor(Math.random() * charset.length);
|
||||
password += charset[randomIndex];
|
||||
}
|
||||
document.getElementById('password').value = password;
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../../../../vendor/autoload.php';
|
||||
use App\Config;
|
||||
|
||||
require_once Config::includePath('head.php');
|
||||
require_once Config::includePath('navbar.php');
|
||||
require_once Config::includePath('pageheader.php');
|
||||
require_once Config::includePath('pagebodystart.php');
|
||||
?>
|
||||
|
||||
|
||||
|
||||
<h2 class="mb-4">კლიენტის რედაქტირება</h2>
|
||||
<form action="dashboard.php?module=clients&action=edit" method="POST">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">First Name</label>
|
||||
<input type="text" class="form-control" name="first_name" value="<?= htmlspecialchars($client['first_name']) ?>" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Last Name</label>
|
||||
<input type="text" class="form-control" name="last_name" value="<?= htmlspecialchars($client['last_name']) ?>" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Company</label>
|
||||
<input type="text" class="form-control" name="company" value="<?= htmlspecialchars($client['company_name']) ?>">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Email</label>
|
||||
<input type="email" class="form-control" name="email" value="<?= htmlspecialchars($client['email']) ?>" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Phone</label>
|
||||
<input type="text" class="form-control" name="phone" value="<?= htmlspecialchars($client['phone']) ?>">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Status</label>
|
||||
<select class="form-select" name="status">
|
||||
<option value="active" <?= $client['status'] === 'active' ? 'selected' : '' ?>>Active</option>
|
||||
<option value="inactive" <?= $client['status'] === 'inactive' ? 'selected' : '' ?>>Inactive</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Client Group</label>
|
||||
<input type="text" class="form-control" name="client_group" value="<?= htmlspecialchars($client['client_group']) ?>">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Admin Notes</label>
|
||||
<textarea class="form-control" name="admin_notes" rows="5"><?= htmlspecialchars($client['admin_notes']) ?></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-footer">
|
||||
<button type="submit" class="btn btn-primary">შენახვა</button>
|
||||
<a href="dashboard.php?module=clients&action=profile&id=<?= $client['id'] ?>" class="btn btn-secondary">გაუქმება</a>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
||||
<?php require_once Config::includePath('footer.php'); ?>
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../../../../vendor/autoload.php';
|
||||
use App\Config;
|
||||
|
||||
require_once Config::includePath('head.php');
|
||||
require_once Config::includePath('navbar.php');
|
||||
require_once Config::includePath('pageheader.php');
|
||||
require_once Config::includePath('pagebodystart.php');
|
||||
?>
|
||||
|
||||
<!-- CONTENT START -->
|
||||
|
||||
|
||||
<h2 class="mb-4">კლიენტების სია</h2>
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>First Name</th>
|
||||
<th>Last Name</th>
|
||||
<th>კომპანია</th>
|
||||
<th>ელ.ფოსტა</th>
|
||||
<th>ტელეფონი</th>
|
||||
<th>შექმნის თარიღი</th>
|
||||
<th>სტატუს</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($clients as $client): ?>
|
||||
<tr>
|
||||
<td><?= htmlspecialchars($client['id']) ?></td>
|
||||
<td><?= htmlspecialchars($client['first_name']) ?></td>
|
||||
<td><?= htmlspecialchars($client['last_name']) ?></td>
|
||||
<td><?= htmlspecialchars($client['company_name']) ?></td>
|
||||
<td><?= htmlspecialchars($client['email']) ?></td>
|
||||
<td><?= htmlspecialchars($client['phone']) ?></td>
|
||||
<td><?= date('d/m/Y', strtotime($client['created_at'])) ?></td>
|
||||
<td>
|
||||
<span class="badge bg-light">
|
||||
<?= strtoupper($client['status']) ?>
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<a href="dashboard.php?module=clients&action=profile&id=<?= $client['id'] ?>" class="btn btn-sm btn-info">პროფილი</a>
|
||||
<a href="dashboard.php?module=clients&action=edit&id=<?= $client['id'] ?>" class="btn btn-sm btn-warning">რედ.</a>
|
||||
<a href="dashboard.php?module=clients&action=delete&id=<?= $client['id'] ?>" class="btn btn-sm btn-danger" onclick="return confirm('ნამდვილად გსურს წაშლა?');">წაშლა</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<?php require_once Config::includePath('footer.php'); ?>
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
|
||||
require_once __DIR__ . '/../../../includes/init.php';
|
||||
require_once __DIR__ . '/../models/marketingmodels.php';
|
||||
|
||||
|
||||
MarketingModel::setDb($pdo);
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$clientIds = $_POST['clients'] ?? [];
|
||||
$subject = trim($_POST['subject'] ?? '');
|
||||
$message = trim($_POST['message'] ?? '');
|
||||
|
||||
if (!empty($clientIds) && $subject && $message) {
|
||||
$success = MarketingModel::sendBroadcast($clientIds, $subject, $message);
|
||||
header("Location: dashboard.php?module=marketing&action=broadcast&sent=1");
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
// მხოლოდ GET მოთხოვნის დროს ჩაიტვირთოს ფორმა
|
||||
require_once __DIR__ . '/../views/broadcast.php';
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
|
||||
require_once __DIR__ . '/../../../includes/init.php';
|
||||
require_once __DIR__ . '/../models/marketingmodels.php';
|
||||
|
||||
MarketingModel::setDb($pdo);
|
||||
$emailLogs = MarketingModel::getEmailLogs();
|
||||
|
||||
require_once __DIR__ . '/../views/email_logs.php';
|
||||
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
|
||||
use PHPMailer\PHPMailer\PHPMailer;
|
||||
use PHPMailer\PHPMailer\Exception;
|
||||
|
||||
require_once __DIR__ . '/../../../libs/phpmailer/src/PHPMailer.php';
|
||||
require_once __DIR__ . '/../../../libs/phpmailer/src/SMTP.php';
|
||||
require_once __DIR__ . '/../../../libs/phpmailer/src/Exception.php';
|
||||
|
||||
class MarketingModel
|
||||
{
|
||||
protected static $db;
|
||||
|
||||
public static function setDb($pdo)
|
||||
{
|
||||
self::$db = $pdo;
|
||||
}
|
||||
|
||||
/**
|
||||
* SMTP კონფიგურაციის მიღება ბაზიდან
|
||||
*/
|
||||
private static function getSmtpConfig()
|
||||
{
|
||||
$stmt = self::$db->query("SELECT setting_key, setting_value FROM smtp_settings");
|
||||
$settings = $stmt->fetchAll(PDO::FETCH_KEY_PAIR);
|
||||
|
||||
// Default ღირებულებები თუ ბაზაში არ არის
|
||||
return array_merge([
|
||||
'smtp_host' => 'localhost',
|
||||
'smtp_port' => '587',
|
||||
'smtp_secure' => 'tls',
|
||||
'smtp_auth' => '1',
|
||||
'smtp_username' => '',
|
||||
'smtp_password' => '',
|
||||
'smtp_from_email' => 'noreply@example.com',
|
||||
'smtp_from_name' => 'Website',
|
||||
'smtp_debug' => '0'
|
||||
], $settings);
|
||||
}
|
||||
|
||||
public static function sendBroadcast($clientIds, $subject, $message)
|
||||
{
|
||||
$stmt = self::$db->prepare(
|
||||
"SELECT id, email, first_name, last_name
|
||||
FROM clients
|
||||
WHERE id IN (" . implode(',', array_fill(0, count($clientIds), '?')) . ")"
|
||||
);
|
||||
$stmt->execute($clientIds);
|
||||
$clients = $stmt->fetchAll();
|
||||
|
||||
// SMTP კონფიგურაციის მიღება ბაზიდან
|
||||
$smtpConfig = self::getSmtpConfig();
|
||||
|
||||
foreach ($clients as $client) {
|
||||
$mail = new PHPMailer(true);
|
||||
|
||||
try {
|
||||
$mail->isSMTP();
|
||||
$mail->Host = $smtpConfig['smtp_host'];
|
||||
$mail->SMTPAuth = (bool)$smtpConfig['smtp_auth'];
|
||||
$mail->Username = $smtpConfig['smtp_username'];
|
||||
$mail->Password = $smtpConfig['smtp_password'];
|
||||
$mail->SMTPSecure = $smtpConfig['smtp_secure'];
|
||||
$mail->Port = (int)$smtpConfig['smtp_port'];
|
||||
|
||||
$mail->SMTPDebug = (int)$smtpConfig['smtp_debug'];
|
||||
$mail->Debugoutput = 'error_log';
|
||||
|
||||
$mail->setFrom($smtpConfig['smtp_from_email'], $smtpConfig['smtp_from_name']);
|
||||
$mail->addAddress($client['email'], $client['first_name'] . ' ' . $client['last_name']);
|
||||
$mail->CharSet = 'UTF-8';
|
||||
$mail->isHTML(true);
|
||||
$mail->Subject = $subject;
|
||||
$mail->Body = nl2br(htmlspecialchars($message));
|
||||
|
||||
if ($mail->send()) {
|
||||
$log = self::$db->prepare(
|
||||
"INSERT INTO email_logs (client_id, subject, message, sent_at) VALUES (?, ?, ?, NOW())"
|
||||
);
|
||||
$log->execute([$client['id'], $subject, $message]);
|
||||
} else {
|
||||
error_log("გაგზავნის შეცდომა: " . $mail->ErrorInfo);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
error_log("PHPMailer გამონაკლისი: " . $mail->ErrorInfo);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function getAllClients()
|
||||
{
|
||||
$stmt = self::$db->query("SELECT id, first_name, last_name, email FROM clients ORDER BY first_name");
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
|
||||
public static function getEmailLogs()
|
||||
{
|
||||
$stmt = self::$db->query("SELECT email_logs.*, clients.first_name, clients.last_name FROM email_logs
|
||||
JOIN clients ON email_logs.client_id = clients.id
|
||||
ORDER BY sent_at DESC");
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
|
||||
require_once __DIR__ . '/../../../includes/init.php';
|
||||
require_once __DIR__ . '/../../../../vendor/autoload.php';
|
||||
use App\Config;
|
||||
|
||||
|
||||
$success = isset($_GET['sent']) && $_GET['sent'] == 1;
|
||||
|
||||
// კლიენტების წამოღება
|
||||
MarketingModel::setDb($pdo);
|
||||
$clients = MarketingModel::getAllClients();
|
||||
?>
|
||||
|
||||
<?php require_once Config::includePath('head.php'); ?>
|
||||
<?php require_once Config::includePath('navbar.php'); ?>
|
||||
<?php require_once Config::includePath('pageheader.php'); ?>
|
||||
<?php require_once Config::includePath('pagebodystart.php'); ?>
|
||||
|
||||
|
||||
<div class="container-xl mt-4">
|
||||
<h2>ელ.ფოსტის გაგზავნა კლიენტებზე</h2>
|
||||
|
||||
<?php if ($success): ?>
|
||||
<div class="alert alert-success">შეტყობინება წარმატებით გაიგზავნა!</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form action="dashboard.php?module=marketing&action=broadcast" method="POST">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">აირჩიე კლიენტები</label><br>
|
||||
<?php foreach ($clients as $client): ?>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="checkbox" name="clients[]" value="<?= $client['id'] ?>">
|
||||
<label class="form-check-label"> <?= htmlspecialchars($client['first_name'] . ' ' . $client['last_name']) ?> </label>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">თემა</label>
|
||||
<input type="text" name="subject" class="form-control" required>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">შეტყობინება</label>
|
||||
<textarea name="message" class="form-control" rows="5" required></textarea>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary">გაგზავნა</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<?php require_once Config::includePath('footer.php'); ?>
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
|
||||
require_once __DIR__ . '/../../../includes/init.php';
|
||||
require_once __DIR__ . '/../../../../vendor/autoload.php';
|
||||
use App\Config;
|
||||
?>
|
||||
|
||||
<?php require_once App\Config::includePath('head.php'); ?>
|
||||
<?php require_once App\Config::includePath('navbar.php'); ?>
|
||||
<?php require_once App\Config::includePath('pageheader.php'); ?>
|
||||
<?php require_once App\Config::includePath('pagebodystart.php'); ?>
|
||||
|
||||
<div class="container-xl mt-4">
|
||||
<h2>ელ.ფოსტის გაგზავნის ისტორია</h2>
|
||||
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>კლიენტი</th>
|
||||
<th>თემა</th>
|
||||
<th>შეტყობინება</th>
|
||||
<th>გაგზავნის დრო</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($emailLogs as $log): ?>
|
||||
<tr>
|
||||
<td><?= htmlspecialchars($log['first_name'] . ' ' . $log['last_name']) ?></td>
|
||||
<td><?= htmlspecialchars($log['subject']) ?></td>
|
||||
<td><?= nl2br(htmlspecialchars($log['message'])) ?></td>
|
||||
<td><?= htmlspecialchars($log['sent_at']) ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<?php require_once App\Config::includePath('footer.php'); ?>
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
// controllers/create.php
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
|
||||
require_once __DIR__ . '/../../../includes/init.php';
|
||||
require_once __DIR__ . '/../models/productmodels.php';
|
||||
require_once __DIR__ . '/../../../../vendor/autoload.php';
|
||||
// ბაზის კავშირის გადაცემა მოდელს
|
||||
ProductModel::setDb($pdo);
|
||||
|
||||
// (დროებით არ გვჭირდება dropdown-ებისთვის ცალკე მონაცემები, მაგრამ თუ დაგჭირდება შემიძლია დავამატო)
|
||||
|
||||
// გადადი ფორმის ვიუ გვერდზე
|
||||
require_once __DIR__ . '/../views/create.php';
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
|
||||
require_once __DIR__ . '/../../../includes/init.php';
|
||||
require_once __DIR__ . '/../models/productmodels.php';
|
||||
|
||||
ProductModel::setDb($pdo);
|
||||
|
||||
if (isset($_GET['id']) && is_numeric($_GET['id'])) {
|
||||
$id = (int) $_GET['id'];
|
||||
ProductModel::deleteProductById($id);
|
||||
}
|
||||
|
||||
header("Location: dashboard.php?module=product&action=list&deleted=1");
|
||||
exit;
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
|
||||
require_once __DIR__ . '/../../../includes/init.php';
|
||||
require_once __DIR__ . '/../models/productmodels.php';
|
||||
require_once __DIR__ . '/../../../../vendor/autoload.php';
|
||||
|
||||
ProductModel::setDb($pdo);
|
||||
|
||||
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
|
||||
echo "<div class='container-xl mt-4'><div class='alert alert-danger'>ID არ არის სწორი.</div></div>";
|
||||
exit;
|
||||
}
|
||||
|
||||
$id = (int) $_GET['id'];
|
||||
$product = ProductModel::getProductById($id);
|
||||
|
||||
if (!$product) {
|
||||
echo "<div class='container-xl mt-4'><div class='alert alert-danger'>პროდუქტი ვერ მოიძებნა.</div></div>";
|
||||
exit;
|
||||
}
|
||||
|
||||
$tab = $_GET['tab'] ?? 'details';
|
||||
|
||||
require_once __DIR__ . '/../views/edit.php';
|
||||
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
// Product index controller - redirects to list
|
||||
header("Location: dashboard.php?module=product&action=list");
|
||||
exit;
|
||||
?>
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
// controllers/list.php
|
||||
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
|
||||
require_once __DIR__ . '/../../../includes/init.php';
|
||||
require_once __DIR__ . '/../models/productmodels.php';
|
||||
require_once __DIR__ . '/../../../../vendor/autoload.php';
|
||||
|
||||
ProductModel::setDb($pdo);
|
||||
$products = ProductModel::getAllProducts();
|
||||
|
||||
require_once __DIR__ . '/../views/list.php';
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
|
||||
require_once __DIR__ . '/../../../includes/init.php';
|
||||
require_once __DIR__ . '/../models/producttypesmodel.php';
|
||||
|
||||
ProductTypesModel::setDb($pdo);
|
||||
|
||||
$message = '';
|
||||
$messageType = '';
|
||||
$action = $_GET['subaction'] ?? 'list';
|
||||
|
||||
// POST მოთხოვნების მართვა
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$postAction = $_POST['action'] ?? '';
|
||||
|
||||
switch ($postAction) {
|
||||
case 'add':
|
||||
$data = [
|
||||
'name' => trim($_POST['name'] ?? ''),
|
||||
'description' => trim($_POST['description'] ?? ''),
|
||||
'icon' => $_POST['icon'] ?? 'box',
|
||||
'sort_order' => (int)($_POST['sort_order'] ?? 0),
|
||||
'is_active' => isset($_POST['is_active']) ? 1 : 0
|
||||
];
|
||||
|
||||
if (empty($data['name'])) {
|
||||
$message = 'პროდუქტის ტიპის სახელი სავალდებულოა';
|
||||
$messageType = 'danger';
|
||||
} else {
|
||||
try {
|
||||
ProductTypesModel::addType($data);
|
||||
$message = 'პროდუქტის ტიპი წარმატებით დაემატა';
|
||||
$messageType = 'success';
|
||||
$action = 'list';
|
||||
} catch (Exception $e) {
|
||||
$message = 'შეცდომა: ' . $e->getMessage();
|
||||
$messageType = 'danger';
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'edit':
|
||||
$id = (int)($_POST['id'] ?? 0);
|
||||
$data = [
|
||||
'name' => trim($_POST['name'] ?? ''),
|
||||
'description' => trim($_POST['description'] ?? ''),
|
||||
'icon' => $_POST['icon'] ?? 'box',
|
||||
'sort_order' => (int)($_POST['sort_order'] ?? 0),
|
||||
'is_active' => isset($_POST['is_active']) ? 1 : 0
|
||||
];
|
||||
|
||||
if (empty($data['name']) || $id <= 0) {
|
||||
$message = 'არასწორი მონაცემები';
|
||||
$messageType = 'danger';
|
||||
} else {
|
||||
try {
|
||||
ProductTypesModel::updateType($id, $data);
|
||||
$message = 'პროდუქტის ტიპი წარმატებით განახლდა';
|
||||
$messageType = 'success';
|
||||
$action = 'list';
|
||||
} catch (Exception $e) {
|
||||
$message = 'შეცდომა: ' . $e->getMessage();
|
||||
$messageType = 'danger';
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'delete':
|
||||
$id = (int)($_POST['id'] ?? 0);
|
||||
if ($id > 0) {
|
||||
try {
|
||||
ProductTypesModel::deleteType($id);
|
||||
$message = 'პროდუქტის ტიპი წარმატებით წაიშალა';
|
||||
$messageType = 'success';
|
||||
} catch (Exception $e) {
|
||||
$message = 'შეცდომა: ' . $e->getMessage();
|
||||
$messageType = 'danger';
|
||||
}
|
||||
}
|
||||
$action = 'list';
|
||||
break;
|
||||
|
||||
case 'update_order':
|
||||
$orders = $_POST['orders'] ?? [];
|
||||
try {
|
||||
ProductTypesModel::updateSortOrder($orders);
|
||||
$message = 'რიგითობა წარმატებით განახლდა';
|
||||
$messageType = 'success';
|
||||
} catch (Exception $e) {
|
||||
$message = 'შეცდომა: ' . $e->getMessage();
|
||||
$messageType = 'danger';
|
||||
}
|
||||
$action = 'list';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// მონაცემების მომზადება view-სთვის
|
||||
switch ($action) {
|
||||
case 'add':
|
||||
case 'edit':
|
||||
$availableIcons = ProductTypesModel::getAvailableIcons();
|
||||
if ($action === 'edit') {
|
||||
$editId = (int)($_GET['id'] ?? 0);
|
||||
$editType = ProductTypesModel::getTypeById($editId);
|
||||
if (!$editType) {
|
||||
$message = 'პროდუქტის ტიპი ვერ მოიძებნა';
|
||||
$messageType = 'danger';
|
||||
$action = 'list';
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'list':
|
||||
default:
|
||||
$productTypes = ProductTypesModel::getAllTypes();
|
||||
break;
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/../views/product_types.php';
|
||||
?>
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
// controllers/save.php
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
|
||||
require_once __DIR__ . '/../../../includes/init.php';
|
||||
require_once __DIR__ . '/../models/productmodels.php';
|
||||
|
||||
ProductModel::setDb($pdo);
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$product = [
|
||||
'name' => trim($_POST['name']),
|
||||
'group' => trim($_POST['group']),
|
||||
'type' => trim($_POST['type']),
|
||||
'url' => trim($_POST['url']),
|
||||
'module' => trim($_POST['module']),
|
||||
'hidden' => isset($_POST['hidden']) ? 1 : 0
|
||||
];
|
||||
|
||||
$success = ProductModel::createProduct($product);
|
||||
|
||||
if ($success) {
|
||||
header("Location: dashboard.php?module=product&action=list&added=1");
|
||||
} else {
|
||||
header("Location: dashboard.php?module=product&action=create&error=1");
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
header("Location: dashboard.php?module=product&action=create");
|
||||
exit;
|
||||
@@ -0,0 +1,38 @@
|
||||
|
||||
|
||||
|
||||
<?php
|
||||
// modules/product/controllers/update.php
|
||||
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
|
||||
require_once __DIR__ . '/../../../includes/init.php';
|
||||
require_once __DIR__ . '/../models/productmodels.php';
|
||||
|
||||
ProductModel::setDb($pdo);
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['id'])) {
|
||||
$id = (int) $_POST['id'];
|
||||
$data = [
|
||||
'name' => trim($_POST['name']),
|
||||
'group' => trim($_POST['group']),
|
||||
'type' => trim($_POST['type']),
|
||||
'url' => trim($_POST['url']),
|
||||
'module' => trim($_POST['module']),
|
||||
'hidden' => isset($_POST['hidden']) ? 1 : 0,
|
||||
];
|
||||
|
||||
$success = ProductModel::updateProduct($id, $data);
|
||||
|
||||
if ($success) {
|
||||
header("Location: dashboard.php?module=product&action=edit&id=$id&updated=1");
|
||||
} else {
|
||||
header("Location: dashboard.php?module=product&action=edit&id=$id&updated=0");
|
||||
}
|
||||
exit;
|
||||
} else {
|
||||
header("Location: dashboard.php?module=product&action=list");
|
||||
exit;
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
class ProductModel
|
||||
{
|
||||
private static $db;
|
||||
|
||||
public static function setDb($pdo)
|
||||
{
|
||||
self::$db = $pdo;
|
||||
}
|
||||
|
||||
/**
|
||||
* პროდუქტის ტიპების მიღება (dynamic)
|
||||
*/
|
||||
public static function getProductTypes()
|
||||
{
|
||||
$stmt = self::$db->query("SELECT name, description, icon FROM product_types WHERE is_active = 1 ORDER BY sort_order, name");
|
||||
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
public static function deleteProductById($id)
|
||||
{
|
||||
if (!$id || !is_numeric($id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$stmt = self::$db->prepare("DELETE FROM products WHERE id = ?");
|
||||
return $stmt->execute([$id]);
|
||||
}
|
||||
|
||||
public static function getProductById($id)
|
||||
{
|
||||
if (!$id || !is_numeric($id)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$stmt = self::$db->prepare("SELECT * FROM products WHERE id = ?");
|
||||
$stmt->execute([$id]);
|
||||
return $stmt->fetch();
|
||||
}
|
||||
|
||||
public static function getAllProducts()
|
||||
{
|
||||
$stmt = self::$db->query("SELECT * FROM products ORDER BY id DESC");
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
|
||||
public static function createProduct($data)
|
||||
{
|
||||
$stmt = self::$db->prepare("INSERT INTO products (name, `group`, type, url, module, hidden) VALUES (?, ?, ?, ?, ?, ?)");
|
||||
return $stmt->execute([
|
||||
$data['name'],
|
||||
$data['group'],
|
||||
$data['type'],
|
||||
$data['url'],
|
||||
$data['module'],
|
||||
$data['hidden']
|
||||
]);
|
||||
}
|
||||
|
||||
public static function updateProduct($id, $data)
|
||||
{
|
||||
$stmt = self::$db->prepare("
|
||||
UPDATE products
|
||||
SET name = ?, `group` = ?, type = ?, url = ?, module = ?, hidden = ?, updated_at = NOW()
|
||||
WHERE id = ?
|
||||
");
|
||||
return $stmt->execute([
|
||||
$data['name'],
|
||||
$data['group'],
|
||||
$data['type'],
|
||||
$data['url'],
|
||||
$data['module'],
|
||||
$data['hidden'],
|
||||
$id
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
|
||||
class ProductTypesModel
|
||||
{
|
||||
protected static $db;
|
||||
|
||||
public static function setDb($pdo)
|
||||
{
|
||||
self::$db = $pdo;
|
||||
}
|
||||
|
||||
/**
|
||||
* ყველა აქტიური product type-ის მიღება
|
||||
*/
|
||||
public static function getActiveTypes()
|
||||
{
|
||||
$stmt = self::$db->query("SELECT * FROM product_types WHERE is_active = 1 ORDER BY sort_order, name");
|
||||
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
/**
|
||||
* ყველა product type-ის მიღება (ადმინისთვის)
|
||||
*/
|
||||
public static function getAllTypes()
|
||||
{
|
||||
$stmt = self::$db->query("SELECT * FROM product_types ORDER BY sort_order, name");
|
||||
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
/**
|
||||
* კონკრეტული product type-ის მიღება
|
||||
*/
|
||||
public static function getTypeById($id)
|
||||
{
|
||||
$stmt = self::$db->prepare("SELECT * FROM product_types WHERE id = ?");
|
||||
$stmt->execute([$id]);
|
||||
return $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
/**
|
||||
* ახალი product type-ის დამატება
|
||||
*/
|
||||
public static function addType($data)
|
||||
{
|
||||
$stmt = self::$db->prepare("
|
||||
INSERT INTO product_types (name, description, icon, sort_order, is_active)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
");
|
||||
|
||||
return $stmt->execute([
|
||||
$data['name'],
|
||||
$data['description'] ?? '',
|
||||
$data['icon'] ?? 'box',
|
||||
$data['sort_order'] ?? 0,
|
||||
$data['is_active'] ?? 1
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Product type-ის განახლება
|
||||
*/
|
||||
public static function updateType($id, $data)
|
||||
{
|
||||
$stmt = self::$db->prepare("
|
||||
UPDATE product_types
|
||||
SET name = ?, description = ?, icon = ?, sort_order = ?, is_active = ?, updated_at = NOW()
|
||||
WHERE id = ?
|
||||
");
|
||||
|
||||
return $stmt->execute([
|
||||
$data['name'],
|
||||
$data['description'] ?? '',
|
||||
$data['icon'] ?? 'box',
|
||||
$data['sort_order'] ?? 0,
|
||||
$data['is_active'] ?? 1,
|
||||
$id
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Product type-ის წაშლა
|
||||
*/
|
||||
public static function deleteType($id)
|
||||
{
|
||||
// ვამოწმებთ არ არის თუ არა გამოყენებული პროდუქტებში
|
||||
$stmt = self::$db->prepare("SELECT COUNT(*) FROM products WHERE type = (SELECT name FROM product_types WHERE id = ?)");
|
||||
$stmt->execute([$id]);
|
||||
$count = $stmt->fetchColumn();
|
||||
|
||||
if ($count > 0) {
|
||||
throw new Exception("ამ ტიპის პროდუქტები უკვე არსებობს. წაშლა შეუძლებელია.");
|
||||
}
|
||||
|
||||
$stmt = self::$db->prepare("DELETE FROM product_types WHERE id = ?");
|
||||
return $stmt->execute([$id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort order-ის განახლება
|
||||
*/
|
||||
public static function updateSortOrder($orders)
|
||||
{
|
||||
try {
|
||||
self::$db->beginTransaction();
|
||||
|
||||
$stmt = self::$db->prepare("UPDATE product_types SET sort_order = ? WHERE id = ?");
|
||||
|
||||
foreach ($orders as $id => $order) {
|
||||
$stmt->execute([$order, $id]);
|
||||
}
|
||||
|
||||
self::$db->commit();
|
||||
return true;
|
||||
} catch (Exception $e) {
|
||||
self::$db->rollback();
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ხელმისაწვდომი აიკონების სია
|
||||
*/
|
||||
public static function getAvailableIcons()
|
||||
{
|
||||
return [
|
||||
'server' => 'სერვერი',
|
||||
'server-2' => 'VPS/Dedicated',
|
||||
'users' => 'მომხმარებლები',
|
||||
'world' => 'დომენი',
|
||||
'shield-check' => 'უსაფრთხოება',
|
||||
'mail' => 'ელ.ფოსტა',
|
||||
'cloud' => 'ღრუბელი',
|
||||
'database' => 'ბაზა',
|
||||
'box' => 'პაკეტი',
|
||||
'gift' => 'საჩუქარი',
|
||||
'star' => 'პრემიუმი',
|
||||
'lightning' => 'სწრაფი',
|
||||
'globe' => 'გლობალური',
|
||||
'lock' => 'დაცული',
|
||||
'dots' => 'სხვა'
|
||||
];
|
||||
}
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,82 @@
|
||||
<div class="container-xl mt-4">
|
||||
<h2 class="mb-4">პროდუქტის რედაქტირება: <?= htmlspecialchars($product['name']) ?></h2>
|
||||
|
||||
<div class="tab-content pt-3">
|
||||
<div class="tab-pane active" id="details">
|
||||
<form method="POST" action="dashboard.php?module=product&action=update">
|
||||
<input type="hidden" name="id" value="<?= $product['id'] ?>">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Product Type</label>
|
||||
<input type="text" name="type" class="form-control" value="<?= htmlspecialchars($product['type']) ?>">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Product Group</label>
|
||||
<input type="text" name="group" class="form-control" value="<?= htmlspecialchars($product['group']) ?>">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Product Name</label>
|
||||
<input type="text" name="name" class="form-control" value="<?= htmlspecialchars($product['name']) ?>" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Product Tagline</label>
|
||||
<input type="text" name="tagline" class="form-control" value="<?= htmlspecialchars($product['tagline'] ?? '') ?>">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">URL</label>
|
||||
<input type="text" name="url" class="form-control" value="<?= htmlspecialchars($product['url']) ?>">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Short Description</label>
|
||||
<textarea name="short_description" class="form-control"><?= htmlspecialchars($product['short_description'] ?? '') ?></textarea>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Description</label>
|
||||
<textarea name="description" class="form-control" rows="4"><?= htmlspecialchars($product['description'] ?? '') ?></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Product Color</label>
|
||||
<input type="color" name="color" class="form-control form-control-color" value="<?= htmlspecialchars($product['color'] ?? '#ffffff') ?>">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Welcome Email</label>
|
||||
<input type="text" name="welcome_email" class="form-control" value="<?= htmlspecialchars($product['welcome_email'] ?? '') ?>">
|
||||
</div>
|
||||
<div class="form-check mb-2">
|
||||
<input class="form-check-input" type="checkbox" name="require_domain" value="1" id="requireDomain" <?= !empty($product['require_domain']) ? 'checked' : '' ?>>
|
||||
<label class="form-check-label" for="requireDomain">Check to show domain registration options</label>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Stock Quantity</label>
|
||||
<input type="number" name="stock_quantity" class="form-control" value="<?= htmlspecialchars($product['stock_quantity'] ?? 0) ?>">
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="apply_tax" value="1" id="applyTax" <?= !empty($product['apply_tax']) ? 'checked' : '' ?>>
|
||||
<label class="form-check-label" for="applyTax">Check to charge tax for this product</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="featured" value="1" id="featured" <?= !empty($product['featured']) ? 'checked' : '' ?>>
|
||||
<label class="form-check-label" for="featured">Display this product more prominently</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="hidden" value="1" id="hiddenSwitch" <?= !empty($product['hidden']) ? 'checked' : '' ?>>
|
||||
<label class="form-check-label" for="hiddenSwitch">Check to hide from order form</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="retired" value="1" id="retired" <?= !empty($product['retired']) ? 'checked' : '' ?>>
|
||||
<label class="form-check-label" for="retired">Check to hide from dropdown menus</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
<button type="submit" class="btn btn-primary">შენახვა</button>
|
||||
<a href="dashboard.php?module=product&action=list" class="btn btn-secondary">უკან</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
$pageTitle = "პროდუქტის რედაქტირება";
|
||||
$tab = $_GET['tab'] ?? 'details';
|
||||
|
||||
include '../../layout/header.php';
|
||||
?>
|
||||
<div class="page-body">
|
||||
<div class="container-xl">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<?php if ($tab === 'details') : ?>
|
||||
<?php include 'tabs/details.php'; ?>
|
||||
<?php elseif ($tab === 'pricing') : ?>
|
||||
<h3>Pricing</h3>
|
||||
<form>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Payment Type</label>
|
||||
<div>
|
||||
<label class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="radio" name="payment_type" value="free"> Free
|
||||
</label>
|
||||
<label class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="radio" name="payment_type" value="onetime"> One Time
|
||||
</label>
|
||||
<label class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="radio" name="payment_type" value="recurring" checked> Recurring
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Currency</th>
|
||||
<th>Setup Fee</th>
|
||||
<th>Price</th>
|
||||
<th>Enable</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>GEL</td>
|
||||
<td><input type="text" class="form-control" value="0.00"></td>
|
||||
<td><input type="text" class="form-control" value="70.00"></td>
|
||||
<td><input type="checkbox" checked></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>USD</td>
|
||||
<td><input type="text" class="form-control" value="0.00"></td>
|
||||
<td><input type="text" class="form-control" value="29.00"></td>
|
||||
<td><input type="checkbox" checked></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php include '../../layout/footer.php'; ?>
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/includes/init.php';
|
||||
|
||||
?>
|
||||
|
||||
<?php
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
use App\Config;
|
||||
|
||||
require_once Config::includePath('head.php');
|
||||
require_once Config::includePath('navbar.php');
|
||||
require_once Config::includePath('pageheader.php');
|
||||
require_once Config::includePath('pagebodystart.php');
|
||||
?>
|
||||
|
||||
|
||||
<?php
|
||||
|
||||
require 'db.php';
|
||||
|
||||
$action = $_GET['action'] ?? 'list';
|
||||
$id = $_GET['id'] ?? null;
|
||||
|
||||
switch ($action) {
|
||||
case 'create':
|
||||
include __DIR__ . '/product/create.php';
|
||||
break;
|
||||
|
||||
case 'edit':
|
||||
if ($id) {
|
||||
include __DIR__ . '/product/edit.php';
|
||||
} else {
|
||||
echo "<div class='container-xl mt-4'><div class='alert alert-danger'>ID ვერ მოიძებნა.</div></div>";
|
||||
}
|
||||
break;
|
||||
|
||||
case 'list':
|
||||
default:
|
||||
include __DIR__ . '/product/list.php';
|
||||
break;
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<?php require_once Config::includePath('footer.php'); ?>
|
||||
@@ -0,0 +1,122 @@
|
||||
<?php
|
||||
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
|
||||
require_once __DIR__ . '/../../../includes/init.php';
|
||||
require_once __DIR__ . '/../models/productmodels.php';
|
||||
use App\Config;
|
||||
|
||||
// Product types-ების მიღება
|
||||
ProductModel::setDb($pdo);
|
||||
$productTypes = ProductModel::getProductTypes();
|
||||
?>
|
||||
|
||||
<?php require_once Config::includePath('head.php'); ?>
|
||||
<?php require_once Config::includePath('navbar.php'); ?>
|
||||
<?php require_once Config::includePath('pageheader.php'); ?>
|
||||
<?php require_once Config::includePath('pagebodystart.php'); ?>
|
||||
|
||||
|
||||
<div class="container-xl mt-4">
|
||||
<h2 class="mb-4">ახალი პროდუქტის დამატება</h2>
|
||||
<form method="POST" action="dashboard.php?module=product&action=save">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Product Type</label>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<select name="type" class="form-select" id="product-type-select" required>
|
||||
<option value="">აირჩიეთ ტიპი...</option>
|
||||
<?php foreach ($productTypes as $type): ?>
|
||||
<option value="<?= htmlspecialchars($type['name']) ?>" data-icon="<?= htmlspecialchars($type['icon']) ?>">
|
||||
<?= htmlspecialchars($type['name']) ?>
|
||||
<?php if ($type['description']): ?>
|
||||
- <?= htmlspecialchars($type['description']) ?>
|
||||
<?php endif; ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<a href="dashboard.php?module=product&action=types" class="btn btn-outline-secondary" title="ტიპების მართვა">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon">
|
||||
<path d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 0 0 2.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 0 0 1.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 0 0-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 0 0-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 0 0-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 0 0-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 0 0 1.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"/>
|
||||
<path d="M9 12a3 3 0 1 0 6 0a3 3 0 0 0 -6 0"/>
|
||||
</svg>
|
||||
მართვა
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-2" id="type-preview" style="display: none;">
|
||||
<div class="d-flex align-items-center text-muted">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon me-2" id="type-icon">
|
||||
</svg>
|
||||
<span id="type-description"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Product Group</label>
|
||||
<input type="text" name="group" class="form-control" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">დასახელება</label>
|
||||
<input type="text" name="name" class="form-control" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">URL</label>
|
||||
<input type="text" name="url" class="form-control">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">მოდული</label>
|
||||
<input type="text" name="module" class="form-control">
|
||||
</div>
|
||||
<div class="form-check form-switch mb-3">
|
||||
<input class="form-check-input" type="checkbox" name="hidden" value="1" id="hiddenSwitch">
|
||||
<label class="form-check-label" for="hiddenSwitch">შექმნა დამალულად</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-footer mt-3">
|
||||
<button type="submit" class="btn btn-primary">შენახვა</button>
|
||||
<a href="dashboard.php?module=product&action=list" class="btn btn-secondary">გაუქმება</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Product type selection preview
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const typeSelect = document.getElementById('product-type-select');
|
||||
const typePreview = document.getElementById('type-preview');
|
||||
const typeIcon = document.getElementById('type-icon');
|
||||
const typeDescription = document.getElementById('type-description');
|
||||
|
||||
typeSelect.addEventListener('change', function() {
|
||||
const selectedOption = this.options[this.selectedIndex];
|
||||
|
||||
if (this.value) {
|
||||
const iconName = selectedOption.getAttribute('data-icon') || 'box';
|
||||
const description = selectedOption.text.split(' - ')[1] || '';
|
||||
|
||||
// Update icon
|
||||
typeIcon.className = `icon me-2 icon-tabler icons-tabler-outline icon-tabler-${iconName}`;
|
||||
|
||||
// Update description
|
||||
if (description) {
|
||||
typeDescription.textContent = description;
|
||||
typePreview.style.display = 'block';
|
||||
} else {
|
||||
typePreview.style.display = 'none';
|
||||
}
|
||||
} else {
|
||||
typePreview.style.display = 'none';
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<?php require_once Config::includePath('footer.php'); ?>
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
|
||||
require_once __DIR__ . '/../../../includes/init.php';
|
||||
require_once __DIR__ . '/../models/productmodels.php';
|
||||
|
||||
use App\Config;
|
||||
?>
|
||||
|
||||
<?php require_once Config::includePath('head.php'); ?>
|
||||
<?php require_once Config::includePath('navbar.php'); ?>
|
||||
<?php require_once Config::includePath('pageheader.php'); ?>
|
||||
<?php require_once Config::includePath('pagebodystart.php'); ?>
|
||||
|
||||
<div class="tab-content mt-4">
|
||||
<?php
|
||||
$allowedTabs = ['details', 'pricing'];
|
||||
if (!in_array($tab, $allowedTabs)) {
|
||||
$tab = 'details';
|
||||
}
|
||||
|
||||
$tabPath = __DIR__ . '/../tabs/' . $tab . '.php';
|
||||
|
||||
if (file_exists($tabPath)) {
|
||||
include $tabPath;
|
||||
} else {
|
||||
echo "<div class='alert alert-danger'>ტაბის ფაილი ვერ მოიძებნა.</div>";
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
|
||||
require_once __DIR__ . '/../../../includes/init.php';
|
||||
require_once __DIR__ . '/../models/productmodels.php';
|
||||
|
||||
use App\Config;
|
||||
?>
|
||||
|
||||
<?php require_once Config::includePath('head.php'); ?>
|
||||
<?php require_once Config::includePath('navbar.php'); ?>
|
||||
<?php require_once Config::includePath('pageheader.php'); ?>
|
||||
<?php require_once Config::includePath('pagebodystart.php'); ?>
|
||||
|
||||
<?php if (isset($_GET['added']) && $_GET['added'] == 1): ?>
|
||||
<div class="alert alert-success">პროდუქტი წარმატებით დაემატა.</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="container-xl mt-4">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h2>პროდუქტები და სერვისები</h2>
|
||||
<div class="btn-list">
|
||||
<a href="dashboard.php?module=product&action=types" class="btn btn-outline-secondary">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon">
|
||||
<rect x="3" y="3" width="7" height="7"/>
|
||||
<rect x="14" y="3" width="7" height="7"/>
|
||||
<rect x="14" y="14" width="7" height="7"/>
|
||||
<rect x="3" y="14" width="7" height="7"/>
|
||||
</svg>
|
||||
ტიპების მართვა
|
||||
</a>
|
||||
<a href="dashboard.php?module=product&action=create" class="btn btn-primary">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon">
|
||||
<path d="M12 5l0 14"/>
|
||||
<path d="M5 12l14 0"/>
|
||||
</svg>
|
||||
ახალი პროდუქტი
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Product Name</th>
|
||||
<th>Group</th>
|
||||
<th>Type</th>
|
||||
<th>Pay Type</th>
|
||||
<th>Auto Setup</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($products as $product): ?>
|
||||
<tr>
|
||||
<td><?= htmlspecialchars($product['id'] ?? '') ?></td>
|
||||
<td><?= htmlspecialchars($product['name'] ?? '') ?></td>
|
||||
<td><?= htmlspecialchars($product['group'] ?? '') ?></td>
|
||||
<td><?= htmlspecialchars($product['type'] ?? '') ?></td>
|
||||
<td><?= htmlspecialchars($product['pay_type'] ?? '') ?></td>
|
||||
<td><?= htmlspecialchars($product['auto_setup'] ?? '') ?></td>
|
||||
<td>
|
||||
<a href="dashboard.php?module=product&action=edit&id=<?= $product['id'] ?>" class="btn btn-sm btn-warning">რედ.</a>
|
||||
<a href="dashboard.php?module=product&action=delete&id=<?= $product['id'] ?>" class="btn btn-sm btn-danger" onclick="return confirm('წაშლა?');">წაშლა</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<?php require_once Config::includePath('footer.php'); ?>
|
||||
@@ -0,0 +1,313 @@
|
||||
<?php
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
|
||||
require_once __DIR__ . '/../../../includes/init.php';
|
||||
require_once __DIR__ . '/../../../../vendor/autoload.php';
|
||||
use App\Config;
|
||||
?>
|
||||
|
||||
<?php require_once Config::includePath('head.php'); ?>
|
||||
<?php require_once Config::includePath('navbar.php'); ?>
|
||||
<?php require_once Config::includePath('pageheader.php'); ?>
|
||||
<?php require_once Config::includePath('pagebodystart.php'); ?>
|
||||
|
||||
<div class="container-xl mt-4">
|
||||
<!-- Page header -->
|
||||
<div class="page-header d-print-none">
|
||||
<div class="row align-items-center">
|
||||
<div class="col">
|
||||
<h2 class="page-title">პროდუქტის ტიპების მართვა</h2>
|
||||
<div class="text-muted mt-1">პროდუქტების კატეგორიების კონფიგურაცია</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<div class="btn-list">
|
||||
<?php if ($action === 'list'): ?>
|
||||
<a href="dashboard.php?module=product&action=types&subaction=add" class="btn btn-primary">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon">
|
||||
<path d="M12 5l0 14"/>
|
||||
<path d="M5 12l14 0"/>
|
||||
</svg>
|
||||
ახალი ტიპი
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
<a href="dashboard.php?module=product&action=list" class="btn btn-outline-primary">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon">
|
||||
<path d="M5 12l14 0"/>
|
||||
<path d="M5 12l6 6"/>
|
||||
<path d="M5 12l6 -6"/>
|
||||
</svg>
|
||||
უკან დაბრუნება
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Messages -->
|
||||
<?php if ($message): ?>
|
||||
<div class="alert alert-<?= $messageType ?> alert-dismissible">
|
||||
<div class="d-flex">
|
||||
<div><?= htmlspecialchars($message) ?></div>
|
||||
</div>
|
||||
<a class="btn-close" data-bs-dismiss="alert" aria-label="close"></a>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($action === 'list'): ?>
|
||||
<!-- Product Types List -->
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">პროდუქტის ტიპები</h3>
|
||||
<div class="card-actions">
|
||||
<small class="text-muted">გადაათრიე რიგითობის ცვლილებისთვის</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<?php if (empty($productTypes)): ?>
|
||||
<div class="text-center text-muted py-4">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon mb-3">
|
||||
<path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/>
|
||||
<polyline points="3.27,6.96 12,12.01 20.73,6.96"/>
|
||||
<line x1="12" y1="22.08" x2="12" y2="12"/>
|
||||
</svg>
|
||||
<h3>პროდუქტის ტიპები არ არის</h3>
|
||||
<p class="text-muted">დაამატეთ ახალი პროდუქტის ტიპი</p>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div id="sortable-types" class="list-group list-group-flush">
|
||||
<?php foreach ($productTypes as $type): ?>
|
||||
<div class="list-group-item" data-id="<?= $type['id'] ?>">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-auto">
|
||||
<span class="handle cursor-move">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon text-muted">
|
||||
<line x1="8" y1="6" x2="21" y2="6"/>
|
||||
<line x1="8" y1="12" x2="21" y2="12"/>
|
||||
<line x1="8" y1="18" x2="21" y2="18"/>
|
||||
<line x1="3" y1="6" x2="3.01" y2="6"/>
|
||||
<line x1="3" y1="12" x2="3.01" y2="12"/>
|
||||
<line x1="3" y1="18" x2="3.01" y2="18"/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-<?= htmlspecialchars($type['icon']) ?>">
|
||||
<!-- Icon will be rendered by Tabler -->
|
||||
</svg>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="d-flex align-items-center">
|
||||
<strong><?= htmlspecialchars($type['name']) ?></strong>
|
||||
<?php if (!$type['is_active']): ?>
|
||||
<span class="badge bg-secondary ms-2">უაქტივო</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php if ($type['description']): ?>
|
||||
<div class="text-muted small"><?= htmlspecialchars($type['description']) ?></div>
|
||||
<?php endif; ?>
|
||||
<small class="text-muted">რიგითობა: <?= $type['sort_order'] ?></small>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<div class="btn-list">
|
||||
<a href="dashboard.php?module=product&action=types&subaction=edit&id=<?= $type['id'] ?>" class="btn btn-sm btn-outline-primary">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon">
|
||||
<path d="M7 7h-1a2 2 0 0 0 -2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2 -2v-1"/>
|
||||
<path d="M20.385 6.585a2.1 2.1 0 0 0 -2.97 -2.97l-8.415 8.385v3h3l8.385 -8.415z"/>
|
||||
<path d="M16 5l3 3"/>
|
||||
</svg>
|
||||
რედაქტირება
|
||||
</a>
|
||||
<form method="post" style="display: inline;">
|
||||
<input type="hidden" name="action" value="delete">
|
||||
<input type="hidden" name="id" value="<?= $type['id'] ?>">
|
||||
<button type="submit" class="btn btn-sm btn-outline-danger" onclick="return confirm('დარწმუნებული ხართ რომ გსურთ ამ ტიპის წაშლა?')">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon">
|
||||
<path d="M4 7l16 0"/>
|
||||
<path d="M10 11l0 6"/>
|
||||
<path d="M14 11l0 6"/>
|
||||
<path d="M5 7l1 12a2 2 0 0 0 2 2h8a2 2 0 0 0 2 -2l1 -12"/>
|
||||
<path d="M9 7v-3a1 1 0 0 1 1 -1h4a1 1 0 0 1 1 1v3"/>
|
||||
</svg>
|
||||
წაშლა
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
|
||||
<form id="sort-form" method="post" style="display: none;">
|
||||
<input type="hidden" name="action" value="update_order">
|
||||
<div id="sort-orders"></div>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php elseif ($action === 'add' || $action === 'edit'): ?>
|
||||
<!-- Add/Edit Form -->
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">
|
||||
<?= $action === 'add' ? 'ახალი პროდუქტის ტიპი' : 'პროდუქტის ტიპის რედაქტირება' ?>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="post">
|
||||
<input type="hidden" name="action" value="<?= $action ?>">
|
||||
<?php if ($action === 'edit'): ?>
|
||||
<input type="hidden" name="id" value="<?= $editType['id'] ?>">
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">ტიპის სახელი</label>
|
||||
<input type="text" name="name" class="form-control"
|
||||
value="<?= htmlspecialchars($editType['name'] ?? '') ?>"
|
||||
placeholder="მაგ: Shared Hosting" required>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">აღწერა</label>
|
||||
<textarea name="description" class="form-control" rows="3"
|
||||
placeholder="ტიპის დეტალური აღწერა"><?= htmlspecialchars($editType['description'] ?? '') ?></textarea>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">რიგითობა</label>
|
||||
<input type="number" name="sort_order" class="form-control"
|
||||
value="<?= $editType['sort_order'] ?? '0' ?>"
|
||||
placeholder="0">
|
||||
<small class="form-hint">უფრო პატარა რიცხვი = უფრო ზევით</small>
|
||||
</div>
|
||||
|
||||
<div class="form-check form-switch mb-3">
|
||||
<input class="form-check-input" type="checkbox" name="is_active"
|
||||
<?= ($editType['is_active'] ?? 1) ? 'checked' : '' ?>>
|
||||
<label class="form-check-label">აქტიური</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">აიკონი</label>
|
||||
<select name="icon" class="form-select" id="icon-select">
|
||||
<?php foreach ($availableIcons as $iconName => $iconLabel): ?>
|
||||
<option value="<?= $iconName ?>"
|
||||
<?= ($editType['icon'] ?? 'box') === $iconName ? 'selected' : '' ?>>
|
||||
<?= $iconLabel ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">აიკონის გადახედვა</label>
|
||||
<div class="icon-preview p-4 border rounded text-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline" id="preview-icon">
|
||||
<!-- Icon preview -->
|
||||
</svg>
|
||||
<div class="mt-2 text-muted" id="preview-text">აიკონის გადახედვა</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-actions">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon">
|
||||
<path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"/>
|
||||
<polyline points="17,21 17,13 7,13 7,21"/>
|
||||
<polyline points="7,3 7,8 15,8"/>
|
||||
</svg>
|
||||
შენახვა
|
||||
</button>
|
||||
<a href="dashboard.php?module=product&action=types" class="btn btn-secondary">გაუქმება</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<script src="../dist/js/tabler.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/sortablejs@1.15.0/Sortable.min.js"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Sortable functionality
|
||||
const sortableEl = document.getElementById('sortable-types');
|
||||
if (sortableEl) {
|
||||
const sortable = Sortable.create(sortableEl, {
|
||||
handle: '.handle',
|
||||
animation: 150,
|
||||
onEnd: function() {
|
||||
const items = sortableEl.querySelectorAll('.list-group-item');
|
||||
const orders = {};
|
||||
|
||||
items.forEach((item, index) => {
|
||||
const id = item.getAttribute('data-id');
|
||||
orders[id] = index + 1;
|
||||
});
|
||||
|
||||
// Update hidden form
|
||||
const sortOrdersDiv = document.getElementById('sort-orders');
|
||||
sortOrdersDiv.innerHTML = '';
|
||||
|
||||
Object.keys(orders).forEach(id => {
|
||||
const input = document.createElement('input');
|
||||
input.type = 'hidden';
|
||||
input.name = `orders[${id}]`;
|
||||
input.value = orders[id];
|
||||
sortOrdersDiv.appendChild(input);
|
||||
});
|
||||
|
||||
// Submit form
|
||||
document.getElementById('sort-form').submit();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Icon preview
|
||||
const iconSelect = document.getElementById('icon-select');
|
||||
const previewIcon = document.getElementById('preview-icon');
|
||||
const previewText = document.getElementById('preview-text');
|
||||
|
||||
if (iconSelect && previewIcon) {
|
||||
function updateIconPreview() {
|
||||
const selectedIcon = iconSelect.value;
|
||||
const selectedText = iconSelect.options[iconSelect.selectedIndex].text;
|
||||
|
||||
// Update icon class
|
||||
previewIcon.className = `icon icon-tabler icons-tabler-outline icon-tabler-${selectedIcon}`;
|
||||
previewText.textContent = selectedText;
|
||||
}
|
||||
|
||||
// Initialize preview
|
||||
updateIconPreview();
|
||||
|
||||
// Update on change
|
||||
iconSelect.addEventListener('change', updateIconPreview);
|
||||
}
|
||||
|
||||
// Form validation
|
||||
const form = document.querySelector('form[method="post"]');
|
||||
if (form && form.querySelector('input[name="name"]')) {
|
||||
form.addEventListener('submit', function(e) {
|
||||
const nameInput = form.querySelector('input[name="name"]');
|
||||
if (!nameInput.value.trim()) {
|
||||
alert('ტიპის სახელი სავალდებულოა');
|
||||
e.preventDefault();
|
||||
nameInput.focus();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<?php require_once Config::includePath('footer.php'); ?>
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
|
||||
require_once __DIR__ . '/../../../includes/init.php';
|
||||
require_once __DIR__ . '/../models/smtpmodel.php';
|
||||
|
||||
SMTPModel::setDb($pdo);
|
||||
|
||||
$message = '';
|
||||
$messageType = '';
|
||||
|
||||
// SMTP settings-ების განახლება
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$action = $_POST['action'] ?? '';
|
||||
|
||||
if ($action === 'update_settings') {
|
||||
$settings = [
|
||||
'smtp_host' => trim($_POST['smtp_host'] ?? ''),
|
||||
'smtp_port' => trim($_POST['smtp_port'] ?? '587'),
|
||||
'smtp_secure' => $_POST['smtp_secure'] ?? 'tls',
|
||||
'smtp_auth' => isset($_POST['smtp_auth']) ? '1' : '0',
|
||||
'smtp_username' => trim($_POST['smtp_username'] ?? ''),
|
||||
'smtp_password' => $_POST['smtp_password'] ?? '',
|
||||
'smtp_from_email' => trim($_POST['smtp_from_email'] ?? ''),
|
||||
'smtp_from_name' => trim($_POST['smtp_from_name'] ?? ''),
|
||||
'smtp_debug' => $_POST['smtp_debug'] ?? '0'
|
||||
];
|
||||
|
||||
try {
|
||||
SMTPModel::updateMultipleSettings($settings);
|
||||
$message = 'SMTP სეტინგები წარმატებით განახლდა!';
|
||||
$messageType = 'success';
|
||||
} catch (Exception $e) {
|
||||
$message = 'შეცდომა: ' . $e->getMessage();
|
||||
$messageType = 'danger';
|
||||
}
|
||||
}
|
||||
|
||||
// SMTP კავშირის ტესტი
|
||||
elseif ($action === 'test_connection') {
|
||||
$testEmail = trim($_POST['test_email'] ?? '');
|
||||
|
||||
if (empty($testEmail)) {
|
||||
$result = SMTPModel::testSmtpConnection();
|
||||
} else {
|
||||
$result = SMTPModel::testSmtpConnection($testEmail);
|
||||
}
|
||||
|
||||
$message = $result['message'];
|
||||
$messageType = $result['success'] ? 'success' : 'danger';
|
||||
}
|
||||
}
|
||||
|
||||
// მიმდინარე settings-ების მიღება
|
||||
$settings = SMTPModel::getSmtpConfig();
|
||||
|
||||
require_once __DIR__ . '/../views/smtp_settings.php';
|
||||
?>
|
||||
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
|
||||
class SMTPModel
|
||||
{
|
||||
protected static $db;
|
||||
|
||||
public static function setDb($pdo)
|
||||
{
|
||||
self::$db = $pdo;
|
||||
}
|
||||
|
||||
/**
|
||||
* ყველა SMTP setting-ის მიღება
|
||||
*/
|
||||
public static function getAllSettings()
|
||||
{
|
||||
$stmt = self::$db->query("SELECT * FROM smtp_settings ORDER BY id");
|
||||
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
/**
|
||||
* კონკრეტული setting-ის მიღება
|
||||
*/
|
||||
public static function getSetting($key)
|
||||
{
|
||||
$stmt = self::$db->prepare("SELECT setting_value FROM smtp_settings WHERE setting_key = ?");
|
||||
$stmt->execute([$key]);
|
||||
$result = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
return $result ? $result['setting_value'] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setting-ის განახლება
|
||||
*/
|
||||
public static function updateSetting($key, $value)
|
||||
{
|
||||
$stmt = self::$db->prepare("UPDATE smtp_settings SET setting_value = ?, updated_at = NOW() WHERE setting_key = ?");
|
||||
return $stmt->execute([$value, $key]);
|
||||
}
|
||||
|
||||
/**
|
||||
* ყველა setting-ის განახლება ერთდროულად
|
||||
*/
|
||||
public static function updateMultipleSettings($settings)
|
||||
{
|
||||
try {
|
||||
self::$db->beginTransaction();
|
||||
|
||||
$stmt = self::$db->prepare("UPDATE smtp_settings SET setting_value = ?, updated_at = NOW() WHERE setting_key = ?");
|
||||
|
||||
foreach ($settings as $key => $value) {
|
||||
$stmt->execute([$value, $key]);
|
||||
}
|
||||
|
||||
self::$db->commit();
|
||||
return true;
|
||||
} catch (Exception $e) {
|
||||
self::$db->rollback();
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* SMTP კონფიგურაციის ასოციაციური მასივის მიღება
|
||||
*/
|
||||
public static function getSmtpConfig()
|
||||
{
|
||||
$settings = self::getAllSettings();
|
||||
$config = [];
|
||||
|
||||
foreach ($settings as $setting) {
|
||||
$config[$setting['setting_key']] = $setting['setting_value'];
|
||||
}
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* SMTP კავშირის ტესტი
|
||||
*/
|
||||
public static function testSmtpConnection($testEmail = null)
|
||||
{
|
||||
require_once __DIR__ . '/../../../libs/phpmailer/src/PHPMailer.php';
|
||||
require_once __DIR__ . '/../../../libs/phpmailer/src/SMTP.php';
|
||||
require_once __DIR__ . '/../../../libs/phpmailer/src/Exception.php';
|
||||
|
||||
$config = self::getSmtpConfig();
|
||||
$mail = new \PHPMailer\PHPMailer\PHPMailer(true);
|
||||
|
||||
try {
|
||||
$mail->isSMTP();
|
||||
$mail->Host = $config['smtp_host'] ?? 'localhost';
|
||||
$mail->SMTPAuth = (bool)($config['smtp_auth'] ?? false);
|
||||
$mail->Username = $config['smtp_username'] ?? '';
|
||||
$mail->Password = $config['smtp_password'] ?? '';
|
||||
$mail->SMTPSecure = $config['smtp_secure'] ?? 'tls';
|
||||
$mail->Port = (int)($config['smtp_port'] ?? 587);
|
||||
$mail->SMTPDebug = 0; // No debug output for test
|
||||
|
||||
$mail->setFrom(
|
||||
$config['smtp_from_email'] ?? 'test@example.com',
|
||||
$config['smtp_from_name'] ?? 'Test'
|
||||
);
|
||||
|
||||
if ($testEmail) {
|
||||
$mail->addAddress($testEmail);
|
||||
$mail->CharSet = 'UTF-8';
|
||||
$mail->isHTML(true);
|
||||
$mail->Subject = 'SMTP კონფიგურაციის ტესტი';
|
||||
$mail->Body = 'ეს არის SMTP კონფიგურაციის ტესტური შეტყობინება. თუ ეს წერილი მიიღეთ, SMTP სეტინგები სწორია.';
|
||||
|
||||
$result = $mail->send();
|
||||
return [
|
||||
'success' => true,
|
||||
'message' => 'ტესტური ელ.წერილი წარმატებით გაიგზავნა ' . $testEmail . ' მისამართზე'
|
||||
];
|
||||
} else {
|
||||
// მხოლოდ კავშირის ტესტი ელ.წერილის გაგზავნის გარეშე
|
||||
$mail->smtpConnect();
|
||||
$mail->smtpClose();
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'message' => 'SMTP კავშირი წარმატებით დამყარდა'
|
||||
];
|
||||
}
|
||||
|
||||
} catch (\PHPMailer\PHPMailer\Exception $e) {
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => 'SMTP შეცდომა: ' . $e->getMessage()
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,295 @@
|
||||
<?php
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
|
||||
require_once __DIR__ . '/../../../includes/init.php';
|
||||
require_once __DIR__ . '/../../../../vendor/autoload.php';
|
||||
use App\Config;
|
||||
?>
|
||||
|
||||
<?php require_once Config::includePath('head.php'); ?>
|
||||
<?php require_once Config::includePath('navbar.php'); ?>
|
||||
<?php require_once Config::includePath('pageheader.php'); ?>
|
||||
<?php require_once Config::includePath('pagebodystart.php'); ?>
|
||||
|
||||
<div class="container-xl mt-4">
|
||||
<!-- Page header -->
|
||||
<div class="page-header d-print-none">
|
||||
<div class="row align-items-center">
|
||||
<div class="col">
|
||||
<h2 class="page-title">SMTP სეტინგები</h2>
|
||||
<div class="text-muted mt-1">ელ.ფოსტის სერვერის კონფიგურაცია</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<a href="../dashboard.php" class="btn btn-outline-primary">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-arrow-left">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
||||
<path d="M5 12l14 0"/>
|
||||
<path d="M5 12l6 6"/>
|
||||
<path d="M5 12l6 -6"/>
|
||||
</svg>
|
||||
უკან დაბრუნება
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Messages -->
|
||||
<?php if ($message): ?>
|
||||
<div class="alert alert-<?= $messageType ?> alert-dismissible">
|
||||
<div class="d-flex">
|
||||
<div><?= htmlspecialchars($message) ?></div>
|
||||
</div>
|
||||
<a class="btn-close" data-bs-dismiss="alert" aria-label="close"></a>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="row">
|
||||
<!-- SMTP კონფიგურაცია -->
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">SMTP სერვერის პარამეტრები</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="post">
|
||||
<input type="hidden" name="action" value="update_settings">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">SMTP სერვერი</label>
|
||||
<input type="text" name="smtp_host" class="form-control"
|
||||
value="<?= htmlspecialchars($settings['smtp_host'] ?? '') ?>"
|
||||
placeholder="mail.example.com" required>
|
||||
<small class="form-hint">მაგ: smtp.gmail.com, mail.yahoo.com</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">პორტი</label>
|
||||
<input type="number" name="smtp_port" class="form-control"
|
||||
value="<?= htmlspecialchars($settings['smtp_port'] ?? '587') ?>"
|
||||
placeholder="587" required>
|
||||
<small class="form-hint">587 (TLS) ან 465 (SSL)</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">დაცვა</label>
|
||||
<select name="smtp_secure" class="form-select" required>
|
||||
<option value="tls" <?= ($settings['smtp_secure'] ?? '') === 'tls' ? 'selected' : '' ?>>TLS</option>
|
||||
<option value="ssl" <?= ($settings['smtp_secure'] ?? '') === 'ssl' ? 'selected' : '' ?>>SSL</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-check">
|
||||
<input type="checkbox" name="smtp_auth" class="form-check-input"
|
||||
<?= ($settings['smtp_auth'] ?? '0') === '1' ? 'checked' : '' ?>>
|
||||
<span class="form-check-label">SMTP ავტორიზაცია</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">მომხმარებლის სახელი</label>
|
||||
<input type="email" name="smtp_username" class="form-control"
|
||||
value="<?= htmlspecialchars($settings['smtp_username'] ?? '') ?>"
|
||||
placeholder="user@example.com">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">პაროლი</label>
|
||||
<div class="input-group">
|
||||
<input type="password" name="smtp_password" id="smtp_password" class="form-control"
|
||||
value="<?= htmlspecialchars($settings['smtp_password'] ?? '') ?>"
|
||||
placeholder="••••••••">
|
||||
<button type="button" class="btn btn-outline-secondary" onclick="togglePassword()">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon">
|
||||
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/>
|
||||
<circle cx="12" cy="12" r="3"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">გამომგზავნის ელ.ფოსტა</label>
|
||||
<input type="email" name="smtp_from_email" class="form-control"
|
||||
value="<?= htmlspecialchars($settings['smtp_from_email'] ?? '') ?>"
|
||||
placeholder="noreply@example.com" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">გამომგზავნის სახელი</label>
|
||||
<input type="text" name="smtp_from_name" class="form-control"
|
||||
value="<?= htmlspecialchars($settings['smtp_from_name'] ?? '') ?>"
|
||||
placeholder="My Website" required>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Debug რეჟიმი</label>
|
||||
<select name="smtp_debug" class="form-select">
|
||||
<option value="0" <?= ($settings['smtp_debug'] ?? '0') === '0' ? 'selected' : '' ?>>გამორთული</option>
|
||||
<option value="1" <?= ($settings['smtp_debug'] ?? '0') === '1' ? 'selected' : '' ?>>კლიენტის შეტყობინებები</option>
|
||||
<option value="2" <?= ($settings['smtp_debug'] ?? '0') === '2' ? 'selected' : '' ?>>კლიენტი + სერვერი</option>
|
||||
</select>
|
||||
<small class="form-hint">პროდაქშენში გამოიყენეთ 0</small>
|
||||
</div>
|
||||
|
||||
<div class="card-actions">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon">
|
||||
<path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"/>
|
||||
<polyline points="17,21 17,13 7,13 7,21"/>
|
||||
<polyline points="7,3 7,8 15,8"/>
|
||||
</svg>
|
||||
შენახვა
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- SMTP ტესტი -->
|
||||
<div class="col-md-4">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">კავშირის ტესტი</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="post">
|
||||
<input type="hidden" name="action" value="test_connection">
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">ტესტური ელ.ფოსტა (არასავალდებულო)</label>
|
||||
<input type="email" name="test_email" class="form-control"
|
||||
placeholder="test@example.com">
|
||||
<small class="form-hint">თუ მითითებული იქნება, ტესტური წერილი გაიგზავნება</small>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-success w-100">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon">
|
||||
<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/>
|
||||
<polyline points="22,4 12,14.01 9,11.01"/>
|
||||
</svg>
|
||||
კავშირის ტესტი
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- მიმდინარე კონფიგურაცია -->
|
||||
<div class="card mt-4">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">მიმდინარე კონფიგურაცია</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="list-group list-group-flush">
|
||||
<div class="list-group-item d-flex justify-content-between">
|
||||
<strong>სერვერი:</strong>
|
||||
<span class="badge bg-secondary"><?= htmlspecialchars($settings['smtp_host'] ?? 'არ არის მითითებული') ?></span>
|
||||
</div>
|
||||
<div class="list-group-item d-flex justify-content-between">
|
||||
<strong>პორტი:</strong>
|
||||
<span class="badge bg-info"><?= htmlspecialchars($settings['smtp_port'] ?? '587') ?></span>
|
||||
</div>
|
||||
<div class="list-group-item d-flex justify-content-between">
|
||||
<strong>დაცვა:</strong>
|
||||
<span class="badge bg-warning"><?= strtoupper($settings['smtp_secure'] ?? 'TLS') ?></span>
|
||||
</div>
|
||||
<div class="list-group-item d-flex justify-content-between">
|
||||
<strong>ავტორიზაცია:</strong>
|
||||
<span class="badge bg-<?= ($settings['smtp_auth'] ?? '0') === '1' ? 'success' : 'danger' ?>">
|
||||
<?= ($settings['smtp_auth'] ?? '0') === '1' ? 'ჩართული' : 'გამორთული' ?>
|
||||
</span>
|
||||
</div>
|
||||
<div class="list-group-item d-flex justify-content-between">
|
||||
<strong>გამომგზავნი:</strong>
|
||||
<span class="text-muted text-truncate" style="max-width: 150px;">
|
||||
<?= htmlspecialchars($settings['smtp_from_email'] ?? 'არ არის მითითებული') ?>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- სასარგებლო ინფორმაცია -->
|
||||
<div class="card mt-4">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">სასარგებლო ინფორმაცია</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="alert alert-info">
|
||||
<h4>Gmail SMTP:</h4>
|
||||
<ul class="mb-0">
|
||||
<li>სერვერი: smtp.gmail.com</li>
|
||||
<li>პორტი: 587 (TLS)</li>
|
||||
<li>App Password საჭიროა!</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-warning">
|
||||
<h4>მნიშვნელოვანი!</h4>
|
||||
<ul class="mb-0">
|
||||
<li>პაროლი ნაჩვენებია ღია ტექსტად</li>
|
||||
<li>გთხოვთ დარწმუნდით უსაფრთხოებაში</li>
|
||||
<li>გამოიყენეთ App Password Gmail-ისთვის</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="../dist/js/tabler.min.js"></script>
|
||||
<script>
|
||||
function togglePassword() {
|
||||
const passwordField = document.getElementById('smtp_password');
|
||||
const type = passwordField.getAttribute('type') === 'password' ? 'text' : 'password';
|
||||
passwordField.setAttribute('type', type);
|
||||
}
|
||||
|
||||
// Form validation
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const form = document.querySelector('form[method="post"]');
|
||||
if (form) {
|
||||
form.addEventListener('submit', function(e) {
|
||||
const smtpHost = document.querySelector('input[name="smtp_host"]').value.trim();
|
||||
const smtpPort = document.querySelector('input[name="smtp_port"]').value.trim();
|
||||
const fromEmail = document.querySelector('input[name="smtp_from_email"]').value.trim();
|
||||
|
||||
if (!smtpHost || !smtpPort || !fromEmail) {
|
||||
alert('გთხოვთ შეავსოთ ყველა სავალდებულო ველი!');
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (parseInt(smtpPort) < 1 || parseInt(smtpPort) > 65535) {
|
||||
alert('პორტი უნდა იყოს 1-65535 დიაპაზონში!');
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<?php require_once Config::includePath('footer.php'); ?>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
// Users index controller - redirects to management
|
||||
header("Location: dashboard.php?module=users&action=management");
|
||||
exit;
|
||||
?>
|
||||
@@ -0,0 +1,200 @@
|
||||
<?php
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
|
||||
require_once __DIR__ . '/../../../includes/init.php';
|
||||
require_once __DIR__ . '/../models/usermodel.php';
|
||||
|
||||
UserModel::setDb($pdo);
|
||||
|
||||
$message = '';
|
||||
$messageType = '';
|
||||
$action = $_GET['subaction'] ?? 'list';
|
||||
|
||||
// POST მოთხოვნების მართვა
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$postAction = $_POST['action'] ?? '';
|
||||
|
||||
switch ($postAction) {
|
||||
case 'add':
|
||||
$data = [
|
||||
'first_name' => trim($_POST['first_name'] ?? ''),
|
||||
'last_name' => trim($_POST['last_name'] ?? ''),
|
||||
'email' => trim($_POST['email'] ?? ''),
|
||||
'password' => $_POST['password'] ?? '',
|
||||
'role' => $_POST['role'] ?? 'user',
|
||||
'is_active' => isset($_POST['is_active']) ? 1 : 0
|
||||
];
|
||||
|
||||
// ვალიდაცია
|
||||
$errors = [];
|
||||
if (empty($data['first_name'])) $errors[] = 'სახელი სავალდებულოა';
|
||||
if (empty($data['last_name'])) $errors[] = 'გვარი სავალდებულოა';
|
||||
if (empty($data['email']) || !filter_var($data['email'], FILTER_VALIDATE_EMAIL)) {
|
||||
$errors[] = 'ვალიდური ელ.ფოსტა სავალდებულოა';
|
||||
}
|
||||
if (empty($data['password']) || strlen($data['password']) < 6) {
|
||||
$errors[] = 'პაროლი უნდა იყოს მინიმუმ 6 სიმბოლო';
|
||||
}
|
||||
|
||||
if (!empty($errors)) {
|
||||
$message = implode('<br>', $errors);
|
||||
$messageType = 'danger';
|
||||
} else {
|
||||
try {
|
||||
UserModel::createUser($data);
|
||||
$message = 'მომხმარებელი წარმატებით დაემატა';
|
||||
$messageType = 'success';
|
||||
$action = 'list';
|
||||
} catch (Exception $e) {
|
||||
$message = 'შეცდომა: ' . $e->getMessage();
|
||||
$messageType = 'danger';
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'edit':
|
||||
$id = (int)($_POST['id'] ?? 0);
|
||||
$data = [
|
||||
'first_name' => trim($_POST['first_name'] ?? ''),
|
||||
'last_name' => trim($_POST['last_name'] ?? ''),
|
||||
'email' => trim($_POST['email'] ?? ''),
|
||||
'password' => $_POST['password'] ?? '',
|
||||
'role' => $_POST['role'] ?? 'user',
|
||||
'is_active' => isset($_POST['is_active']) ? 1 : 0
|
||||
];
|
||||
|
||||
// ვალიდაცია
|
||||
$errors = [];
|
||||
if (empty($data['first_name'])) $errors[] = 'სახელი სავალდებულოა';
|
||||
if (empty($data['last_name'])) $errors[] = 'გვარი სავალდებულოა';
|
||||
if (empty($data['email']) || !filter_var($data['email'], FILTER_VALIDATE_EMAIL)) {
|
||||
$errors[] = 'ვალიდური ელ.ფოსტა სავალდებულოა';
|
||||
}
|
||||
if (!empty($data['password']) && strlen($data['password']) < 6) {
|
||||
$errors[] = 'პაროლი უნდა იყოს მინიმუმ 6 სიმბოლო';
|
||||
}
|
||||
|
||||
if (!empty($errors)) {
|
||||
$message = implode('<br>', $errors);
|
||||
$messageType = 'danger';
|
||||
} else {
|
||||
try {
|
||||
UserModel::updateUser($id, $data);
|
||||
$message = 'მომხმარებელი წარმატებით განახლდა';
|
||||
$messageType = 'success';
|
||||
$action = 'list';
|
||||
} catch (Exception $e) {
|
||||
$message = 'შეცდომა: ' . $e->getMessage();
|
||||
$messageType = 'danger';
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'delete':
|
||||
$id = (int)($_POST['id'] ?? 0);
|
||||
if ($id > 0) {
|
||||
try {
|
||||
UserModel::deleteUser($id);
|
||||
$message = 'მომხმარებელი წარმატებით წაიშალა';
|
||||
$messageType = 'success';
|
||||
} catch (Exception $e) {
|
||||
$message = 'შეცდომა: ' . $e->getMessage();
|
||||
$messageType = 'danger';
|
||||
}
|
||||
}
|
||||
$action = 'list';
|
||||
break;
|
||||
|
||||
case 'toggle_status':
|
||||
$id = (int)($_POST['id'] ?? 0);
|
||||
if ($id > 0) {
|
||||
try {
|
||||
UserModel::toggleUserStatus($id);
|
||||
$message = 'მომხმარებლის სტატუსი შეიცვალა';
|
||||
$messageType = 'success';
|
||||
} catch (Exception $e) {
|
||||
$message = 'შეცდომა: ' . $e->getMessage();
|
||||
$messageType = 'danger';
|
||||
}
|
||||
}
|
||||
$action = 'list';
|
||||
break;
|
||||
|
||||
case 'change_password':
|
||||
$id = (int)($_POST['id'] ?? 0);
|
||||
$currentPassword = $_POST['current_password'] ?? '';
|
||||
$newPassword = $_POST['new_password'] ?? '';
|
||||
$confirmPassword = $_POST['confirm_password'] ?? '';
|
||||
|
||||
$errors = [];
|
||||
if (empty($currentPassword)) $errors[] = 'მიმდინარე პაროლი სავალდებულოა';
|
||||
if (empty($newPassword) || strlen($newPassword) < 6) {
|
||||
$errors[] = 'ახალი პაროლი უნდა იყოს მინიმუმ 6 სიმბოლო';
|
||||
}
|
||||
if ($newPassword !== $confirmPassword) $errors[] = 'პაროლები არ ემთხვევა';
|
||||
|
||||
if (!empty($errors)) {
|
||||
$message = implode('<br>', $errors);
|
||||
$messageType = 'danger';
|
||||
} else {
|
||||
try {
|
||||
UserModel::changePassword($id, $currentPassword, $newPassword);
|
||||
$message = 'პაროლი წარმატებით შეიცვალა';
|
||||
$messageType = 'success';
|
||||
} catch (Exception $e) {
|
||||
$message = 'შეცდომა: ' . $e->getMessage();
|
||||
$messageType = 'danger';
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// მონაცემების მომზადება view-სთვის
|
||||
switch ($action) {
|
||||
case 'add':
|
||||
case 'edit':
|
||||
$availableRoles = UserModel::getAvailableRoles();
|
||||
if ($action === 'edit') {
|
||||
$editId = (int)($_GET['id'] ?? 0);
|
||||
$editUser = UserModel::getUserById($editId);
|
||||
if (!$editUser) {
|
||||
$message = 'მომხმარებელი ვერ მოიძებნა';
|
||||
$messageType = 'danger';
|
||||
$action = 'list';
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'profile':
|
||||
$profileId = (int)($_GET['id'] ?? $_SESSION['user_id'] ?? 0);
|
||||
$profileUser = UserModel::getUserById($profileId);
|
||||
if (!$profileUser) {
|
||||
$message = 'მომხმარებელი ვერ მოიძებნა';
|
||||
$messageType = 'danger';
|
||||
$action = 'list';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'list':
|
||||
default:
|
||||
// Search და Filter პარამეტრები
|
||||
$search = $_GET['search'] ?? '';
|
||||
$role = $_GET['role'] ?? '';
|
||||
$status = $_GET['status'] ?? '';
|
||||
|
||||
if ($search || $role || $status !== '') {
|
||||
$users = UserModel::searchUsers($search, $role, $status);
|
||||
} else {
|
||||
$users = UserModel::getAllUsers();
|
||||
}
|
||||
|
||||
$userStats = UserModel::getUserStats();
|
||||
$availableRoles = UserModel::getAvailableRoles();
|
||||
break;
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/../views/users_management.php';
|
||||
?>
|
||||
@@ -0,0 +1,267 @@
|
||||
<?php
|
||||
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
|
||||
class UserModel
|
||||
{
|
||||
protected static $db;
|
||||
|
||||
public static function setDb($pdo)
|
||||
{
|
||||
self::$db = $pdo;
|
||||
}
|
||||
|
||||
/**
|
||||
* ყველა მომხმარებლის მიღება
|
||||
*/
|
||||
public static function getAllUsers()
|
||||
{
|
||||
$stmt = self::$db->query("SELECT * FROM users ORDER BY created_at DESC");
|
||||
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
/**
|
||||
* კონკრეტული მომხმარებლის მიღება ID-ით
|
||||
*/
|
||||
public static function getUserById($id)
|
||||
{
|
||||
$stmt = self::$db->prepare("SELECT * FROM users WHERE id = ?");
|
||||
$stmt->execute([$id]);
|
||||
return $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
/**
|
||||
* მომხმარებლის მიღება ელ.ფოსტით
|
||||
*/
|
||||
public static function getUserByEmail($email)
|
||||
{
|
||||
$stmt = self::$db->prepare("SELECT * FROM users WHERE email = ?");
|
||||
$stmt->execute([$email]);
|
||||
return $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
/**
|
||||
* ახალი მომხმარებლის დამატება
|
||||
*/
|
||||
public static function createUser($data)
|
||||
{
|
||||
// ვამოწმებთ არსებობს თუ არა ასეთი ელ.ფოსტა
|
||||
if (self::getUserByEmail($data['email'])) {
|
||||
throw new Exception("მომხმარებელი ამ ელ.ფოსტით უკვე არსებობს");
|
||||
}
|
||||
|
||||
$hashedPassword = password_hash($data['password'], PASSWORD_DEFAULT);
|
||||
|
||||
$stmt = self::$db->prepare("
|
||||
INSERT INTO users (first_name, last_name, email, password, role, is_active)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
");
|
||||
|
||||
return $stmt->execute([
|
||||
$data['first_name'],
|
||||
$data['last_name'],
|
||||
$data['email'],
|
||||
$hashedPassword,
|
||||
$data['role'] ?? 'user',
|
||||
$data['is_active'] ?? 1
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* მომხმარებლის განახლება
|
||||
*/
|
||||
public static function updateUser($id, $data)
|
||||
{
|
||||
// ვამოწმებთ ელ.ფოსტის უნიკალურობას (თუ შეიცვალა)
|
||||
$currentUser = self::getUserById($id);
|
||||
if (!$currentUser) {
|
||||
throw new Exception("მომხმარებელი ვერ მოიძებნა");
|
||||
}
|
||||
|
||||
if ($currentUser['email'] !== $data['email']) {
|
||||
$existingUser = self::getUserByEmail($data['email']);
|
||||
if ($existingUser && $existingUser['id'] != $id) {
|
||||
throw new Exception("მომხმარებელი ამ ელ.ფოსტით უკვე არსებობს");
|
||||
}
|
||||
}
|
||||
|
||||
$updateFields = [
|
||||
'first_name' => $data['first_name'],
|
||||
'last_name' => $data['last_name'],
|
||||
'email' => $data['email'],
|
||||
'role' => $data['role'] ?? 'user',
|
||||
'is_active' => $data['is_active'] ?? 1
|
||||
];
|
||||
|
||||
// თუ ახალი პაროლი არის მითითებული
|
||||
if (!empty($data['password'])) {
|
||||
$updateFields['password'] = password_hash($data['password'], PASSWORD_DEFAULT);
|
||||
$sql = "UPDATE users SET first_name = ?, last_name = ?, email = ?, password = ?, role = ?, is_active = ?, updated_at = NOW() WHERE id = ?";
|
||||
$params = array_values($updateFields);
|
||||
$params[] = $id;
|
||||
} else {
|
||||
unset($updateFields['password']);
|
||||
$sql = "UPDATE users SET first_name = ?, last_name = ?, email = ?, role = ?, is_active = ?, updated_at = NOW() WHERE id = ?";
|
||||
$params = array_values($updateFields);
|
||||
$params[] = $id;
|
||||
}
|
||||
|
||||
$stmt = self::$db->prepare($sql);
|
||||
return $stmt->execute($params);
|
||||
}
|
||||
|
||||
/**
|
||||
* მომხმარებლის წაშლა
|
||||
*/
|
||||
public static function deleteUser($id)
|
||||
{
|
||||
// ვამოწმებთ არ არის თუ არა ეს უკანასკნელი ადმინი
|
||||
$admins = self::getAdminUsers();
|
||||
$userToDelete = self::getUserById($id);
|
||||
|
||||
if ($userToDelete['role'] === 'admin' && count($admins) <= 1) {
|
||||
throw new Exception("ვერ შეიძლება უკანასკნელი ადმინისტრატორის წაშლა");
|
||||
}
|
||||
|
||||
$stmt = self::$db->prepare("DELETE FROM users WHERE id = ?");
|
||||
return $stmt->execute([$id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* მომხმარებლის სტატუსის ცვლილება
|
||||
*/
|
||||
public static function toggleUserStatus($id)
|
||||
{
|
||||
$user = self::getUserById($id);
|
||||
if (!$user) {
|
||||
throw new Exception("მომხმარებელი ვერ მოიძებნა");
|
||||
}
|
||||
|
||||
$newStatus = $user['is_active'] ? 0 : 1;
|
||||
|
||||
// ვამოწმებთ ადმინების რაოდენობას
|
||||
if ($user['role'] === 'admin' && $user['is_active'] && $newStatus === 0) {
|
||||
$activeAdmins = self::getActiveAdminUsers();
|
||||
if (count($activeAdmins) <= 1) {
|
||||
throw new Exception("ვერ შეიძლება უკანასკნელი აქტიური ადმინისტრატორის გამორთვა");
|
||||
}
|
||||
}
|
||||
|
||||
$stmt = self::$db->prepare("UPDATE users SET is_active = ? WHERE id = ?");
|
||||
return $stmt->execute([$newStatus, $id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* პაროლის შეცვლა
|
||||
*/
|
||||
public static function changePassword($id, $currentPassword, $newPassword)
|
||||
{
|
||||
$user = self::getUserById($id);
|
||||
if (!$user) {
|
||||
throw new Exception("მომხმარებელი ვერ მოიძებნა");
|
||||
}
|
||||
|
||||
if (!password_verify($currentPassword, $user['password'])) {
|
||||
throw new Exception("მიმდინარე პაროლი არასწორია");
|
||||
}
|
||||
|
||||
$hashedPassword = password_hash($newPassword, PASSWORD_DEFAULT);
|
||||
$stmt = self::$db->prepare("UPDATE users SET password = ?, updated_at = NOW() WHERE id = ?");
|
||||
return $stmt->execute([$hashedPassword, $id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* ადმინისტრატორების მიღება
|
||||
*/
|
||||
public static function getAdminUsers()
|
||||
{
|
||||
$stmt = self::$db->prepare("SELECT * FROM users WHERE role = 'admin'");
|
||||
$stmt->execute();
|
||||
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
/**
|
||||
* აქტიური ადმინისტრატორების მიღება
|
||||
*/
|
||||
public static function getActiveAdminUsers()
|
||||
{
|
||||
$stmt = self::$db->prepare("SELECT * FROM users WHERE role = 'admin' AND is_active = 1");
|
||||
$stmt->execute();
|
||||
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
/**
|
||||
* მომხმარებლების სტატისტიკა
|
||||
*/
|
||||
public static function getUserStats()
|
||||
{
|
||||
$stats = [];
|
||||
|
||||
// სულ მომხმარებლები
|
||||
$stmt = self::$db->query("SELECT COUNT(*) FROM users");
|
||||
$stats['total'] = $stmt->fetchColumn();
|
||||
|
||||
// აქტიური მომხმარებლები
|
||||
$stmt = self::$db->query("SELECT COUNT(*) FROM users WHERE is_active = 1");
|
||||
$stats['active'] = $stmt->fetchColumn();
|
||||
|
||||
// ადმინისტრატორები
|
||||
$stmt = self::$db->query("SELECT COUNT(*) FROM users WHERE role = 'admin'");
|
||||
$stats['admins'] = $stmt->fetchColumn();
|
||||
|
||||
// ამ თვის ახალი მომხმარებლები
|
||||
$stmt = self::$db->query("SELECT COUNT(*) FROM users WHERE created_at >= DATE_FORMAT(NOW(), '%Y-%m-01')");
|
||||
$stats['this_month'] = $stmt->fetchColumn();
|
||||
|
||||
return $stats;
|
||||
}
|
||||
|
||||
/**
|
||||
* ხელმისაწვდომი როლები
|
||||
*/
|
||||
public static function getAvailableRoles()
|
||||
{
|
||||
return [
|
||||
'admin' => 'ადმინისტრატორი',
|
||||
'manager' => 'მენეჯერი',
|
||||
'user' => 'მომხმარებელი',
|
||||
'guest' => 'სტუმარი'
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* მომხმარებლის search და filter
|
||||
*/
|
||||
public static function searchUsers($search = '', $role = '', $status = '')
|
||||
{
|
||||
$sql = "SELECT * FROM users WHERE 1=1";
|
||||
$params = [];
|
||||
|
||||
if (!empty($search)) {
|
||||
$sql .= " AND (first_name LIKE ? OR last_name LIKE ? OR email LIKE ?)";
|
||||
$searchTerm = "%$search%";
|
||||
$params[] = $searchTerm;
|
||||
$params[] = $searchTerm;
|
||||
$params[] = $searchTerm;
|
||||
}
|
||||
|
||||
if (!empty($role)) {
|
||||
$sql .= " AND role = ?";
|
||||
$params[] = $role;
|
||||
}
|
||||
|
||||
if ($status !== '') {
|
||||
$sql .= " AND is_active = ?";
|
||||
$params[] = (int)$status;
|
||||
}
|
||||
|
||||
$sql .= " ORDER BY created_at DESC";
|
||||
|
||||
$stmt = self::$db->prepare($sql);
|
||||
$stmt->execute($params);
|
||||
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,584 @@
|
||||
<?php
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
|
||||
require_once __DIR__ . '/../../../includes/init.php';
|
||||
require_once __DIR__ . '/../../../../vendor/autoload.php';
|
||||
use App\Config;
|
||||
?>
|
||||
|
||||
<?php require_once Config::includePath('head.php'); ?>
|
||||
<?php require_once Config::includePath('navbar.php'); ?>
|
||||
<?php require_once Config::includePath('pageheader.php'); ?>
|
||||
<?php require_once Config::includePath('pagebodystart.php'); ?>
|
||||
|
||||
<div class="container-xl mt-4">
|
||||
<!-- Page header -->
|
||||
<div class="page-header d-print-none">
|
||||
<div class="row align-items-center">
|
||||
<div class="col">
|
||||
<h2 class="page-title">
|
||||
<?php
|
||||
switch ($action) {
|
||||
case 'add': echo 'ახალი მომხმარებლის დამატება'; break;
|
||||
case 'edit': echo 'მომხმარებლის რედაქტირება'; break;
|
||||
case 'profile': echo 'მომხმარებლის პროფილი'; break;
|
||||
default: echo 'მომხმარებლების მართვა'; break;
|
||||
}
|
||||
?>
|
||||
</h2>
|
||||
<?php if ($action === 'list'): ?>
|
||||
<div class="text-muted mt-1">სისტემის მომხმარებლების კონფიგურაცია და მართვა</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<div class="btn-list">
|
||||
<?php if ($action === 'list'): ?>
|
||||
<a href="dashboard.php?module=users&action=management&subaction=add" class="btn btn-primary">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon">
|
||||
<path d="M12 5l0 14"/>
|
||||
<path d="M5 12l14 0"/>
|
||||
</svg>
|
||||
ახალი მომხმარებელი
|
||||
</a>
|
||||
<?php else: ?>
|
||||
<a href="dashboard.php?module=users&action=management" class="btn btn-outline-primary">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon">
|
||||
<path d="M5 12l14 0"/>
|
||||
<path d="M5 12l6 6"/>
|
||||
<path d="M5 12l6 -6"/>
|
||||
</svg>
|
||||
უკან დაბრუნება
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Messages -->
|
||||
<?php if ($message): ?>
|
||||
<div class="alert alert-<?= $messageType ?> alert-dismissible">
|
||||
<div class="d-flex">
|
||||
<div><?= $message ?></div>
|
||||
</div>
|
||||
<a class="btn-close" data-bs-dismiss="alert" aria-label="close"></a>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($action === 'list'): ?>
|
||||
<!-- Statistics Cards -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-sm-6 col-lg-3">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="subheader">სულ მომხმარებლები</div>
|
||||
<div class="ms-auto lh-1">
|
||||
<div class="dropdown">
|
||||
<a class="dropdown-toggle text-muted" href="#" data-bs-toggle="dropdown">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon">
|
||||
<circle cx="12" cy="12" r="1"/>
|
||||
<circle cx="12" cy="5" r="1"/>
|
||||
<circle cx="12" cy="19" r="1"/>
|
||||
</svg>
|
||||
</a>
|
||||
<div class="dropdown-menu dropdown-menu-end">
|
||||
<a class="dropdown-item" href="dashboard.php?module=users&action=management">ყველას ნახვა</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="h1 mb-3"><?= $userStats['total'] ?></div>
|
||||
<div class="d-flex mb-2">
|
||||
<div>რეგისტრაცია</div>
|
||||
<div class="ms-auto">
|
||||
<span class="text-green d-inline-flex align-items-center lh-1">
|
||||
<?= $userStats['this_month'] ?> <small class="text-muted ms-1">ამ თვეში</small>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6 col-lg-3">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="subheader">აქტიური</div>
|
||||
<div class="h1 mb-3 text-green"><?= $userStats['active'] ?></div>
|
||||
<div class="d-flex mb-2">
|
||||
<div>უაქტივო</div>
|
||||
<div class="ms-auto">
|
||||
<span class="text-red"><?= $userStats['total'] - $userStats['active'] ?></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6 col-lg-3">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="subheader">ადმინისტრატორები</div>
|
||||
<div class="h1 mb-3 text-blue"><?= $userStats['admins'] ?></div>
|
||||
<div class="d-flex mb-2">
|
||||
<div>სხვა როლები</div>
|
||||
<div class="ms-auto">
|
||||
<span class="text-muted"><?= $userStats['total'] - $userStats['admins'] ?></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6 col-lg-3">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="subheader">ონლაინ</div>
|
||||
<div class="h1 mb-3 text-yellow">0</div>
|
||||
<div class="d-flex mb-2">
|
||||
<div>უკანასკნელი აქტივობა</div>
|
||||
<div class="ms-auto">
|
||||
<span class="text-muted">დღეს</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Search and Filter -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-body">
|
||||
<form method="get" class="row">
|
||||
<input type="hidden" name="module" value="users">
|
||||
<input type="hidden" name="action" value="management">
|
||||
|
||||
<div class="col-md-3">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">ძიება</label>
|
||||
<input type="text" name="search" class="form-control"
|
||||
value="<?= htmlspecialchars($_GET['search'] ?? '') ?>"
|
||||
placeholder="სახელი, გვარი, ელ.ფოსტა...">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">როლი</label>
|
||||
<select name="role" class="form-select">
|
||||
<option value="">ყველა როლი</option>
|
||||
<?php foreach ($availableRoles as $roleKey => $roleLabel): ?>
|
||||
<option value="<?= $roleKey ?>" <?= ($_GET['role'] ?? '') === $roleKey ? 'selected' : '' ?>>
|
||||
<?= $roleLabel ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">სტატუსი</label>
|
||||
<select name="status" class="form-select">
|
||||
<option value="">ყველა სტატუსი</option>
|
||||
<option value="1" <?= ($_GET['status'] ?? '') === '1' ? 'selected' : '' ?>>აქტიური</option>
|
||||
<option value="0" <?= ($_GET['status'] ?? '') === '0' ? 'selected' : '' ?>>უაქტივო</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="mb-3">
|
||||
<label class="form-label"> </label>
|
||||
<div class="btn-list">
|
||||
<button type="submit" class="btn btn-primary w-100">ძიება</button>
|
||||
<a href="dashboard.php?module=users&action=management" class="btn btn-outline-secondary">გასუფთავება</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Users List -->
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">მომხმარებლების სია</h3>
|
||||
<div class="card-actions">
|
||||
<span class="text-muted">სულ: <?= count($users) ?> მომხმარებელი</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<?php if (empty($users)): ?>
|
||||
<div class="text-center text-muted py-4">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon mb-3">
|
||||
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/>
|
||||
<circle cx="12" cy="7" r="4"/>
|
||||
</svg>
|
||||
<h3>მომხმარებლები ვერ მოიძებნა</h3>
|
||||
<p class="text-muted">შეცვალეთ ფილტრები ან დაამატეთ ახალი მომხმარებელი</p>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-vcenter">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>მომხმარებელი</th>
|
||||
<th>ელ.ფოსტა</th>
|
||||
<th>როლი</th>
|
||||
<th>სტატუსი</th>
|
||||
<th>რეგისტრაცია</th>
|
||||
<th>მოქმედებები</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($users as $user): ?>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="d-flex align-items-center">
|
||||
<span class="avatar bg-<?= $user['role'] === 'admin' ? 'red' : ($user['is_active'] ? 'green' : 'secondary') ?>-lt">
|
||||
<?= strtoupper(substr($user['first_name'], 0, 1) . substr($user['last_name'], 0, 1)) ?>
|
||||
</span>
|
||||
<div class="ms-2">
|
||||
<div class="fw-bold"><?= htmlspecialchars($user['first_name'] . ' ' . $user['last_name']) ?></div>
|
||||
<div class="text-muted small">ID: <?= $user['id'] ?></div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div><?= htmlspecialchars($user['email']) ?></div>
|
||||
<?php if ($user['id'] == $_SESSION['user_id']): ?>
|
||||
<small class="text-blue">ეს ხარ თუ</small>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge bg-<?= $user['role'] === 'admin' ? 'red' : ($user['role'] === 'manager' ? 'blue' : 'secondary') ?>">
|
||||
<?= $availableRoles[$user['role']] ?? $user['role'] ?>
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge bg-<?= $user['is_active'] ? 'success' : 'danger' ?>">
|
||||
<?= $user['is_active'] ? 'აქტიური' : 'უაქტივო' ?>
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<div><?= date('Y-m-d', strtotime($user['created_at'])) ?></div>
|
||||
<small class="text-muted"><?= date('H:i', strtotime($user['created_at'])) ?></small>
|
||||
</td>
|
||||
<td>
|
||||
<div class="btn-list">
|
||||
<a href="dashboard.php?module=users&action=management&subaction=profile&id=<?= $user['id'] ?>"
|
||||
class="btn btn-sm btn-outline-info" title="პროფილი">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon">
|
||||
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/>
|
||||
<circle cx="12" cy="7" r="4"/>
|
||||
</svg>
|
||||
</a>
|
||||
<a href="dashboard.php?module=users&action=management&subaction=edit&id=<?= $user['id'] ?>"
|
||||
class="btn btn-sm btn-outline-primary" title="რედაქტირება">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon">
|
||||
<path d="M7 7h-1a2 2 0 0 0 -2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2 -2v-1"/>
|
||||
<path d="M20.385 6.585a2.1 2.1 0 0 0 -2.97 -2.97l-8.415 8.385v3h3l8.385 -8.415z"/>
|
||||
<path d="M16 5l3 3"/>
|
||||
</svg>
|
||||
</a>
|
||||
<form method="post" style="display: inline;">
|
||||
<input type="hidden" name="action" value="toggle_status">
|
||||
<input type="hidden" name="id" value="<?= $user['id'] ?>">
|
||||
<button type="submit" class="btn btn-sm btn-outline-<?= $user['is_active'] ? 'warning' : 'success' ?>"
|
||||
title="<?= $user['is_active'] ? 'გამორთვა' : 'ჩართვა' ?>"
|
||||
onclick="return confirm('დარწმუნებული ხართ?')">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon">
|
||||
<?php if ($user['is_active']): ?>
|
||||
<path d="M10 3H6a2 2 0 0 0-2 2v14c0 5.5 7.5 3 7.5 3s7.5 2.5 7.5-3V5a2 2 0 0 0-2-2h-4"/>
|
||||
<path d="M8 5a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2"/>
|
||||
<?php else: ?>
|
||||
<path d="M9 11l3 3l8 -8"/>
|
||||
<?php endif; ?>
|
||||
</svg>
|
||||
</button>
|
||||
</form>
|
||||
<?php if ($user['id'] != $_SESSION['user_id']): ?>
|
||||
<form method="post" style="display: inline;">
|
||||
<input type="hidden" name="action" value="delete">
|
||||
<input type="hidden" name="id" value="<?= $user['id'] ?>">
|
||||
<button type="submit" class="btn btn-sm btn-outline-danger" title="წაშლა"
|
||||
onclick="return confirm('დარწმუნებული ხართ რომ გსურთ ამ მომხმარებლის წაშლა?')">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon">
|
||||
<path d="M4 7l16 0"/>
|
||||
<path d="M10 11l0 6"/>
|
||||
<path d="M14 11l0 6"/>
|
||||
<path d="M5 7l1 12a2 2 0 0 0 2 2h8a2 2 0 0 0 2 -2l1 -12"/>
|
||||
<path d="M9 7v-3a1 1 0 0 1 1 -1h4a1 1 0 0 1 1 1v3"/>
|
||||
</svg>
|
||||
</button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php elseif ($action === 'add' || $action === 'edit'): ?>
|
||||
<!-- Add/Edit Form -->
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">
|
||||
<?= $action === 'add' ? 'ახალი მომხმარებლის დამატება' : 'მომხმარებლის რედაქტირება' ?>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="post">
|
||||
<input type="hidden" name="action" value="<?= $action ?>">
|
||||
<?php if ($action === 'edit'): ?>
|
||||
<input type="hidden" name="id" value="<?= $editUser['id'] ?>">
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">სახელი</label>
|
||||
<input type="text" name="first_name" class="form-control"
|
||||
value="<?= htmlspecialchars($editUser['first_name'] ?? '') ?>"
|
||||
placeholder="სახელი" required>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">გვარი</label>
|
||||
<input type="text" name="last_name" class="form-control"
|
||||
value="<?= htmlspecialchars($editUser['last_name'] ?? '') ?>"
|
||||
placeholder="გვარი" required>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">ელ.ფოსტა</label>
|
||||
<input type="email" name="email" class="form-control"
|
||||
value="<?= htmlspecialchars($editUser['email'] ?? '') ?>"
|
||||
placeholder="user@example.com" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">პაროლი <?= $action === 'edit' ? '(დატოვეთ ცარიელი თუ არ გსურთ ცვლილება)' : '' ?></label>
|
||||
<input type="password" name="password" class="form-control"
|
||||
placeholder="••••••••" <?= $action === 'add' ? 'required' : '' ?>>
|
||||
<?php if ($action === 'add'): ?>
|
||||
<small class="form-hint">მინიმუმ 6 სიმბოლო</small>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">როლი</label>
|
||||
<select name="role" class="form-select" required>
|
||||
<?php foreach ($availableRoles as $roleKey => $roleLabel): ?>
|
||||
<option value="<?= $roleKey ?>"
|
||||
<?= ($editUser['role'] ?? 'user') === $roleKey ? 'selected' : '' ?>>
|
||||
<?= $roleLabel ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-check form-switch mb-3">
|
||||
<input class="form-check-input" type="checkbox" name="is_active"
|
||||
<?= ($editUser['is_active'] ?? 1) ? 'checked' : '' ?>>
|
||||
<label class="form-check-label">აქტიური მომხმარებელი</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-actions">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon">
|
||||
<path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"/>
|
||||
<polyline points="17,21 17,13 7,13 7,21"/>
|
||||
<polyline points="7,3 7,8 15,8"/>
|
||||
</svg>
|
||||
შენახვა
|
||||
</button>
|
||||
<a href="dashboard.php?module=users&action=management" class="btn btn-secondary">გაუქმება</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php elseif ($action === 'profile'): ?>
|
||||
<!-- User Profile -->
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<!-- Profile Info -->
|
||||
<div class="card">
|
||||
<div class="card-body text-center">
|
||||
<span class="avatar avatar-xl bg-<?= $profileUser['role'] === 'admin' ? 'red' : ($profileUser['is_active'] ? 'green' : 'secondary') ?>-lt mb-3">
|
||||
<?= strtoupper(substr($profileUser['first_name'], 0, 1) . substr($profileUser['last_name'], 0, 1)) ?>
|
||||
</span>
|
||||
<h3 class="m-0 mb-1"><?= htmlspecialchars($profileUser['first_name'] . ' ' . $profileUser['last_name']) ?></h3>
|
||||
<div class="text-muted"><?= htmlspecialchars($profileUser['email']) ?></div>
|
||||
<div class="mt-3">
|
||||
<span class="badge bg-<?= $profileUser['role'] === 'admin' ? 'red' : ($profileUser['role'] === 'manager' ? 'blue' : 'secondary') ?> me-1">
|
||||
<?= $availableRoles[$profileUser['role']] ?? $profileUser['role'] ?>
|
||||
</span>
|
||||
<span class="badge bg-<?= $profileUser['is_active'] ? 'success' : 'danger' ?>">
|
||||
<?= $profileUser['is_active'] ? 'აქტიური' : 'უაქტივო' ?>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<div class="row text-center">
|
||||
<div class="col">
|
||||
<div class="h4 m-0">ID</div>
|
||||
<div class="text-muted"><?= $profileUser['id'] ?></div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="h4 m-0">რეგისტრაცია</div>
|
||||
<div class="text-muted"><?= date('Y-m-d', strtotime($profileUser['created_at'])) ?></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-8">
|
||||
<!-- Profile Details -->
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">პროფილის დეტალები</h3>
|
||||
<div class="card-actions">
|
||||
<a href="dashboard.php?module=users&action=management&subaction=edit&id=<?= $profileUser['id'] ?>" class="btn btn-primary btn-sm">
|
||||
რედაქტირება
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">სახელი</label>
|
||||
<div class="form-control-plaintext"><?= htmlspecialchars($profileUser['first_name']) ?></div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">ელ.ფოსტა</label>
|
||||
<div class="form-control-plaintext"><?= htmlspecialchars($profileUser['email']) ?></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">გვარი</label>
|
||||
<div class="form-control-plaintext"><?= htmlspecialchars($profileUser['last_name']) ?></div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">ბოლო განახლება</label>
|
||||
<div class="form-control-plaintext">
|
||||
<?= $profileUser['updated_at'] ? date('Y-m-d H:i', strtotime($profileUser['updated_at'])) : 'არასდროს' ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Password Change -->
|
||||
<?php if ($profileUser['id'] == $_SESSION['user_id']): ?>
|
||||
<div class="card mt-4">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">პაროლის შეცვლა</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="post">
|
||||
<input type="hidden" name="action" value="change_password">
|
||||
<input type="hidden" name="id" value="<?= $profileUser['id'] ?>">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">მიმდინარე პაროლი</label>
|
||||
<input type="password" name="current_password" class="form-control" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">ახალი პაროლი</label>
|
||||
<input type="password" name="new_password" class="form-control" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">პაროლის დადასტურება</label>
|
||||
<input type="password" name="confirm_password" class="form-control" required>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-warning">პაროლის შეცვლა</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<script src="../dist/js/tabler.min.js"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Form validation
|
||||
const forms = document.querySelectorAll('form[method="post"]');
|
||||
forms.forEach(form => {
|
||||
form.addEventListener('submit', function(e) {
|
||||
const action = form.querySelector('input[name="action"]')?.value;
|
||||
|
||||
if (action === 'add' || action === 'edit') {
|
||||
const firstName = form.querySelector('input[name="first_name"]')?.value.trim();
|
||||
const lastName = form.querySelector('input[name="last_name"]')?.value.trim();
|
||||
const email = form.querySelector('input[name="email"]')?.value.trim();
|
||||
const password = form.querySelector('input[name="password"]')?.value;
|
||||
|
||||
let errors = [];
|
||||
|
||||
if (!firstName) errors.push('სახელი სავალდებულოა');
|
||||
if (!lastName) errors.push('გვარი სავალდებულოა');
|
||||
if (!email || !email.includes('@')) errors.push('ვალიდური ელ.ფოსტა სავალდებულოა');
|
||||
|
||||
if (action === 'add' && (!password || password.length < 6)) {
|
||||
errors.push('პაროლი უნდა იყოს მინიმუმ 6 სიმბოლო');
|
||||
}
|
||||
|
||||
if (action === 'edit' && password && password.length < 6) {
|
||||
errors.push('პაროლი უნდა იყოს მინიმუმ 6 სიმბოლო');
|
||||
}
|
||||
|
||||
if (errors.length > 0) {
|
||||
alert('შეცდომები:\n' + errors.join('\n'));
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (action === 'change_password') {
|
||||
const currentPassword = form.querySelector('input[name="current_password"]')?.value;
|
||||
const newPassword = form.querySelector('input[name="new_password"]')?.value;
|
||||
const confirmPassword = form.querySelector('input[name="confirm_password"]')?.value;
|
||||
|
||||
let errors = [];
|
||||
|
||||
if (!currentPassword) errors.push('მიმდინარე პაროლი სავალდებულოა');
|
||||
if (!newPassword || newPassword.length < 6) errors.push('ახალი პაროლი უნდა იყოს მინიმუმ 6 სიმბოლო');
|
||||
if (newPassword !== confirmPassword) errors.push('პაროლები არ ემთხვევა');
|
||||
|
||||
if (errors.length > 0) {
|
||||
alert('შეცდომები:\n' + errors.join('\n'));
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<?php require_once Config::includePath('footer.php'); ?>
|
||||
Reference in New Issue
Block a user