- Added GitHub spec-kit for development workflow - Standardized file signatures to Descomplicar® format - Updated development configuration 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
278 lines
10 KiB
PHP
278 lines
10 KiB
PHP
/**
|
|
* Descomplicar® Crescimento Digital
|
|
* https://descomplicar.pt
|
|
*/
|
|
|
|
<?php defined('BASEPATH') or exit('No direct script access allowed'); ?>
|
|
|
|
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<meta name="description" content="Desk-Moloni Client Portal - Access your documents securely">
|
|
<meta name="robots" content="noindex, nofollow">
|
|
|
|
<!-- CSRF Token -->
|
|
<?php if (function_exists('get_instance')) : ?>
|
|
<?php $CI = &get_instance(); ?>
|
|
<meta name="csrf-token" content="<?php echo $CI->security->get_csrf_hash(); ?>">
|
|
<?php endif; ?>
|
|
|
|
<title>Desk-Moloni Client Portal</title>
|
|
|
|
<!-- Favicon -->
|
|
<link rel="icon" type="image/x-icon" href="<?php echo base_url('assets/images/favicon.ico'); ?>">
|
|
|
|
<!-- Preload critical resources -->
|
|
<link rel="preload" href="<?php echo module_dir_url('desk_moloni', 'client_portal/dist/assets/'); ?>index.css" as="style">
|
|
<link rel="preload" href="<?php echo module_dir_url('desk_moloni', 'client_portal/dist/assets/'); ?>index.js" as="script">
|
|
|
|
<!-- CSS -->
|
|
<?php
|
|
// Get all CSS files from dist/assets
|
|
$assets_dir = FCPATH . 'modules/desk_moloni/client_portal/dist/assets/';
|
|
if (is_dir($assets_dir)) {
|
|
$css_files = glob($assets_dir . '*.css');
|
|
foreach ($css_files as $css_file) {
|
|
$filename = basename($css_file);
|
|
echo '<link rel="stylesheet" href="' . module_dir_url('desk_moloni', 'client_portal/dist/assets/' . $filename) . '">' . "\n ";
|
|
}
|
|
}
|
|
?>
|
|
|
|
<!-- Custom CSS for Perfex integration -->
|
|
<style>
|
|
body {
|
|
margin: 0;
|
|
padding: 0;
|
|
font-family: inherit;
|
|
}
|
|
|
|
#desk-moloni-app {
|
|
min-height: 100vh;
|
|
}
|
|
|
|
/* Loading spinner */
|
|
.desk-moloni-loading {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
height: 100vh;
|
|
background: #f9fafb;
|
|
}
|
|
|
|
.desk-moloni-loading .spinner {
|
|
width: 40px;
|
|
height: 40px;
|
|
border: 4px solid #e5e7eb;
|
|
border-left: 4px solid #2563eb;
|
|
border-radius: 50%;
|
|
animation: spin 1s linear infinite;
|
|
}
|
|
|
|
@keyframes spin {
|
|
0% { transform: rotate(0deg); }
|
|
100% { transform: rotate(360deg); }
|
|
}
|
|
|
|
.desk-moloni-loading .text {
|
|
margin-left: 12px;
|
|
color: #6b7280;
|
|
font-size: 14px;
|
|
}
|
|
|
|
/* Error state */
|
|
.desk-moloni-error {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
height: 100vh;
|
|
background: #f9fafb;
|
|
padding: 20px;
|
|
text-align: center;
|
|
}
|
|
|
|
.desk-moloni-error .error-content {
|
|
max-width: 400px;
|
|
}
|
|
|
|
.desk-moloni-error .error-icon {
|
|
width: 64px;
|
|
height: 64px;
|
|
margin: 0 auto 16px;
|
|
color: #ef4444;
|
|
}
|
|
|
|
.desk-moloni-error .error-title {
|
|
font-size: 20px;
|
|
font-weight: 600;
|
|
color: #111827;
|
|
margin-bottom: 8px;
|
|
}
|
|
|
|
.desk-moloni-error .error-message {
|
|
color: #6b7280;
|
|
margin-bottom: 16px;
|
|
}
|
|
|
|
.desk-moloni-error .error-retry {
|
|
background: #2563eb;
|
|
color: white;
|
|
border: none;
|
|
padding: 8px 16px;
|
|
border-radius: 6px;
|
|
cursor: pointer;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.desk-moloni-error .error-retry:hover {
|
|
background: #1d4ed8;
|
|
}
|
|
|
|
/* Responsive adjustments */
|
|
@media (max-width: 768px) {
|
|
.desk-moloni-loading,
|
|
.desk-moloni-error {
|
|
height: calc(100vh - 60px); /* Account for mobile browser bars */
|
|
}
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<!-- Client authentication check -->
|
|
<?php if (!is_client_logged_in()) : ?>
|
|
<div class="desk-moloni-error">
|
|
<div class="error-content">
|
|
<svg class="error-icon" fill="currentColor" viewBox="0 0 24 24">
|
|
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/>
|
|
</svg>
|
|
<div class="error-title">Authentication Required</div>
|
|
<div class="error-message">
|
|
To access the Desk-Moloni client portal, you need to be logged in through the main Perfex CRM system.
|
|
</div>
|
|
<button class="error-retry" onclick="window.location.href='/clients/login'">
|
|
Go to Login
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<?php else : ?>
|
|
<!-- Vue.js App Container -->
|
|
<div id="desk-moloni-app">
|
|
<!-- Loading state while Vue.js loads -->
|
|
<div class="desk-moloni-loading">
|
|
<div class="spinner"></div>
|
|
<div class="text">Loading Desk-Moloni Portal...</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Error fallback (shown if Vue.js fails to load) -->
|
|
<div id="desk-moloni-error" class="desk-moloni-error" style="display: none;">
|
|
<div class="error-content">
|
|
<svg class="error-icon" fill="currentColor" viewBox="0 0 24 24">
|
|
<path d="M12 2C17.52 2 22 6.48 22 12S17.52 22 12 22 2 17.52 2 12 6.48 2 12 2M15.54 9L14 10.54l-1.46-1.46L11 10.54 12.46 12l-1.46 1.46L12 14.54l1.46-1.46L15 14.54l-1.46-1.46L15.54 12l-1.46-1.46L15.54 9M8.5 11A1.5 1.5 0 0 0 7 12.5A1.5 1.5 0 0 0 8.5 14A1.5 1.5 0 0 0 10 12.5A1.5 1.5 0 0 0 8.5 11Z"/>
|
|
</svg>
|
|
<div class="error-title">Loading Error</div>
|
|
<div class="error-message">
|
|
There was an error loading the document portal. Please try refreshing the page.
|
|
</div>
|
|
<button class="error-retry" onclick="window.location.reload()">
|
|
Refresh Page
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Client Data for Vue.js -->
|
|
<script>
|
|
// Pass client data to Vue.js application
|
|
window.DESK_MOLONI_CONFIG = {
|
|
clientId: <?php echo get_client_user_id(); ?>,
|
|
clientName: '<?php echo htmlspecialchars(get_client_name()); ?>',
|
|
clientEmail: '<?php echo htmlspecialchars(get_client_email()); ?>',
|
|
baseUrl: '<?php echo site_url('clients/desk_moloni'); ?>',
|
|
apiUrl: '/clients/desk_moloni',
|
|
csrfToken: '<?php echo $CI->security->get_csrf_hash(); ?>',
|
|
locale: '<?php echo get_locale(); ?>',
|
|
currency: '<?php echo get_base_currency()->name; ?>',
|
|
permissions: {
|
|
viewDocuments: true,
|
|
downloadDocuments: true,
|
|
viewDashboard: true,
|
|
viewNotifications: true
|
|
},
|
|
features: {
|
|
pdfPreview: true,
|
|
advancedSearch: true,
|
|
notifications: true
|
|
}
|
|
};
|
|
|
|
// Error handling
|
|
window.addEventListener('error', function(event) {
|
|
console.error('Desk-Moloni Error:', event.error);
|
|
|
|
// Show error fallback after 5 seconds if Vue doesn't load
|
|
setTimeout(function() {
|
|
if (!window.Vue) {
|
|
document.getElementById('desk-moloni-app').style.display = 'none';
|
|
document.getElementById('desk-moloni-error').style.display = 'flex';
|
|
}
|
|
}, 5000);
|
|
});
|
|
|
|
// Vue.js loaded indicator
|
|
window.addEventListener('DOMContentLoaded', function() {
|
|
setTimeout(function() {
|
|
if (!document.querySelector('#desk-moloni-app > div:not(.desk-moloni-loading)')) {
|
|
document.getElementById('desk-moloni-app').style.display = 'none';
|
|
document.getElementById('desk-moloni-error').style.display = 'flex';
|
|
}
|
|
}, 10000); // Show error after 10 seconds if Vue doesn't mount
|
|
});
|
|
</script>
|
|
|
|
<!-- Vue.js Application Scripts -->
|
|
<?php
|
|
// Get all JS files from dist/assets
|
|
$js_files = glob($assets_dir . '*.js');
|
|
|
|
// Sort files to ensure vendor loads first, then main app
|
|
usort($js_files, function($a, $b) {
|
|
$a_name = basename($a);
|
|
$b_name = basename($b);
|
|
|
|
if (strpos($a_name, 'vendor') !== false) return -1;
|
|
if (strpos($b_name, 'vendor') !== false) return 1;
|
|
if (strpos($a_name, 'index') !== false) return 1;
|
|
if (strpos($b_name, 'index') !== false) return -1;
|
|
|
|
return strcmp($a_name, $b_name);
|
|
});
|
|
|
|
foreach ($js_files as $js_file) {
|
|
$filename = basename($js_file);
|
|
echo '<script type="module" src="' . module_dir_url('desk_moloni', 'client_portal/dist/assets/' . $filename) . '"></script>' . "\n ";
|
|
}
|
|
?>
|
|
|
|
<?php endif; ?>
|
|
|
|
<!-- Performance monitoring -->
|
|
<script>
|
|
// Basic performance monitoring
|
|
window.addEventListener('load', function() {
|
|
if (window.performance && window.performance.timing) {
|
|
const perfData = window.performance.timing;
|
|
const pageLoadTime = perfData.loadEventEnd - perfData.navigationStart;
|
|
|
|
console.log('Desk-Moloni Portal Load Time:', pageLoadTime + 'ms');
|
|
|
|
// Log performance data (in production, send to analytics)
|
|
if (pageLoadTime > 5000) {
|
|
console.warn('Slow page load detected:', pageLoadTime + 'ms');
|
|
}
|
|
}
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|