Autosave: 20260405-115130
This commit is contained in:
parent
566328e604
commit
ae86c5cba1
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
5
cookies.txt
Normal 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
22
healthz.php
Normal 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);
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user