Compare commits

...

1 Commits

Author SHA1 Message Date
Flatlogic Bot
6b9021084a 1.0 2025-11-06 09:44:40 +00:00
15 changed files with 733 additions and 159 deletions

0
.perm_test_apache Normal file
View File

0
.perm_test_exec Normal file
View File

6
agents.php Normal file
View File

@ -0,0 +1,6 @@
<?php require_once 'header.php'; ?>
<h1 class="display-5 fw-bold text-center">Manage Agents</h1>
<p class="text-center lead">This section is under construction.</p>
<?php require_once 'footer.php'; ?>

311
ai/LocalAIApi.php Normal file
View File

@ -0,0 +1,311 @@
<?php
// LocalAIApi — proxy client for the Responses API.
// Usage:
// require_once __DIR__ . '/ai/LocalAIApi.php';
// $response = LocalAIApi::createResponse([
// 'input' => [
// ['role' => 'system', 'content' => 'You are a helpful assistant.'],
// ['role' => 'user', 'content' => 'Tell me a bedtime story.'],
// ],
// ]);
// if (!empty($response['success'])) {
// $decoded = LocalAIApi::decodeJsonFromResponse($response);
// }
class LocalAIApi
{
/** @var array<string,mixed>|null */
private static ?array $configCache = null;
/**
* Signature compatible with the OpenAI Responses API.
*
* @param array<string,mixed> $params Request body (model, input, text, reasoning, metadata, etc.).
* @param array<string,mixed> $options Extra options (timeout, verify_tls, headers, path, project_uuid).
* @return array{
* success:bool,
* status?:int,
* data?:mixed,
* error?:string,
* response?:mixed,
* message?:string
* }
*/
public static function createResponse(array $params, array $options = []): array
{
$cfg = self::config();
$payload = $params;
if (empty($payload['input']) || !is_array($payload['input'])) {
return [
'success' => false,
'error' => 'input_missing',
'message' => 'Parameter "input" is required and must be an array.',
];
}
if (!isset($payload['model']) || $payload['model'] === '') {
$payload['model'] = $cfg['default_model'];
}
return self::request($options['path'] ?? null, $payload, $options);
}
/**
* Snake_case alias for createResponse (matches the provided example).
*
* @param array<string,mixed> $params
* @param array<string,mixed> $options
* @return array<string,mixed>
*/
public static function create_response(array $params, array $options = []): array
{
return self::createResponse($params, $options);
}
/**
* Perform a raw request to the AI proxy.
*
* @param string $path Endpoint (may be an absolute URL).
* @param array<string,mixed> $payload JSON payload.
* @param array<string,mixed> $options Additional request options.
* @return array<string,mixed>
*/
public static function request(?string $path = null, array $payload = [], array $options = []): array
{
if (!function_exists('curl_init')) {
return [
'success' => false,
'error' => 'curl_missing',
'message' => 'PHP cURL extension is missing. Install or enable it on the VM.',
];
}
$cfg = self::config();
$projectUuid = $cfg['project_uuid'];
if (empty($projectUuid)) {
return [
'success' => false,
'error' => 'project_uuid_missing',
'message' => 'PROJECT_UUID is not defined; aborting AI request.',
];
}
$defaultPath = $cfg['responses_path'] ?? null;
$resolvedPath = $path ?? ($options['path'] ?? $defaultPath);
if (empty($resolvedPath)) {
return [
'success' => false,
'error' => 'project_id_missing',
'message' => 'PROJECT_ID is not defined; cannot resolve AI proxy endpoint.',
];
}
$url = self::buildUrl($resolvedPath, $cfg['base_url']);
$baseTimeout = isset($cfg['timeout']) ? (int) $cfg['timeout'] : 30;
$timeout = isset($options['timeout']) ? (int) $options['timeout'] : $baseTimeout;
if ($timeout <= 0) {
$timeout = 30;
}
$baseVerifyTls = array_key_exists('verify_tls', $cfg) ? (bool) $cfg['verify_tls'] : true;
$verifyTls = array_key_exists('verify_tls', $options)
? (bool) $options['verify_tls']
: $baseVerifyTls;
$projectHeader = $cfg['project_header'];
$headers = [
'Content-Type: application/json',
'Accept: application/json',
];
$headers[] = $projectHeader . ': ' . $projectUuid;
if (!empty($options['headers']) && is_array($options['headers'])) {
foreach ($options['headers'] as $header) {
if (is_string($header) && $header !== '') {
$headers[] = $header;
}
}
}
if (!empty($projectUuid) && !array_key_exists('project_uuid', $payload)) {
$payload['project_uuid'] = $projectUuid;
}
$body = json_encode($payload, JSON_UNESCAPED_UNICODE);
if ($body === false) {
return [
'success' => false,
'error' => 'json_encode_failed',
'message' => 'Failed to encode request body to JSON.',
];
}
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $verifyTls);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, $verifyTls ? 2 : 0);
curl_setopt($ch, CURLOPT_FAILONERROR, false);
$responseBody = curl_exec($ch);
if ($responseBody === false) {
$error = curl_error($ch) ?: 'Unknown cURL error';
curl_close($ch);
return [
'success' => false,
'error' => 'curl_error',
'message' => $error,
];
}
$status = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
$decoded = null;
if ($responseBody !== '' && $responseBody !== null) {
$decoded = json_decode($responseBody, true);
if (json_last_error() !== JSON_ERROR_NONE) {
$decoded = null;
}
}
if ($status >= 200 && $status < 300) {
return [
'success' => true,
'status' => $status,
'data' => $decoded ?? $responseBody,
];
}
$errorMessage = 'AI proxy request failed';
if (is_array($decoded)) {
$errorMessage = $decoded['error'] ?? $decoded['message'] ?? $errorMessage;
} elseif (is_string($responseBody) && $responseBody !== '') {
$errorMessage = $responseBody;
}
return [
'success' => false,
'status' => $status,
'error' => $errorMessage,
'response' => $decoded ?? $responseBody,
];
}
/**
* Extract plain text from a Responses API payload.
*
* @param array<string,mixed> $response Result of LocalAIApi::createResponse|request.
* @return string
*/
public static function extractText(array $response): string
{
$payload = $response['data'] ?? $response;
if (!is_array($payload)) {
return '';
}
if (!empty($payload['output']) && is_array($payload['output'])) {
$combined = '';
foreach ($payload['output'] as $item) {
if (!isset($item['content']) || !is_array($item['content'])) {
continue;
}
foreach ($item['content'] as $block) {
if (is_array($block) && ($block['type'] ?? '') === 'output_text' && !empty($block['text'])) {
$combined .= $block['text'];
}
}
}
if ($combined !== '') {
return $combined;
}
}
if (!empty($payload['choices'][0]['message']['content'])) {
return (string) $payload['choices'][0]['message']['content'];
}
return '';
}
/**
* Attempt to decode JSON emitted by the model (handles markdown fences).
*
* @param array<string,mixed> $response
* @return array<string,mixed>|null
*/
public static function decodeJsonFromResponse(array $response): ?array
{
$text = self::extractText($response);
if ($text === '') {
return null;
}
$decoded = json_decode($text, true);
if (is_array($decoded)) {
return $decoded;
}
$stripped = preg_replace('/^```json|```$/m', '', trim($text));
if ($stripped !== null && $stripped !== $text) {
$decoded = json_decode($stripped, true);
if (is_array($decoded)) {
return $decoded;
}
}
return null;
}
/**
* Load configuration from ai/config.php.
*
* @return array<string,mixed>
*/
private static function config(): array
{
if (self::$configCache === null) {
$configPath = __DIR__ . '/config.php';
if (!file_exists($configPath)) {
throw new RuntimeException('AI config file not found: ai/config.php');
}
$cfg = require $configPath;
if (!is_array($cfg)) {
throw new RuntimeException('Invalid AI config format: expected array');
}
self::$configCache = $cfg;
}
return self::$configCache;
}
/**
* Build an absolute URL from base_url and a path.
*/
private static function buildUrl(string $path, string $baseUrl): string
{
$trimmed = trim($path);
if ($trimmed === '') {
return $baseUrl;
}
if (str_starts_with($trimmed, 'http://') || str_starts_with($trimmed, 'https://')) {
return $trimmed;
}
if ($trimmed[0] === '/') {
return $baseUrl . $trimmed;
}
return $baseUrl . '/' . $trimmed;
}
}
// Legacy alias for backward compatibility with the previous class name.
if (!class_exists('OpenAIService')) {
class_alias(LocalAIApi::class, 'OpenAIService');
}

