initial code commit

This commit is contained in:
2024-02-09 20:52:11 -06:00
parent f5d267ea98
commit 532df6b9be
18 changed files with 583 additions and 1 deletions

21
Model/Bill.php Normal file
View File

@@ -0,0 +1,21 @@
<?php
namespace Hpz937\BillReminder\Model;
class Bill {
public int $id;
public string $dueDate; // Format: 'YYYY-MM-DD'
public float $amount;
public string $description;
public bool $isPaid;
public function __construct(int $id, string $dueDate, float $amount, string $description, bool $isPaid) {
$this->id = $id;
$this->dueDate = $dueDate;
$this->amount = $amount;
$this->description = $description;
$this->isPaid = $isPaid;
}
// Additional methods as needed, such as getters and setters
}

20
Model/User.php Normal file
View File

@@ -0,0 +1,20 @@
<?php
namespace Hpz937\BillReminder\Model;
class User {
public int $id;
public string $username;
private string $passwordHash;
public function __construct(int $id, string $username, string $passwordHash) {
$this->id = $id;
$this->username = $username;
$this->passwordHash = $passwordHash;
}
// Add methods for password verification, etc.
public function verifyPassword(string $password): bool {
return password_verify($password, $this->passwordHash);
}
}

69
Service/BillManager.php Normal file
View File

@@ -0,0 +1,69 @@
<?php
namespace Hpz937\BillReminder\Service;
use Hpz937\BillReminder\Database\DatabaseInterface;
use Hpz937\BillReminder\Model\Bill;
class BillManager {
private DatabaseInterface $db;
public function __construct(DatabaseInterface $db) {
$this->db = $db;
}
public function createBill(Bill $bill): bool {
$sql = "INSERT INTO bills (dueDate, amount, description, isPaid) VALUES (:dueDate, :amount, :description, :isPaid)";
$this->db->prepare($sql);
$this->db->execute([
':dueDate' => $bill->dueDate,
':amount' => $bill->amount,
':description' => $bill->description,
':isPaid' => $bill->isPaid ? 1 : 0,
]);
return $this->db->lastInsertId() > 0;
}
public function getBill(int $id): ?Bill {
$sql = "SELECT * FROM bills WHERE id = :id";
$this->db->prepare($sql);
$this->db->execute([':id' => $id]);
$result = $this->db->fetch();
if ($result) {
return new Bill($result['id'], $result['dueDate'], $result['amount'], $result['description'], $result['isPaid'] == 1);
}
return null;
}
public function updateBill(Bill $bill): bool {
$sql = "UPDATE bills SET dueDate = :dueDate, amount = :amount, description = :description, isPaid = :isPaid WHERE id = :id";
$this->db->prepare($sql);
return $this->db->execute([
':id' => $bill->id,
':dueDate' => $bill->dueDate,
':amount' => $bill->amount,
':description' => $bill->description,
':isPaid' => $bill->isPaid ? 1 : 0,
]);
}
public function deleteBill(int $id): bool {
$sql = "DELETE FROM bills WHERE id = :id";
$this->db->prepare($sql);
return $this->db->execute([':id' => $id]);
}
public function listBills(): array {
$sql = "SELECT * FROM bills";
$this->db->prepare($sql);
$this->db->execute();
$bills = [];
while ($row = $this->db->fetch()) {
$bills[] = new Bill($row['id'], $row['dueDate'], $row['amount'], $row['description'], $row['isPaid'] == 1);
}
return $bills;
}
}

42
Service/UserManager.php Normal file
View File

@@ -0,0 +1,42 @@
<?php
namespace Hpz937\BillReminder\Service;
use Hpz937\BillReminder\Database\DatabaseInterface;
use Hpz937\BillReminder\Model\User;
class UserManager {
private DatabaseInterface $db;
public function __construct(DatabaseInterface $db) {
$this->db = $db;
}
public function register(string $username, string $password): bool {
$passwordHash = password_hash($password, PASSWORD_DEFAULT);
$sql = "INSERT INTO users (username, passwordHash) VALUES (:username, :passwordHash)";
$this->db->prepare($sql);
return $this->db->execute([
':username' => $username,
':passwordHash' => $passwordHash,
]);
}
public function login(string $username, string $password): ?User {
$sql = "SELECT * FROM users WHERE username = :username";
$this->db->prepare($sql);
$this->db->execute([':username' => $username]);
$result = $this->db->fetch();
if ($result && (new User($result['id'], $result['username'], $result['passwordHash']))->verifyPassword($password)) {
// Start or regenerate the session
session_regenerate_id();
$_SESSION['user_id'] = $result['id'];
return new User($result['id'], $result['username'], $result['passwordHash']);
}
return null;
}
// Additional methods as needed, e.g., for password change, user details update, etc.
}

32
View/PageRenderer.php Normal file
View File

@@ -0,0 +1,32 @@
<?php
namespace Hpz937\BillReminder\View;
class PageRenderer {
public static function renderHeader($title = 'Bill Reminder') {
echo <<<HTML
<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>$title</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.0.0/dist/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<!-- Additional stylesheet links or other <head> elements -->
</head>
<body>
<div class="container">
HTML;
}
public static function renderFooter() {
echo <<<HTML
</div> <!-- Closing container div -->
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.12.9/dist/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.0.0/dist/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
</body>
</html>
HTML;
}
}

