Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ae86c5cba1 |
@ -1,403 +1,490 @@
|
|||||||
body {
|
:root {
|
||||||
background: linear-gradient(-45deg, #ee7752, #e73c7e, #23a6d5, #23d5ab);
|
--bg: #f4f7fb;
|
||||||
background-size: 400% 400%;
|
--surface: rgba(255, 255, 255, 0.9);
|
||||||
animation: gradient 15s ease infinite;
|
--surface-strong: #ffffff;
|
||||||
color: #212529;
|
--text: #101828;
|
||||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
--muted: #667085;
|
||||||
font-size: 14px;
|
--line: #d7dfeb;
|
||||||
margin: 0;
|
--primary: #0f766e;
|
||||||
min-height: 100vh;
|
--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;
|
display: flex;
|
||||||
align-items: center;
|
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;
|
justify-content: center;
|
||||||
min-height: 100vh;
|
min-height: 46px;
|
||||||
width: 100%;
|
padding: 0 18px;
|
||||||
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;
|
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
line-height: 1.5;
|
transition: .2s ease;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes fadeIn {
|
.ghost-btn {
|
||||||
from { opacity: 0; transform: translateY(20px) scale(0.95); }
|
background: var(--surface-strong);
|
||||||
to { opacity: 1; transform: translateY(0) scale(1); }
|
color: var(--text);
|
||||||
|
border: 1px solid var(--line);
|
||||||
}
|
}
|
||||||
|
|
||||||
.message.visitor {
|
.primary-btn,
|
||||||
align-self: flex-end;
|
.primary-link,
|
||||||
background: linear-gradient(135deg, #212529 0%, #343a40 100%);
|
.tab-link.is-active,
|
||||||
color: #fff;
|
.secondary-tone {
|
||||||
border-bottom-right-radius: 4px;
|
background: linear-gradient(135deg, var(--primary), var(--primary-strong));
|
||||||
|
color: #ffffff;
|
||||||
|
box-shadow: 0 14px 28px rgba(15, 118, 110, 0.22);
|
||||||
}
|
}
|
||||||
|
|
||||||
.message.bot {
|
.primary-btn:hover,
|
||||||
align-self: flex-start;
|
.primary-link:hover,
|
||||||
background: #ffffff;
|
.ghost-btn:hover,
|
||||||
color: #212529;
|
.tab-link:hover {
|
||||||
border-bottom-left-radius: 4px;
|
transform: translateY(-1px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.chat-input-area {
|
.secondary-tone {
|
||||||
padding: 1.25rem;
|
border: none;
|
||||||
background: rgba(255, 255, 255, 0.5);
|
|
||||||
border-top: 1px solid rgba(0, 0, 0, 0.05);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.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;
|
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 {
|
.gradient-card {
|
||||||
flex: 1;
|
background:
|
||||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
linear-gradient(145deg, rgba(15, 118, 110, 0.16), rgba(14, 165, 233, 0.08)),
|
||||||
border-radius: 12px;
|
var(--surface);
|
||||||
padding: 0.75rem 1rem;
|
}
|
||||||
|
|
||||||
|
.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;
|
outline: none;
|
||||||
background: rgba(255, 255, 255, 0.9);
|
transition: border-color .2s ease, box-shadow .2s ease;
|
||||||
transition: all 0.3s ease;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.chat-input-area input:focus {
|
input:focus,
|
||||||
border-color: #23a6d5;
|
select:focus,
|
||||||
box-shadow: 0 0 0 3px rgba(35, 166, 213, 0.2);
|
textarea:focus {
|
||||||
|
border-color: var(--primary);
|
||||||
|
box-shadow: 0 0 0 4px var(--primary-soft);
|
||||||
}
|
}
|
||||||
|
|
||||||
.chat-input-area button {
|
.split-fields {
|
||||||
background: #212529;
|
display: grid;
|
||||||
color: #fff;
|
gap: 12px;
|
||||||
border: none;
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
padding: 0.75rem 1.5rem;
|
|
||||||
border-radius: 12px;
|
|
||||||
cursor: pointer;
|
|
||||||
font-weight: 600;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.chat-input-area button:hover {
|
.app-layout {
|
||||||
background: #000;
|
display: grid;
|
||||||
transform: translateY(-2px);
|
gap: 22px;
|
||||||
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Background Animations */
|
.hero-card {
|
||||||
.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 {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
gap: 20px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-links {
|
.tab-nav {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 1rem;
|
gap: 12px;
|
||||||
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.admin-card {
|
.tab-link {
|
||||||
background: rgba(255, 255, 255, 0.6);
|
background: var(--surface);
|
||||||
padding: 2rem;
|
color: var(--text);
|
||||||
border-radius: 20px;
|
border: 1px solid var(--line);
|
||||||
border: 1px solid rgba(255, 255, 255, 0.5);
|
|
||||||
margin-bottom: 2.5rem;
|
|
||||||
box-shadow: 0 10px 30px rgba(0,0,0,0.05);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.admin-card h3 {
|
.tab-link.is-active {
|
||||||
margin-top: 0;
|
border-color: transparent;
|
||||||
margin-bottom: 1.5rem;
|
}
|
||||||
|
|
||||||
|
.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;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-delete {
|
.alert-success {
|
||||||
background: #dc3545;
|
background: var(--success-soft);
|
||||||
color: white;
|
color: var(--success);
|
||||||
border: none;
|
border: 1px solid rgba(2, 122, 72, 0.24);
|
||||||
padding: 0.25rem 0.5rem;
|
|
||||||
border-radius: 4px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-add {
|
.alert-error {
|
||||||
background: #212529;
|
background: var(--danger-soft);
|
||||||
color: white;
|
color: var(--danger);
|
||||||
border: none;
|
border: 1px solid rgba(180, 35, 24, 0.24);
|
||||||
padding: 0.5rem 1rem;
|
|
||||||
border-radius: 4px;
|
|
||||||
cursor: pointer;
|
|
||||||
margin-top: 1rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-save {
|
.help-drawer {
|
||||||
background: #0088cc;
|
position: fixed;
|
||||||
color: white;
|
inset: 0;
|
||||||
border: none;
|
background: rgba(2, 6, 23, 0.36);
|
||||||
padding: 0.8rem 1.5rem;
|
padding: 20px;
|
||||||
border-radius: 12px;
|
display: none;
|
||||||
cursor: pointer;
|
align-items: flex-start;
|
||||||
font-weight: 600;
|
justify-content: flex-start;
|
||||||
width: 100%;
|
z-index: 40;
|
||||||
transition: all 0.3s ease;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.webhook-url {
|
.help-drawer.is-open {
|
||||||
font-size: 0.85em;
|
display: flex;
|
||||||
color: #555;
|
|
||||||
margin-top: 0.5rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.history-table-container {
|
.help-card {
|
||||||
overflow-x: auto;
|
width: min(420px, 100%);
|
||||||
background: rgba(255, 255, 255, 0.4);
|
border-radius: 28px;
|
||||||
padding: 1rem;
|
padding: 22px;
|
||||||
border-radius: 12px;
|
|
||||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.history-table {
|
.help-header {
|
||||||
width: 100%;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.history-table-time {
|
.icon-btn {
|
||||||
width: 15%;
|
width: 42px;
|
||||||
white-space: nowrap;
|
height: 42px;
|
||||||
font-size: 0.85em;
|
border-radius: 14px;
|
||||||
color: #555;
|
background: var(--surface-strong);
|
||||||
|
color: var(--text);
|
||||||
|
border: 1px solid var(--line);
|
||||||
}
|
}
|
||||||
|
|
||||||
.history-table-user {
|
.empty-state,
|
||||||
width: 35%;
|
.empty-cell {
|
||||||
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 {
|
|
||||||
text-align: center;
|
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', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
const chatForm = document.getElementById('chat-form');
|
const body = document.body;
|
||||||
const chatInput = document.getElementById('chat-input');
|
const savedTheme = localStorage.getItem('store-theme');
|
||||||
const chatMessages = document.getElementById('chat-messages');
|
if (savedTheme === 'dark' || savedTheme === 'light') {
|
||||||
|
body.dataset.theme = savedTheme;
|
||||||
|
}
|
||||||
|
|
||||||
const appendMessage = (text, sender) => {
|
const themeToggle = document.querySelector('[data-theme-toggle]');
|
||||||
const msgDiv = document.createElement('div');
|
const helpToggle = document.querySelector('[data-help-toggle]');
|
||||||
msgDiv.classList.add('message', sender);
|
const helpClose = document.querySelector('[data-help-close]');
|
||||||
msgDiv.textContent = text;
|
const helpDrawer = document.querySelector('[data-help-drawer]');
|
||||||
chatMessages.appendChild(msgDiv);
|
|
||||||
chatMessages.scrollTop = chatMessages.scrollHeight;
|
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) => {
|
helpToggle?.addEventListener('click', () => toggleHelp(true));
|
||||||
e.preventDefault();
|
helpClose?.addEventListener('click', () => toggleHelp(false));
|
||||||
const message = chatInput.value.trim();
|
helpDrawer?.addEventListener('click', (event) => {
|
||||||
if (!message) return;
|
if (event.target === helpDrawer) {
|
||||||
|
toggleHelp(false);
|
||||||
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');
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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