52
ai/config.php Normal file
View File

@ -0,0 +1,52 @@
<?php
// OpenAI proxy configuration (workspace scope).
// Reads values from environment variables or executor/.env.
$projectUuid = getenv('PROJECT_UUID');
$projectId = getenv('PROJECT_ID');
if (
($projectUuid === false || $projectUuid === null || $projectUuid === '') ||
($projectId === false || $projectId === null || $projectId === '')
) {
$envPath = realpath(__DIR__ . '/../../.env'); // executor/.env
if ($envPath && is_readable($envPath)) {
$lines = @file($envPath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) ?: [];
foreach ($lines as $line) {
$line = trim($line);
if ($line === '' || $line[0] === '#') {
continue;
}
if (!str_contains($line, '=')) {
continue;
}
[$key, $value] = array_map('trim', explode('=', $line, 2));
if ($key === '') {
continue;
}
$value = trim($value, "\"' ");
if (getenv($key) === false || getenv($key) === '') {
putenv("{$key}={$value}");
}
}
$projectUuid = getenv('PROJECT_UUID');
$projectId = getenv('PROJECT_ID');
}
}
$projectUuid = ($projectUuid === false) ? null : $projectUuid;
$projectId = ($projectId === false) ? null : $projectId;
$baseUrl = 'https://flatlogic.com';
$responsesPath = $projectId ? "/projects/{$projectId}/ai-request" : null;
return [
'base_url' => $baseUrl,
'responses_path' => $responsesPath,
'project_id' => $projectId,
'project_uuid' => $projectUuid,
'project_header' => 'project-uuid',
'default_model' => 'gpt-5',
'timeout' => 30,
'verify_tls' => true,
];

