Compare commits

...

1 Commits

Author SHA1 Message Date
Flatlogic Bot
ae86c5cba1 Autosave: 20260405-115130 2026-04-05 11:51:30 +00:00
5 changed files with 1490 additions and 517 deletions

View File

@ -1,403 +1,490 @@
body {
background: linear-gradient(-45deg, #ee7752, #e73c7e, #23a6d5, #23d5ab);
background-size: 400% 400%;
animation: gradient 15s ease infinite;
color: #212529;
font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
font-size: 14px;
margin: 0;
min-height: 100vh;
:root {
--bg: #f4f7fb;
--surface: rgba(255, 255, 255, 0.9);
--surface-strong: #ffffff;
--text: #101828;
--muted: #667085;
--line: #d7dfeb;
--primary: #0f766e;
--primary-strong: #115e59;
--primary-soft: rgba(15, 118, 110, 0.12);
--danger: #b42318;
--danger-soft: rgba(180, 35, 24, 0.12);
--success: #027a48;
--success-soft: rgba(2, 122, 72, 0.12);
--shadow: 0 24px 60px rgba(15, 23, 42, 0.09);
--radius: 24px;
}
.main-wrapper {
body[data-theme="dark"] {
--bg: #07111f;
--surface: rgba(10, 18, 32, 0.88);
--surface-strong: #0f172a;
--text: #eef2ff;
--muted: #9fb0c7;
--line: rgba(148, 163, 184, 0.22);
--primary: #34d399;
--primary-strong: #10b981;
--primary-soft: rgba(52, 211, 153, 0.16);
--danger: #f97066;
--danger-soft: rgba(249, 112, 102, 0.16);
--success: #32d583;
--success-soft: rgba(50, 213, 131, 0.16);
--shadow: 0 24px 60px rgba(2, 6, 23, 0.45);
}
* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
margin: 0;
font-family: "Segoe UI", Tahoma, Arial, sans-serif;
background:
radial-gradient(circle at top right, rgba(15, 118, 110, 0.14), transparent 30%),
radial-gradient(circle at left bottom, rgba(14, 165, 233, 0.10), transparent 30%),
var(--bg);
color: var(--text);
}
a { color: inherit; text-decoration: none; }
button, input, select, textarea {
font: inherit;
}
button {
cursor: pointer;
border: 0;
}
.page-shell {
width: min(1200px, calc(100% - 32px));
margin: 0 auto;
padding: 28px 0 40px;
}
.topbar,
.hero-card,
.panel-card,
.login-card,
.preview-card,
.stat-card,
.help-card {
backdrop-filter: blur(14px);
background: var(--surface);
border: 1px solid var(--line);
box-shadow: var(--shadow);
}
.topbar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 18px;
padding: 22px 24px;
border-radius: var(--radius);
margin-bottom: 24px;
position: sticky;
top: 12px;
z-index: 20;
}
.eyebrow {
margin: 0 0 6px;
font-size: 12px;
text-transform: uppercase;
letter-spacing: .18em;
color: var(--muted);
}
h1, h2, h3, p {
margin-top: 0;
}
h1 {
margin-bottom: 8px;
font-size: clamp(28px, 3vw, 40px);
}
.subtitle {
margin: 0;
color: var(--muted);
max-width: 780px;
}
.topbar-actions,
.hero-actions,
.inline-actions {
display: flex;
gap: 10px;
flex-wrap: wrap;
}
.ghost-btn,
.primary-btn,
.primary-link,
.tab-link {
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 100vh;
width: 100%;
padding: 20px;
box-sizing: border-box;
position: relative;
z-index: 1;
}
@keyframes gradient {
0% {
background-position: 0% 50%;
}
50% {
background-position: 100% 50%;
}
100% {
background-position: 0% 50%;
}
}
.chat-container {
width: 100%;
max-width: 600px;
background: rgba(255, 255, 255, 0.85);
border: 1px solid rgba(255, 255, 255, 0.3);
border-radius: 20px;
display: flex;
flex-direction: column;
height: 85vh;
box-shadow: 0 20px 40px rgba(0,0,0,0.2);
backdrop-filter: blur(15px);
-webkit-backdrop-filter: blur(15px);
overflow: hidden;
}
.chat-header {
padding: 1.5rem;
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
background: rgba(255, 255, 255, 0.5);
font-weight: 700;
font-size: 1.1rem;
display: flex;
justify-content: space-between;
align-items: center;
}
.chat-messages {
flex: 1;
overflow-y: auto;
padding: 1.5rem;
display: flex;
flex-direction: column;
gap: 1.25rem;
}
/* Custom Scrollbar */
::-webkit-scrollbar {
width: 6px;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.3);
border-radius: 10px;
}
::-webkit-scrollbar-thumb:hover {
background: rgba(255, 255, 255, 0.5);
}
.message {
max-width: 85%;
padding: 0.85rem 1.1rem;
min-height: 46px;
padding: 0 18px;
border-radius: 16px;
line-height: 1.5;
font-size: 0.95rem;
box-shadow: 0 4px 15px rgba(0,0,0,0.05);
animation: fadeIn 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
transition: .2s ease;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(20px) scale(0.95); }
to { opacity: 1; transform: translateY(0) scale(1); }
.ghost-btn {
background: var(--surface-strong);
color: var(--text);
border: 1px solid var(--line);
}
.message.visitor {
align-self: flex-end;
background: linear-gradient(135deg, #212529 0%, #343a40 100%);
color: #fff;
border-bottom-right-radius: 4px;
.primary-btn,
.primary-link,
.tab-link.is-active,
.secondary-tone {
background: linear-gradient(135deg, var(--primary), var(--primary-strong));
color: #ffffff;
box-shadow: 0 14px 28px rgba(15, 118, 110, 0.22);
}
.message.bot {
align-self: flex-start;
background: #ffffff;
color: #212529;
border-bottom-left-radius: 4px;
.primary-btn:hover,
.primary-link:hover,
.ghost-btn:hover,
.tab-link:hover {
transform: translateY(-1px);
}
.chat-input-area {
padding: 1.25rem;
background: rgba(255, 255, 255, 0.5);
border-top: 1px solid rgba(0, 0, 0, 0.05);
.secondary-tone {
border: none;
}
.chat-input-area form {
.login-layout,
.grid.two-col,
.reports-grid {
display: grid;
gap: 22px;
}
.login-layout,
.grid.two-col {
grid-template-columns: 1.05fr .95fr;
}
.login-card,
.preview-card,
.hero-card,
.panel-card,
.stat-card {
border-radius: 28px;
padding: 24px;
}
.card-head {
margin-bottom: 18px;
}
.card-head p,
.help-note,
.empty-state,
.empty-cell,
.summary-list li span,
.mini-item p,
.stat-inline span,
.invoice-note {
color: var(--muted);
}
.demo-box,
.invoice-preview-box,
.invoice-row,
.invoice-item,
.summary-list li,
.mini-item,
.stat-inline {
display: flex;
gap: 0.75rem;
align-items: center;
justify-content: space-between;
gap: 12px;
padding: 14px 16px;
background: var(--surface-strong);
border: 1px solid var(--line);
border-radius: 18px;
}
.chat-input-area input {
flex: 1;
border: 1px solid rgba(0, 0, 0, 0.1);
border-radius: 12px;
padding: 0.75rem 1rem;
.gradient-card {
background:
linear-gradient(145deg, rgba(15, 118, 110, 0.16), rgba(14, 165, 233, 0.08)),
var(--surface);
}
.gradient-card ul {
margin: 18px 0 0;
padding-inline-start: 20px;
line-height: 1.9;
}
.stack-form {
display: grid;
gap: 14px;
}
label {
display: grid;
gap: 8px;
font-weight: 600;
}
input,
select,
textarea {
width: 100%;
border: 1px solid var(--line);
background: var(--surface-strong);
color: var(--text);
border-radius: 16px;
padding: 14px 16px;
outline: none;
background: rgba(255, 255, 255, 0.9);
transition: all 0.3s ease;
transition: border-color .2s ease, box-shadow .2s ease;
}
.chat-input-area input:focus {
border-color: #23a6d5;
box-shadow: 0 0 0 3px rgba(35, 166, 213, 0.2);
input:focus,
select:focus,
textarea:focus {
border-color: var(--primary);
box-shadow: 0 0 0 4px var(--primary-soft);
}
.chat-input-area button {
background: #212529;
color: #fff;
border: none;
padding: 0.75rem 1.5rem;
border-radius: 12px;
cursor: pointer;
font-weight: 600;
transition: all 0.3s ease;
.split-fields {
display: grid;
gap: 12px;
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.chat-input-area button:hover {
background: #000;
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
.app-layout {
display: grid;
gap: 22px;
}
/* Background Animations */
.bg-animations {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 0;
overflow: hidden;
pointer-events: none;
}
.blob {
position: absolute;
width: 500px;
height: 500px;
background: rgba(255, 255, 255, 0.2);
border-radius: 50%;
filter: blur(80px);
animation: move 20s infinite alternate cubic-bezier(0.45, 0, 0.55, 1);
}
.blob-1 {
top: -10%;
left: -10%;
background: rgba(238, 119, 82, 0.4);
}
.blob-2 {
bottom: -10%;
right: -10%;
background: rgba(35, 166, 213, 0.4);
animation-delay: -7s;
width: 600px;
height: 600px;
}
.blob-3 {
top: 40%;
left: 30%;
background: rgba(231, 60, 126, 0.3);
animation-delay: -14s;
width: 450px;
height: 450px;
}
@keyframes move {
0% { transform: translate(0, 0) rotate(0deg) scale(1); }
33% { transform: translate(150px, 100px) rotate(120deg) scale(1.1); }
66% { transform: translate(-50px, 200px) rotate(240deg) scale(0.9); }
100% { transform: translate(0, 0) rotate(360deg) scale(1); }
}
.header-link {
font-size: 14px;
color: #fff;
text-decoration: none;
background: rgba(0, 0, 0, 0.2);
padding: 0.5rem 1rem;
border-radius: 8px;
transition: all 0.3s ease;
}
.header-link:hover {
background: rgba(0, 0, 0, 0.4);
text-decoration: none;
}
/* Admin Styles */
.admin-container {
max-width: 900px;
margin: 3rem auto;
padding: 2.5rem;
background: rgba(255, 255, 255, 0.85);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border-radius: 24px;
box-shadow: 0 20px 50px rgba(0,0,0,0.15);
border: 1px solid rgba(255, 255, 255, 0.4);
position: relative;
z-index: 1;
}
.admin-container h1 {
margin-top: 0;
color: #212529;
font-weight: 800;
}
.table {
width: 100%;
border-collapse: separate;
border-spacing: 0 8px;
margin-top: 1.5rem;
}
.table th {
background: transparent;
border: none;
padding: 1rem;
color: #6c757d;
font-weight: 600;
text-transform: uppercase;
font-size: 0.75rem;
letter-spacing: 1px;
}
.table td {
background: #fff;
padding: 1rem;
border: none;
}
.table tr td:first-child { border-radius: 12px 0 0 12px; }
.table tr td:last-child { border-radius: 0 12px 12px 0; }
.form-group {
margin-bottom: 1.25rem;
}
.form-group label {
display: block;
margin-bottom: 0.5rem;
font-weight: 600;
font-size: 0.9rem;
}
.form-control {
width: 100%;
padding: 0.75rem 1rem;
border: 1px solid rgba(0, 0, 0, 0.1);
border-radius: 12px;
background: #fff;
transition: all 0.3s ease;
box-sizing: border-box;
}
.form-control:focus {
outline: none;
border-color: #23a6d5;
box-shadow: 0 0 0 3px rgba(35, 166, 213, 0.1);
}
.header-container {
.hero-card {
display: flex;
justify-content: space-between;
gap: 20px;
align-items: center;
}
.header-links {
.tab-nav {
display: flex;
gap: 1rem;
gap: 12px;
flex-wrap: wrap;
}
.admin-card {
background: rgba(255, 255, 255, 0.6);
padding: 2rem;
border-radius: 20px;
border: 1px solid rgba(255, 255, 255, 0.5);
margin-bottom: 2.5rem;
box-shadow: 0 10px 30px rgba(0,0,0,0.05);
.tab-link {
background: var(--surface);
color: var(--text);
border: 1px solid var(--line);
}
.admin-card h3 {
margin-top: 0;
margin-bottom: 1.5rem;
.tab-link.is-active {
border-color: transparent;
}
.table-wrap {
overflow: auto;
border-radius: 22px;
border: 1px solid var(--line);
}
table {
width: 100%;
border-collapse: collapse;
min-width: 680px;
background: var(--surface-strong);
}
th,
td {
padding: 16px;
border-bottom: 1px solid var(--line);
text-align: right;
white-space: nowrap;
}
thead th {
background: var(--primary-soft);
color: var(--text);
font-size: 14px;
}
tbody tr:hover {
background: var(--primary-soft);
}
.clickable-row {
cursor: pointer;
}
.inline-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 18px;
}
.stats-grid {
display: grid;
gap: 18px;
grid-template-columns: repeat(4, minmax(0, 1fr));
}
.stat-card span {
display: block;
margin-bottom: 10px;
color: var(--muted);
}
.stat-card strong {
font-size: 28px;
}
.summary-list,
.mini-list {
display: grid;
gap: 12px;
margin: 0;
padding: 0;
list-style: none;
}
.printable-panel .invoice-sheet {
display: grid;
gap: 12px;
}
.invoice-items {
display: grid;
gap: 10px;
}
.invoice-note {
margin: 0;
padding: 14px 16px;
background: var(--surface-strong);
border: 1px dashed var(--line);
border-radius: 16px;
}
.alert {
padding: 14px 16px;
border-radius: 18px;
margin-bottom: 18px;
font-weight: 700;
}
.btn-delete {
background: #dc3545;
color: white;
border: none;
padding: 0.25rem 0.5rem;
border-radius: 4px;
cursor: pointer;
.alert-success {
background: var(--success-soft);
color: var(--success);
border: 1px solid rgba(2, 122, 72, 0.24);
}
.btn-add {
background: #212529;
color: white;
border: none;
padding: 0.5rem 1rem;
border-radius: 4px;
cursor: pointer;
margin-top: 1rem;
.alert-error {
background: var(--danger-soft);
color: var(--danger);
border: 1px solid rgba(180, 35, 24, 0.24);
}
.btn-save {
background: #0088cc;
color: white;
border: none;
padding: 0.8rem 1.5rem;
border-radius: 12px;
cursor: pointer;
font-weight: 600;
width: 100%;
transition: all 0.3s ease;
.help-drawer {
position: fixed;
inset: 0;
background: rgba(2, 6, 23, 0.36);
padding: 20px;
display: none;
align-items: flex-start;
justify-content: flex-start;
z-index: 40;
}
.webhook-url {
font-size: 0.85em;
color: #555;
margin-top: 0.5rem;
.help-drawer.is-open {
display: flex;
}
.history-table-container {
overflow-x: auto;
background: rgba(255, 255, 255, 0.4);
padding: 1rem;
border-radius: 12px;
border: 1px solid rgba(255, 255, 255, 0.3);
.help-card {
width: min(420px, 100%);
border-radius: 28px;
padding: 22px;
}
.history-table {
width: 100%;
.help-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
}
.history-table-time {
width: 15%;
white-space: nowrap;
font-size: 0.85em;
color: #555;
.icon-btn {
width: 42px;
height: 42px;
border-radius: 14px;
background: var(--surface-strong);
color: var(--text);
border: 1px solid var(--line);
}
.history-table-user {
width: 35%;
background: rgba(255, 255, 255, 0.3);
border-radius: 8px;
padding: 8px;
}
.history-table-ai {
width: 50%;
background: rgba(255, 255, 255, 0.5);
border-radius: 8px;
padding: 8px;
}
.no-messages {
.empty-state,
.empty-cell {
text-align: center;
color: #777;
padding: 24px;
}
@media (max-width: 1024px) {
.login-layout,
.grid.two-col,
.reports-grid,
.stats-grid {
grid-template-columns: 1fr;
}
}
@media (max-width: 720px) {
.page-shell {
width: min(100% - 20px, 100%);
padding-top: 16px;
}
.topbar,
.hero-card,
.inline-head,
.split-fields,
.demo-box,
.invoice-preview-box,
.invoice-row,
.invoice-item,
.summary-list li,
.mini-item,
.stat-inline {
grid-template-columns: 1fr;
flex-direction: column;
align-items: stretch;
}
.topbar,
.hero-card {
position: static;
}
.topbar-actions,
.hero-actions,
.inline-actions,
.tab-nav {
width: 100%;
}
.ghost-btn,
.primary-btn,
.primary-link,
.tab-link {
width: 100%;
}
th,
td {
padding: 14px 12px;
}
}

View File

@ -1,39 +1,154 @@
document.addEventListener('DOMContentLoaded', () => {
const chatForm = document.getElementById('chat-form');
const chatInput = document.getElementById('chat-input');
const chatMessages = document.getElementById('chat-messages');
const body = document.body;
const savedTheme = localStorage.getItem('store-theme');
if (savedTheme === 'dark' || savedTheme === 'light') {
body.dataset.theme = savedTheme;
}
const appendMessage = (text, sender) => {
const msgDiv = document.createElement('div');
msgDiv.classList.add('message', sender);
msgDiv.textContent = text;
chatMessages.appendChild(msgDiv);
chatMessages.scrollTop = chatMessages.scrollHeight;
const themeToggle = document.querySelector('[data-theme-toggle]');
const helpToggle = document.querySelector('[data-help-toggle]');
const helpClose = document.querySelector('[data-help-close]');
const helpDrawer = document.querySelector('[data-help-drawer]');
themeToggle?.addEventListener('click', () => {
const nextTheme = body.dataset.theme === 'dark' ? 'light' : 'dark';
body.dataset.theme = nextTheme;
localStorage.setItem('store-theme', nextTheme);
});
const toggleHelp = (open) => {
if (!helpDrawer) return;
helpDrawer.classList.toggle('is-open', open);
helpDrawer.setAttribute('aria-hidden', open ? 'false' : 'true');
};
chatForm.addEventListener('submit', async (e) => {
e.preventDefault();
const message = chatInput.value.trim();
if (!message) return;
appendMessage(message, 'visitor');
chatInput.value = '';
try {
const response = await fetch('api/chat.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message })
});
const data = await response.json();
// Artificial delay for realism
setTimeout(() => {
appendMessage(data.reply, 'bot');
}, 500);
} catch (error) {
console.error('Error:', error);
appendMessage("Sorry, something went wrong. Please try again.", 'bot');
helpToggle?.addEventListener('click', () => toggleHelp(true));
helpClose?.addEventListener('click', () => toggleHelp(false));
helpDrawer?.addEventListener('click', (event) => {
if (event.target === helpDrawer) {
toggleHelp(false);
}
});
document.querySelectorAll('[data-auto-hide]').forEach((alert) => {
setTimeout(() => {
alert.style.transition = 'opacity .3s ease';
alert.style.opacity = '0';
setTimeout(() => alert.remove(), 320);
}, 3200);
});
const formatSyp = (value) => `${new Intl.NumberFormat('en-US', { maximumFractionDigits: 0 }).format(value || 0)} ل.س`;
const rateInput = document.querySelector('[data-rate-input]');
const ratePreview = document.querySelector('[data-rate-preview]');
const equipmentUsd = document.querySelector('[data-equipment-usd]');
const equipmentPreview = document.querySelector('[data-equipment-preview]');
const invoiceEquipment = document.querySelector('[data-invoice-equipment]');
const invoiceQty = document.querySelector('[data-invoice-qty]');
const invoiceStock = document.querySelector('[data-invoice-stock]');
const invoiceTotal = document.querySelector('[data-invoice-total]');
const currentRate = () => {
const parsed = parseFloat(rateInput?.value || body.dataset.currentRate || '0');
return Number.isFinite(parsed) ? parsed : 0;
};
const updateRatePreview = () => {
if (ratePreview) ratePreview.textContent = formatSyp(currentRate());
};
const updateEquipmentPreview = () => {
if (!equipmentPreview) return;
const usd = parseFloat(equipmentUsd?.value || '0');
equipmentPreview.textContent = usd > 0 && currentRate() > 0 ? formatSyp(usd * currentRate()) : '—';
};
const updateInvoicePreview = () => {
if (!invoiceTotal) return;
const option = invoiceEquipment?.selectedOptions?.[0];
const price = parseFloat(option?.dataset.price || '0');
const stock = parseInt(option?.dataset.stock || '0', 10);
const qty = parseInt(invoiceQty?.value || '0', 10);
if (invoiceStock) {
invoiceStock.textContent = option && option.value ? `${stock} قطعة` : '—';
}
if (!option || !option.value || price <= 0 || qty <= 0 || currentRate() <= 0) {
invoiceTotal.textContent = '—';
return;
}
invoiceTotal.textContent = formatSyp(price * qty * currentRate());
};
rateInput?.addEventListener('input', () => {
body.dataset.currentRate = rateInput.value;
updateRatePreview();
updateEquipmentPreview();
updateInvoicePreview();
});
equipmentUsd?.addEventListener('input', updateEquipmentPreview);
invoiceEquipment?.addEventListener('change', updateInvoicePreview);
invoiceQty?.addEventListener('input', updateInvoicePreview);
updateRatePreview();
updateEquipmentPreview();
updateInvoicePreview();
const printableInvoice = document.querySelector('[data-printable-invoice]');
const printButton = document.querySelector('[data-print-invoice]');
const exportSelectedButton = document.querySelector('[data-export-selected-pdf]');
const exportInvoicesButton = document.querySelector('[data-export-invoices]');
const invoicesTable = document.querySelector('[data-invoices-table]');
const openPrintWindow = (title, html) => {
const win = window.open('', '_blank', 'width=900,height=700');
if (!win) return;
win.document.write(`<!doctype html><html lang="ar" dir="rtl"><head><meta charset="utf-8"><title>${title}</title><style>
body{font-family:Segoe UI,Tahoma,Arial,sans-serif;padding:24px;background:#fff;color:#111827}
.sheet{max-width:720px;margin:0 auto;border:1px solid #d7dfeb;border-radius:24px;padding:24px}
.sheet *{direction:rtl;text-align:right}
.invoice-row,.invoice-item,.invoice-note{margin-bottom:10px;padding:12px 14px;border:1px solid #d7dfeb;border-radius:14px;background:#f8fafc}
</style></head><body><div class="sheet">${html}</div><script>window.onload=function(){window.print();setTimeout(function(){window.close();},150);};</script></body></html>`);
win.document.close();
};
printButton?.addEventListener('click', () => {
if (!printableInvoice) return;
openPrintWindow('فاتورة', printableInvoice.innerHTML);
});
const hasPdf = () => window.jspdf && window.jspdf.jsPDF;
exportSelectedButton?.addEventListener('click', () => {
if (!hasPdf() || !printableInvoice) return;
const { jsPDF } = window.jspdf;
const doc = new jsPDF({ unit: 'pt', format: 'a4' });
doc.setFontSize(18);
doc.text('Selected Invoice', 40, 50);
const lines = printableInvoice.innerText.split('\n').map((line) => line.trim()).filter(Boolean);
let y = 90;
lines.forEach((line) => {
const chunks = doc.splitTextToSize(line, 500);
doc.text(chunks, 40, y);
y += chunks.length * 18 + 8;
});
doc.save('selected-invoice.pdf');
});
exportInvoicesButton?.addEventListener('click', () => {
if (!hasPdf() || !invoicesTable) return;
const { jsPDF } = window.jspdf;
const doc = new jsPDF({ orientation: 'landscape', unit: 'pt', format: 'a4' });
doc.setFontSize(16);
doc.text('Invoices Register', 40, 40);
const rows = Array.from(invoicesTable.querySelectorAll('tbody tr')).map((tr) =>
Array.from(tr.querySelectorAll('td')).map((td) => td.innerText.replace(/\s+/g, ' ').trim())
).filter((row) => row.length === 5);
doc.autoTable({
head: [['Invoice', 'Customer', 'Total USD', 'Total SYP', 'Created At']],
body: rows,
startY: 60,
styles: { fontSize: 10 },
headStyles: { fillColor: [15, 118, 110] }
});
doc.save('invoices-register.pdf');
});
});

5
cookies.txt Normal file
View File

@ -0,0 +1,5 @@
# Netscape HTTP Cookie File
# https://curl.se/docs/http-cookies.html
# This file was generated by libcurl! Edit at your own risk.
127.0.0.1 FALSE / FALSE 0 PHPSESSID 7k5qe8i2prge2kh3hqfs9pkf5a

22
healthz.php Normal file
View File

@ -0,0 +1,22 @@
<?php
declare(strict_types=1);
header('Content-Type: application/json; charset=utf-8');
require_once __DIR__ . '/db/config.php';
try {
db()->query('SELECT 1');
http_response_code(200);
echo json_encode([
'status' => 'ok',
'app' => 'lamp-store-admin',
'time' => gmdate('c'),
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
} catch (Throwable $exception) {
http_response_code(500);
echo json_encode([
'status' => 'error',
'message' => 'database_unreachable',
'time' => gmdate('c'),
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
}

1020
index.php

File diff suppressed because it is too large Load Diff