View File

@@ -13,5 +13,12 @@
"email": "hpz937+code@gmail.com"
}
],
"require": {}
"repositories": [{
"type": "composer",
"url": "https://git.hpz.pw/api/packages/hpz937/composer"
}
],
"require": {
"hpz937/restclient": "1.0.0"
}
}

27
index.php Normal file
View File

@@ -0,0 +1,27 @@
<?php
// index.php
session_start();
require 'vendor/autoload.php'; // Assuming you're using Composer
// Define a simple router
$request = $_SERVER['REQUEST_URI'];
switch ($request) {
case '/' :
case '' :
require __DIR__ . '/views/home.php';
break;
case '/login' :
require __DIR__ . '/views/login.php';
break;
case '/register' :
require __DIR__ . '/views/register.php';
break;
// Add more routes as needed
default:
http_response_code(404);
require __DIR__ . '/views/404.php';
break;
}

14
setup.php Normal file
View File

@@ -0,0 +1,14 @@
<?php
use Hpz937\BillReminder\Database\DatabaseSetup;
use Hpz937\BillReminder\Database\SQLite3Database;
// Assuming you have a SQLite database file path
$databaseFile = 'path/to/your/database/file.sqlite';
$db = new SQLite3Database($databaseFile);
$db->connect();
$databaseSetup = new DatabaseSetup($db);
$databaseSetup->setup();
// Continue with the rest of your application setup

View File

@@ -0,0 +1,14 @@
<?php
namespace Hpz937\BillReminder\Database;
interface DatabaseInterface {
public function connect();
public function query(string $query);
public function prepare(string $sql);
public function execute(array $parameters = []);
public function fetch();
public function fetchAll();
public function close();
}

View File

@@ -0,0 +1,44 @@
<?php
namespace Hpz937\BillReminder\Database;
class DatabaseSetup {
private DatabaseInterface $db;
public function __construct(DatabaseInterface $db) {
$this->db = $db;
}
public function setup(): void {
$this->createBillsTable();
$this->createUsersTable();
}
private function createBillsTable(): void {
$sql = <<<SQL
CREATE TABLE IF NOT EXISTS bills (
id INTEGER PRIMARY KEY AUTOINCREMENT,
dueDate TEXT NOT NULL,
amount REAL NOT NULL,
description TEXT,
isPaid INTEGER NOT NULL
);
SQL;
$this->db->query($sql);
}
private function createUsersTable(): void {
$sql = <<<SQL
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
passwordHash TEXT NOT NULL
);
SQL;
$this->db->query($sql);
}
// Additional methods to setup other tables as needed
}

View File

@@ -0,0 +1,83 @@
<?php
namespace Hpz937\BillReminder\Database;
use SQLite3;
use Exception;
class SQLite3Database implements DatabaseInterface {
/**
* @var SQLite3
*/
private $connection;
/**
* @var string The path to the SQLite database file.
*/
private $databaseFile;
/**
* @var \SQLite3Stmt
*/
private $statement;
public function __construct(string $databaseFile) {
$this->databaseFile = $databaseFile;
}
public function connect() {
try {
$this->connection = new SQLite3($this->databaseFile);
} catch (Exception $e) {
throw new Exception("Unable to connect to SQLite database: " . $e->getMessage());
}
}
public function query(string $query) {
if (!$this->connection) {
throw new Exception("No database connection");
}
return $this->connection->query($query);
}
public function prepare(string $sql) {
if (!$this->connection) {
throw new Exception("No database connection");
}
$this->statement = $this->connection->prepare($sql);
if (!$this->statement) {
throw new Exception("Failed to prepare SQL statement");
}
}
public function execute(array $parameters = []) {
if (!$this->statement) {
throw new Exception("No prepared statement");
}
foreach ($parameters as $param => $value) {
$this->statement->bindValue($param, $value);
}
return $this->statement->execute();
}
public function fetch() {
if (!$this->statement) {
throw new Exception("No prepared statement");
}
return $this->statement->fetchArray(SQLITE3_ASSOC);
}
public function fetchAll() {
$data = [];
while ($row = $this->fetch()) {
$data[] = $row;
}
return $data;
}
public function close() {
if ($this->connection) {
$this->connection->close();
}
}
}

View File

@@ -0,0 +1,44 @@
<?php
namespace Hpz937\BillReminder\Notification;
use Hpz937\Restclient\RestClient;
use Exception;
class NftyNotification implements NotificationInterface {
private $restClient;
public function __construct(RestClient $restClient) {
$this->restClient = $restClient;
}
public function send(string $to, string $message, array $options = []): bool {
try {
// ntfy.sh uses simple text/plain content type for the message body
$headers = ["Content-Type: text/plain"];
if (isset($options['title'])) {
$headers[] = "Title: {$options['title']}";
}
if (isset($options['priority'])) {
$headers[] = "Priority: {$options['priority']}";
}
if (isset($options['tags'])) {
$headers[] = "Tags: {$options['tags']}";
}
$this->restClient->setHeaders($headers);
// Use the RestClient's custom request method to mimic the file_get_contents approach
$this->restClient->post("https://ntfy.sh/{$this->to}", $message);
// Since ntfy.sh returns a simple response, we might not need to decode JSON
$response = $this->restClient->getResponse();
// Check the response or HTTP status code as needed to determine success
return true; // Assuming the request was successful, adjust based on actual response handling
} catch (Exception $e) {
// Error handling logic
return false;
}
}
}