29
assets/css/style.css Normal file
View File

@ -0,0 +1,29 @@
body {
background-color: #f8f9fa;
}
.navbar {
box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
}
.card {
border: none;
border-radius: 0.5rem;
box-shadow: 0 0.25rem 0.75rem rgba(0, 0, 0, 0.05);
transition: transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out;
}
.card:hover {
transform: translateY(-5px);
box-shadow: 0 0.5rem 1.5rem rgba(0, 0, 0, 0.1);
}
.card-title {
font-weight: 600;
}
.stat-number {
font-size: 2.5rem;
font-weight: 700;
color: #0d6efd;
}

6
clients.php Normal file
View File

@ -0,0 +1,6 @@
<?php require_once 'header.php'; ?>
<h1 class="display-5 fw-bold text-center">Manage Clients</h1>
<p class="text-center lead">This section is under construction.</p>
<?php require_once 'footer.php'; ?>

128
database.sql Normal file
View File

@ -0,0 +1,128 @@
-- Create user and grant privileges
DROP USER IF EXISTS 'app_user'@'localhost';
CREATE USER 'app_user'@'localhost' IDENTIFIED BY 'Secure@Password123';
GRANT ALL PRIVILEGES ON real_estate.* TO 'app_user'@'localhost';
FLUSH PRIVILEGES;
-- Create the database
CREATE DATABASE IF NOT EXISTS `real_estate`;
USE `real_estate`;
-- Table structure for table `owner`
CREATE TABLE IF NOT EXISTS `owner` (
`owner_id` INT AUTO_INCREMENT PRIMARY KEY,
`name` VARCHAR(255) NOT NULL,
`contact` VARCHAR(20) UNIQUE,
`email` VARCHAR(255) UNIQUE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- Inserting 5 sample records into `owner`
INSERT INTO `owner` (`name`, `contact`, `email`) VALUES
('John Smith', '111-222-3333', 'john.smith@example.com'),
('Jane Doe', '444-555-6666', 'jane.doe@example.com'),
('Peter Jones', '777-888-9999', 'peter.jones@example.com'),
('Mary Williams', '123-456-7890', 'mary.williams@example.com'),
('David Brown', '098-765-4321', 'david.brown@example.com');
-- Table structure for table `agent`
CREATE TABLE IF NOT EXISTS `agent` (
`agent_id` INT AUTO_INCREMENT PRIMARY KEY,
`name` VARCHAR(255) NOT NULL,
`contact` VARCHAR(20) UNIQUE,
`email` VARCHAR(255) UNIQUE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- Inserting 5 sample records into `agent`
INSERT INTO `agent` (`name`, `contact`, `email`) VALUES
('Agent Alice', '101-202-3030', 'alice@realestate.com'),
('Agent Bob', '404-505-6060', 'bob@realestate.com'),
('Agent Charlie', '707-808-9090', 'charlie@realestate.com'),
('Agent Diana', '112-223-3344', 'diana@realestate.com'),
('Agent Eve', '556-667-7788', 'eve@realestate.com');
-- Table structure for table `clients`
CREATE TABLE IF NOT EXISTS `clients` (
`client_id` INT AUTO_INCREMENT PRIMARY KEY,
`name` VARCHAR(255) NOT NULL,
`phone` VARCHAR(20) UNIQUE,
`email` VARCHAR(255) UNIQUE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- Inserting 5 sample records into `clients`
INSERT INTO `clients` (`name`, `phone`, `email`) VALUES
('Client Chris', '121-232-3434', 'chris.c@client.com'),
('Client Dana', '454-565-6767', 'dana.d@client.com'),
('Client Frank', '787-898-9090', 'frank.f@client.com'),
('Client Grace', '212-323-4343', 'grace.g@client.com'),
('Client Heidi', '545-656-7676', 'heidi.h@client.com');
-- Table structure for table `properties`
CREATE TABLE IF NOT EXISTS `properties` (
`property_id` INT AUTO_INCREMENT PRIMARY KEY,
`title` VARCHAR(255) NOT NULL,
`type` VARCHAR(50) NOT NULL,
`address` VARCHAR(255) NOT NULL,
`city` VARCHAR(100) NOT NULL,
`price` DECIMAL(12, 2) NOT NULL,
`status` ENUM('Available', 'Sold', 'Rented') NOT NULL DEFAULT 'Available',
`owner_id` INT,
`agent_id` INT,
CONSTRAINT `fk_owner` FOREIGN KEY (`owner_id`) REFERENCES `owner` (`owner_id`) ON DELETE SET NULL,
CONSTRAINT `fk_agent` FOREIGN KEY (`agent_id`) REFERENCES `agent` (`agent_id`) ON DELETE SET NULL,
CHECK (`price` > 0)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- Inserting 5 sample records into `properties`
INSERT INTO `properties` (`title`, `type`, `address`, `city`, `price`, `status`, `owner_id`, `agent_id`) VALUES
('Modern Downtown Loft', 'House', '123 Main St', 'Metropolis', 1200000.00, 'Available', 1, 1),
('Suburban Family Home', 'House', '456 Oak Ave', 'Smallville', 750000.00, 'Available', 2, 2),
('Luxury Beachfront Villa', 'House', '789 Ocean Dr', 'Coast City', 2500000.00, 'Sold', 3, 1),
('Cozy Countryside Cottage', 'House', '101 Pine Ln', 'Green Valley', 450000.00, 'Rented', 4, 3),
('High-Rise Apartment', 'Apartment', '212 Sky Blvd', 'Metropolis', 950000.00, 'Available', 5, 4);
-- Table structure for table `transactions`
CREATE TABLE IF NOT EXISTS `transactions` (
`transaction_id` INT AUTO_INCREMENT PRIMARY KEY,
`type` ENUM('Sale', 'Rent') NOT NULL,
`amount` DECIMAL(12, 2) NOT NULL,
`date` DATE NOT NULL,
`property_id` INT,
`agent_id` INT,
`client_id` INT,
UNIQUE (`property_id`), -- A property can only be in one final transaction
CONSTRAINT `fk_trans_property` FOREIGN KEY (`property_id`) REFERENCES `properties` (`property_id`) ON DELETE CASCADE,
CONSTRAINT `fk_trans_agent` FOREIGN KEY (`agent_id`) REFERENCES `agent` (`agent_id`) ON DELETE SET NULL,
CONSTRAINT `fk_trans_client` FOREIGN KEY (`client_id`) REFERENCES `clients` (`client_id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- Inserting 2 sample records into `transactions`
INSERT INTO `transactions` (`type`, `amount`, `date`, `property_id`, `agent_id`, `client_id`) VALUES
('Sale', 2450000.00, '2024-05-20', 3, 1, 3),
('Rent', 3000.00, '2024-06-01', 4, 3, 4);
-- SQL Views
-- View for Available Properties
CREATE OR REPLACE VIEW `AvailableProperties` AS
SELECT `property_id`, `title`, `type`, `address`, `city`, `price`
FROM `properties`
WHERE `status` = 'Available';
-- View for High Value Properties
CREATE OR REPLACE VIEW `HighValueProperties` AS
SELECT `property_id`, `title`, `type`, `address`, `city`, `price`
FROM `properties`
WHERE `price` > 1000000;
-- Trigger to update property status after a transaction
DELIMITER $$
CREATE TRIGGER `after_transaction_insert`
AFTER INSERT ON `transactions`
FOR EACH ROW
BEGIN
IF NEW.type = 'Sale' THEN
UPDATE `properties` SET `status` = 'Sold' WHERE `property_id` = NEW.property_id;
ELSEIF NEW.type = 'Rent' THEN
UPDATE `properties` SET `status` = 'Rented' WHERE `property_id` = NEW.property_id;
END IF;
END$$
DELIMITER ;

View File

@ -1,17 +1,23 @@
<?php <?php
// Generated by setup_mariadb_project.sh — edit as needed. // Database configuration
define('DB_HOST', '127.0.0.1'); define('DB_HOST', '127.0.0.1');
define('DB_NAME', 'app_31009'); define('DB_NAME', 'real_estate');
define('DB_USER', 'app_31009'); define('DB_USER', 'app_user');
define('DB_PASS', '2c66b530-2a65-423a-a106-6760b49ad1a2'); define('DB_PASS', 'Secure@Password123');
function db() { /**
static $pdo; * Establishes a database connection using PDO.
if (!$pdo) { * @return PDO A PDO database connection object.
$pdo = new PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME.';charset=utf8mb4', DB_USER, DB_PASS, [ */
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, function db_connect() {
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, try {
]); $pdoconn = new PDO('mysql:host=' . DB_HOST . ';dbname=' . DB_NAME, DB_USER, DB_PASS);
} $pdoconn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return $pdo; return $pdoconn;
} catch (PDOException $e) {
// In a real app, you'd log this error and show a generic message
die("Database connection failed: " . $e->getMessage());
}
} }
// You can now include this file and call db_connect() to get a database connection.

10
footer.php Normal file
View File

@ -0,0 +1,10 @@
</main>
<footer class="container mt-4 text-center text-muted">
<p>&copy; <?php echo date('Y'); ?> Real Estate Management. Built with <a href="https://flatlogic.com">Flatlogic</a>.</p>
</footer>
<!-- Scripts -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

65
header.php Normal file
View File

@ -0,0 +1,65 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Real Estate Management</title>
<meta name="description" content="A full-stack Real Estate Management System built with PHP and Bootstrap.">
<meta name="keywords" content="real estate, property management, php, bootstrap, flatlogic, web application">
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website">
<meta property="og:title" content="Real Estate Management">
<meta property="og:description" content="A full-stack Real Estate Management System built with PHP and Bootstrap.">
<!-- Twitter -->
<meta property="twitter:card" content="summary_large_image">
<meta property="twitter:title" content="Real Estate Management">
<meta property="twitter:description" content="A full-stack Real Estate Management System built with PHP and Bootstrap.">
<!-- Styles -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
<link rel="stylesheet" href="assets/css/style.css">
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-light bg-white sticky-top">
<div class="container-fluid">
<a class="navbar-brand" href="index.php">
<i class="bi bi-buildings-fill text-primary"></i> Real Estate
</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link" href="index.php">Dashboard</a>
</li>
<li class="nav-item">
<a class="nav-link" href="properties.php">Properties</a>
</li>
<li class="nav-item">
<a class="nav-link" href="agents.php">Agents</a>
</li>
<li class="nav-item">
<a class="nav-link" href="owners.php">Owners</a>
</li>
<li class="nav-item">
<a class="nav-link" href="clients.php">Clients</a>
</li>
<li class="nav-item">
<a class="nav-link" href="transactions.php">Transactions</a>
</li>
</ul>
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="#logout">Logout</a>
</li>
</ul>
</div>
</div>
</nav>
<main class="container mt-4">

235
index.php
View File

@ -1,150 +1,93 @@
<?php <?php
declare(strict_types=1); require_once 'header.php';
@ini_set('display_errors', '1'); require_once 'db/config.php';
@error_reporting(E_ALL);
@date_default_timezone_set('UTC'); // Fetch stats from the database
$pdo = db_connect();
$counts = [
'properties' => 0,
'agents' => 0,
'owners' => 0,
'clients' => 0,
'transactions' => 0
];
try {
$counts['properties'] = $pdo->query('SELECT count(*) FROM properties')->fetchColumn();
$counts['agents'] = $pdo->query('SELECT count(*) FROM agent')->fetchColumn();
$counts['owners'] = $pdo->query('SELECT count(*) FROM owner')->fetchColumn();
$counts['clients'] = $pdo->query('SELECT count(*) FROM clients')->fetchColumn();
$counts['transactions'] = $pdo->query('SELECT count(*) FROM transactions')->fetchColumn();
} catch (PDOException $e) {
// If the tables don't exist yet, we can just show 0.
// On a real site, you would log this error.
}
$phpVersion = PHP_VERSION;
$now = date('Y-m-d H:i:s');
?> ?>
<!doctype html>
<html lang="en"> <div class="px-4 py-5 my-5 text-center">
<head> <h1 class="display-5 fw-bold">Admin Dashboard</h1>
<meta charset="utf-8" /> <div class="col-lg-6 mx-auto">
<meta name="viewport" content="width=device-width, initial-scale=1" /> <p class="lead mb-4">Welcome to the Real Estate Management System. Here you can manage properties, clients, and transactions.</p>
<title>New Style</title>
<?php
// Read project preview data from environment
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? '';
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
?>
<?php if ($projectDescription): ?>
<!-- Meta description -->
<meta name="description" content='<?= htmlspecialchars($projectDescription) ?>' />
<!-- Open Graph meta tags -->
<meta property="og:description" content="<?= htmlspecialchars($projectDescription) ?>" />
<!-- Twitter meta tags -->
<meta property="twitter:description" content="<?= htmlspecialchars($projectDescription) ?>" />
<?php endif; ?>
<?php if ($projectImageUrl): ?>
<!-- Open Graph image -->
<meta property="og:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
<!-- Twitter image -->
<meta property="twitter:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
<?php endif; ?>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet">
<style>
:root {
--bg-color-start: #6a11cb;
--bg-color-end: #2575fc;
--text-color: #ffffff;
--card-bg-color: rgba(255, 255, 255, 0.01);
--card-border-color: rgba(255, 255, 255, 0.1);
}
body {
margin: 0;
font-family: 'Inter', sans-serif;
background: linear-gradient(45deg, var(--bg-color-start), var(--bg-color-end));
color: var(--text-color);
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
text-align: center;
overflow: hidden;
position: relative;
}
body::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100"><path d="M-10 10L110 10M10 -10L10 110" stroke-width="1" stroke="rgba(255,255,255,0.05)"/></svg>');
animation: bg-pan 20s linear infinite;
z-index: -1;
}
@keyframes bg-pan {
0% { background-position: 0% 0%; }
100% { background-position: 100% 100%; }
}
main {
padding: 2rem;
}
.card {
background: var(--card-bg-color);
border: 1px solid var(--card-border-color);
border-radius: 16px;
padding: 2rem;
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.1);
}
.loader {
margin: 1.25rem auto 1.25rem;
width: 48px;
height: 48px;
border: 3px solid rgba(255, 255, 255, 0.25);
border-top-color: #fff;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.hint {
opacity: 0.9;
}
.sr-only {
position: absolute;
width: 1px; height: 1px;
padding: 0; margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap; border: 0;
}
h1 {
font-size: 3rem;
font-weight: 700;
margin: 0 0 1rem;
letter-spacing: -1px;
}
p {
margin: 0.5rem 0;
font-size: 1.1rem;
}
code {
background: rgba(0,0,0,0.2);
padding: 2px 6px;
border-radius: 4px;
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
footer {
position: absolute;
bottom: 1rem;
font-size: 0.8rem;
opacity: 0.7;
}
</style>
</head>
<body>
<main>
<div class="card">
<h1>Analyzing your requirements and generating your website…</h1>
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes">
<span class="sr-only">Loading…</span>
</div>
<p class="hint"><?= ($_SERVER['HTTP_HOST'] ?? '') === 'appwizzy.com' ? 'AppWizzy' : 'Flatlogic' ?> AI is collecting your requirements and applying the first changes.</p>
<p class="hint">This page will update automatically as the plan is implemented.</p>
<p>Runtime: PHP <code><?= htmlspecialchars($phpVersion) ?></code> — UTC <code><?= htmlspecialchars($now) ?></code></p>
</div> </div>
</main> </div>
<footer>
Page updated: <?= htmlspecialchars($now) ?> (UTC) <div class="row">
</footer> <!-- Properties Card -->
</body> <div class="col-md-4 col-lg mb-4">
</html> <div class="card h-100">
<div class="card-body text-center">
<h5 class="card-title">Total Properties</h5>
<p class="stat-number"><?php echo $counts['properties']; ?></p>
<a href="properties.php" class="btn btn-primary">Manage</a>
</div>
</div>
</div>
<!-- Agents Card -->
<div class="col-md-4 col-lg mb-4">
<div class="card h-100">
<div class="card-body text-center">
<h5 class="card-title">Total Agents</h5>
<p class="stat-number"><?php echo $counts['agents']; ?></p>
<a href="agents.php" class="btn btn-primary">Manage</a>
</div>
</div>
</div>
<!-- Owners Card -->
<div class="col-md-4 col-lg mb-4">
<div class="card h-100">
<div class="card-body text-center">
<h5 class="card-title">Total Owners</h5>
<p class="stat-number"><?php echo $counts['owners']; ?></p>
<a href="owners.php" class="btn btn-primary">Manage</a>
</div>
</div>
</div>
<!-- Clients Card -->
<div class="col-md-6 col-lg mb-4">
<div class="card h-100">
<div class="card-body text-center">
<h5 class="card-title">Total Clients</h5>
<p class="stat-number"><?php echo $counts['clients']; ?></p>
<a href="clients.php" class="btn btn-primary">Manage</a>
</div>
</div>
</div>
<!-- Transactions Card -->
<div class="col-md-6 col-lg mb-4">
<div class="card h-100">
<div class="card-body text-center">
<h5 class="card-title">Total Transactions</h5>
<p class="stat-number"><?php echo $counts['transactions']; ?></p>
<a href="transactions.php" class="btn btn-primary">Manage</a>
</div>
</div>
</div>
</div>
<?php require_once 'footer.php'; ?>

6
owners.php Normal file
View File

@ -0,0 +1,6 @@
<?php require_once 'header.php'; ?>
<h1 class="display-5 fw-bold text-center">Manage Owners</h1>
<p class="text-center lead">This section is under construction.</p>
<?php require_once 'footer.php'; ?>

6
properties.php Normal file
View File

@ -0,0 +1,6 @@
<?php require_once 'header.php'; ?>
<h1 class="display-5 fw-bold text-center">Manage Properties</h1>
<p class="text-center lead">This section is under construction.</p>
<?php require_once 'footer.php'; ?>

6
transactions.php Normal file
View File

@ -0,0 +1,6 @@
<?php require_once 'header.php'; ?>
<h1 class="display-5 fw-bold text-center">Manage Transactions</h1>
<p class="text-center lead">This section is under construction.</p>
<?php require_once 'footer.php'; ?>