View File

@@ -0,0 +1,15 @@
<?php
namespace Hpz937\BillReminder\Notification;
interface NotificationInterface {
/**
* Sends a notification with optional parameters such as title and priority.
*
* @param string $to The recipient of the notification.
* @param string $message The message content of the notification.
* @param array $options Optional parameters for the notification, such as title, priority, and tags.
* @return bool Returns true on success, false on failure.
*/
public function send(string $to, string $message, array $options = []): bool;
}

0
views/404.php Normal file
View File

50
views/dashboard.php Normal file
View File

@@ -0,0 +1,50 @@
<?php
use Hpz937\BillReminder\View\PageRenderer;
session_start();
// Redirect to login if not logged in
if (!isset($_SESSION['user_id'])) {
header('Location: /login');
exit;
}
PageRenderer::renderHeader('My Bills Dashboard');
// Fetch user's bills from the database
// $bills = fetchBillsForUser($_SESSION['user_id']);
?>
<h2>My Bills</h2>
<a href="/add-bill" class="btn btn-success">Add New Bill</a>
<div class="mt-3">
<table class="table">
<thead>
<tr>
<th>Due Date</th>
<th>Amount</th>
<th>Description</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php foreach ($bills as $bill): ?>
<tr>
<td><?php echo htmlspecialchars($bill->dueDate); ?></td>
<td><?php echo htmlspecialchars($bill->amount); ?></td>
<td><?php echo htmlspecialchars($bill->description); ?></td>
<td><?php echo $bill->isPaid ? 'Paid' : 'Pending'; ?></td>
<td>
<a href="/edit-bill/<?php echo $bill->id; ?>" class="btn btn-primary btn-sm">Edit</a>
<a href="/delete-bill/<?php echo $bill->id; ?>" class="btn btn-danger btn-sm" onclick="return confirm('Are you sure?');">Delete</a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php PageRenderer::renderFooter(); ?>

30
views/home.php Normal file
View File

@@ -0,0 +1,30 @@
<?php
use Hpz937\BillReminder\View\PageRenderer;
session_start();
PageRenderer::renderHeader('Bill Reminder Home');
// views/home.php
session_start(); // Ensure session is started to access session variables
// Check if user is logged in
$loggedIn = isset($_SESSION['user_id']);
?>
<h1>Welcome to Bill Reminder</h1>
<p>Manage your bills efficiently and never miss a payment.</p>
<?php if ($loggedIn): ?>
<p>Hello, <?php echo htmlspecialchars($_SESSION['username']); ?>!</p>
<a href="/dashboard" class="btn btn-primary">Go to Dashboard</a>
<a href="/logout" class="btn btn-secondary">Logout</a>
<?php else: ?>
<a href="/login" class="btn btn-primary">Login</a>
<a href="/register" class="btn btn-secondary">Register</a>
<?php endif; ?>
<?php PageRenderer::renderFooter(); ?>

35
views/login.php Normal file
View File

@@ -0,0 +1,35 @@
<?php
// views/login.php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// Handle login logic here
// Use UserManager to verify credentials
// Redirect to home page or dashboard on success
}
?>
<!DOCTYPE html>
<html>
<head>
<title>Login</title>
<link rel="stylesheet" href="path/to/bootstrap/css/bootstrap.min.css">
</head>
<body>
<div class="container">
<h2>Login</h2>
<form method="POST" action="/login">
<div class="form-group">
<label for="username">Username:</label>
<input type="text" class="form-control" id="username" name="username" required>
</div>
<div class="form-group">
<label for="password">Password:</label>
<input type="password" class="form-control" id="password" name="password" required>
</div>
<button type="submit" class="btn btn-primary">Login</button>
</form>
</div>
</body>
</html>

35
views/register.php Normal file
View File

@@ -0,0 +1,35 @@
<?php
// views/register.php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// Handle registration logic here
// Use UserManager to create a new user
// Redirect to login page on success
}
?>
<!DOCTYPE html>
<html>
<head>
<title>Register</title>
<link rel="stylesheet" href="path/to/bootstrap/css/bootstrap.min.css">
</head>
<body>
<div class="container">
<h2>Register</h2>
<form method="POST" action="/register">
<div class="form-group">
<label for="username">Username:</label>
<input type="text" class="form-control" id="username" name="username" required>
</div>
<div class="form-group">
<label for="password">Password:</label>
<input type="password" class="form-control" id="password" name="password" required>
</div>
<button type="submit" class="btn btn-success">Register</button>
</form>
</div>
</body>
</html>