Merge pull request 'fix(front-end): css file duplicte' (#32) from fix/css-files into feat/shop
Reviewed-on: #32
This commit was merged in pull request #32.
This commit is contained in:
@@ -1,11 +1,18 @@
|
||||
<section class="section-card">
|
||||
<header class="section-header">
|
||||
<div class="header-copy">
|
||||
<h2>Richieste di contatto</h2>
|
||||
<p>Richieste preventivo personalizzato ricevute dal sito.</p>
|
||||
<span class="total-pill">{{ requests.length }} richieste</span>
|
||||
<section class="section-card ui-section-card">
|
||||
<header class="section-header ui-section-header">
|
||||
<div class="header-copy ui-section-header__copy">
|
||||
<h2 class="ui-section-header__title">Richieste di contatto</h2>
|
||||
<p class="ui-section-header__description">
|
||||
Richieste preventivo personalizzato ricevute dal sito.
|
||||
</p>
|
||||
<span class="total-pill ui-pill">{{ requests.length }} richieste</span>
|
||||
</div>
|
||||
<button type="button" (click)="loadRequests()" [disabled]="loading">
|
||||
<button
|
||||
type="button"
|
||||
class="ui-button"
|
||||
(click)="loadRequests()"
|
||||
[disabled]="loading"
|
||||
>
|
||||
Aggiorna
|
||||
</button>
|
||||
</header>
|
||||
@@ -13,11 +20,11 @@
|
||||
<p class="error" *ngIf="errorMessage">{{ errorMessage }}</p>
|
||||
<p class="success" *ngIf="successMessage">{{ successMessage }}</p>
|
||||
|
||||
<div class="workspace" *ngIf="!loading; else loadingTpl">
|
||||
<div class="workspace ui-split-workspace" *ngIf="!loading; else loadingTpl">
|
||||
<section class="list-panel">
|
||||
<h3>Lista richieste</h3>
|
||||
<div class="table-wrap">
|
||||
<table class="requests-table">
|
||||
<div class="table-wrap ui-table-wrap">
|
||||
<table class="requests-table ui-data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Data</th>
|
||||
@@ -50,14 +57,18 @@
|
||||
</td>
|
||||
<td class="email-cell">{{ request.email }}</td>
|
||||
<td>
|
||||
<span class="chip chip-neutral">{{ request.requestType }}</span>
|
||||
<span class="ui-status-chip ui-status-chip--neutral">{{
|
||||
request.requestType
|
||||
}}</span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="chip chip-light">{{ request.customerType }}</span>
|
||||
<span class="ui-status-chip ui-status-chip--neutral">{{
|
||||
request.customerType
|
||||
}}</span>
|
||||
</td>
|
||||
<td>
|
||||
<span
|
||||
class="chip"
|
||||
class="ui-status-chip"
|
||||
[ngClass]="getStatusChipClass(request.status)"
|
||||
>{{ request.status }}</span
|
||||
>
|
||||
@@ -71,13 +82,14 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="detail-panel" *ngIf="selectedRequest">
|
||||
<section class="detail-panel ui-detail-panel" *ngIf="selectedRequest">
|
||||
<header class="detail-header">
|
||||
<div>
|
||||
<h3>Dettaglio richiesta</h3>
|
||||
<p class="request-id">
|
||||
<span>ID</span>
|
||||
<code
|
||||
class="ui-code-pill"
|
||||
[title]="selectedRequest.id"
|
||||
[appCopyOnClick]="selectedRequest.id"
|
||||
>{{ selectedRequest.id }}</code
|
||||
@@ -86,14 +98,14 @@
|
||||
</div>
|
||||
<div class="detail-chips">
|
||||
<span
|
||||
class="chip"
|
||||
class="ui-status-chip"
|
||||
[ngClass]="getStatusChipClass(selectedRequest.status)"
|
||||
>{{ selectedRequest.status }}</span
|
||||
>
|
||||
<span class="chip chip-neutral">{{
|
||||
<span class="ui-status-chip ui-status-chip--neutral">{{
|
||||
selectedRequest.requestType
|
||||
}}</span>
|
||||
<span class="chip chip-light">{{
|
||||
<span class="ui-status-chip ui-status-chip--neutral">{{
|
||||
selectedRequest.customerType
|
||||
}}</span>
|
||||
</div>
|
||||
@@ -103,32 +115,32 @@
|
||||
Caricamento dettaglio...
|
||||
</p>
|
||||
|
||||
<dl class="meta-grid">
|
||||
<div class="meta-item">
|
||||
<dl class="meta-grid ui-meta-grid">
|
||||
<div class="meta-item ui-meta-item">
|
||||
<dt>Creata</dt>
|
||||
<dd>{{ selectedRequest.createdAt | date: "medium" }}</dd>
|
||||
</div>
|
||||
<div class="meta-item">
|
||||
<div class="meta-item ui-meta-item">
|
||||
<dt>Aggiornata</dt>
|
||||
<dd>{{ selectedRequest.updatedAt | date: "medium" }}</dd>
|
||||
</div>
|
||||
<div class="meta-item">
|
||||
<div class="meta-item ui-meta-item">
|
||||
<dt>Email</dt>
|
||||
<dd>{{ selectedRequest.email }}</dd>
|
||||
</div>
|
||||
<div class="meta-item">
|
||||
<div class="meta-item ui-meta-item">
|
||||
<dt>Telefono</dt>
|
||||
<dd>{{ selectedRequest.phone || "-" }}</dd>
|
||||
</div>
|
||||
<div class="meta-item">
|
||||
<div class="meta-item ui-meta-item">
|
||||
<dt>Nome</dt>
|
||||
<dd>{{ selectedRequest.name || "-" }}</dd>
|
||||
</div>
|
||||
<div class="meta-item">
|
||||
<div class="meta-item ui-meta-item">
|
||||
<dt>Azienda</dt>
|
||||
<dd>{{ selectedRequest.companyName || "-" }}</dd>
|
||||
</div>
|
||||
<div class="meta-item">
|
||||
<div class="meta-item ui-meta-item">
|
||||
<dt>Referente</dt>
|
||||
<dd>{{ selectedRequest.contactPerson || "-" }}</dd>
|
||||
</div>
|
||||
@@ -138,6 +150,7 @@
|
||||
<div class="status-editor-field">
|
||||
<label for="contact-request-status">Stato richiesta</label>
|
||||
<select
|
||||
class="ui-form-control"
|
||||
id="contact-request-status"
|
||||
[ngModel]="selectedStatus"
|
||||
(ngModelChange)="selectedStatus = $event"
|
||||
@@ -148,6 +161,7 @@
|
||||
</select>
|
||||
</div>
|
||||
<button
|
||||
class="ui-button"
|
||||
type="button"
|
||||
(click)="updateRequestStatus()"
|
||||
[disabled]="
|
||||
@@ -190,7 +204,7 @@
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
class="ghost"
|
||||
class="ui-button ui-button--ghost"
|
||||
(click)="downloadAttachment(attachment)"
|
||||
>
|
||||
Scarica file
|
||||
@@ -200,7 +214,10 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="detail-panel empty" *ngIf="!selectedRequest">
|
||||
<section
|
||||
class="detail-panel ui-detail-panel ui-detail-panel--empty"
|
||||
*ngIf="!selectedRequest"
|
||||
>
|
||||
<h3>Nessuna richiesta selezionata</h3>
|
||||
<p>Seleziona una riga dalla lista per vedere il dettaglio.</p>
|
||||
</section>
|
||||
|
||||
@@ -4,23 +4,6 @@
|
||||
gap: var(--space-5);
|
||||
}
|
||||
|
||||
.section-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
gap: var(--space-4);
|
||||
}
|
||||
|
||||
.section-header h2 {
|
||||
margin: 0;
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
|
||||
.section-header p {
|
||||
margin: var(--space-2) 0 0;
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
.header-copy {
|
||||
display: grid;
|
||||
gap: var(--space-1);
|
||||
@@ -29,21 +12,6 @@
|
||||
.total-pill {
|
||||
width: fit-content;
|
||||
margin-top: var(--space-1);
|
||||
border-radius: 999px;
|
||||
border: 1px solid var(--color-border);
|
||||
background: var(--color-neutral-100);
|
||||
color: var(--color-text-muted);
|
||||
font-size: 0.78rem;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
padding: 6px 10px;
|
||||
}
|
||||
|
||||
.workspace {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: var(--space-4);
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.list-panel,
|
||||
@@ -51,72 +19,11 @@
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
button {
|
||||
border: 0;
|
||||
border-radius: var(--radius-md);
|
||||
background: var(--color-brand);
|
||||
color: var(--color-neutral-900);
|
||||
padding: var(--space-2) var(--space-4);
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition:
|
||||
background-color 0.2s ease,
|
||||
opacity 0.2s ease;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
button:hover:not(:disabled) {
|
||||
background: var(--color-brand-hover);
|
||||
}
|
||||
|
||||
button.ghost {
|
||||
background: var(--color-bg-card);
|
||||
border: 1px solid var(--color-border);
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.list-panel h3 {
|
||||
margin: 0 0 var(--space-2);
|
||||
font-size: 1.02rem;
|
||||
}
|
||||
|
||||
.table-wrap {
|
||||
overflow: auto;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-md);
|
||||
background: var(--color-bg-card);
|
||||
max-height: 72vh;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
min-width: 760px;
|
||||
}
|
||||
|
||||
thead {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
background: var(--color-neutral-100);
|
||||
}
|
||||
|
||||
th,
|
||||
td {
|
||||
text-align: left;
|
||||
padding: var(--space-3);
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
font-size: 0.92rem;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
th {
|
||||
font-size: 0.78rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.03em;
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
.name-cell .primary {
|
||||
margin: 0;
|
||||
font-weight: 600;
|
||||
@@ -135,11 +42,6 @@ th {
|
||||
|
||||
tbody tr {
|
||||
cursor: pointer;
|
||||
transition: background-color 0.15s ease;
|
||||
}
|
||||
|
||||
tbody tr:hover {
|
||||
background: #fff9d9;
|
||||
}
|
||||
|
||||
tbody tr.selected {
|
||||
@@ -154,27 +56,6 @@ tbody tr.selected {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.detail-panel {
|
||||
display: grid;
|
||||
gap: var(--space-3);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-md);
|
||||
background: var(--color-bg-card);
|
||||
padding: var(--space-4);
|
||||
min-height: 500px;
|
||||
}
|
||||
|
||||
.detail-panel.empty {
|
||||
display: grid;
|
||||
align-content: center;
|
||||
justify-items: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.detail-panel.empty h3 {
|
||||
margin: 0 0 var(--space-2);
|
||||
}
|
||||
|
||||
.detail-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
@@ -210,11 +91,6 @@ tbody tr.selected {
|
||||
text-overflow: clip;
|
||||
white-space: normal;
|
||||
overflow-wrap: anywhere;
|
||||
color: var(--color-text);
|
||||
background: var(--color-neutral-100);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: 7px;
|
||||
padding: 3px 8px;
|
||||
}
|
||||
|
||||
.loading-detail {
|
||||
@@ -223,28 +99,12 @@ tbody tr.selected {
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.meta-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
|
||||
gap: var(--space-2);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.meta-item {
|
||||
margin: 0;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-md);
|
||||
padding: var(--space-3);
|
||||
background: var(--color-neutral-100);
|
||||
display: grid;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.meta-item dt {
|
||||
margin: 0;
|
||||
font-size: 0.78rem;
|
||||
font-weight: 700;
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
.meta-item dd {
|
||||
@@ -323,7 +183,7 @@ tbody tr.selected {
|
||||
.status-editor {
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-md);
|
||||
background: var(--color-neutral-100);
|
||||
background: var(--color-surface-muted);
|
||||
padding: var(--space-3);
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
@@ -343,79 +203,7 @@ tbody tr.selected {
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
.status-editor-field select {
|
||||
width: 100%;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-md);
|
||||
padding: var(--space-2) var(--space-3);
|
||||
background: var(--color-bg-card);
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
.chip {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
border-radius: 999px;
|
||||
border: 1px solid transparent;
|
||||
font-size: 0.74rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.02em;
|
||||
line-height: 1;
|
||||
padding: 5px 9px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.chip-neutral {
|
||||
background: #e9f4ff;
|
||||
border-color: #c8def4;
|
||||
color: #1e4d78;
|
||||
}
|
||||
|
||||
.chip-light {
|
||||
background: #f4f5f8;
|
||||
border-color: #dde1e8;
|
||||
color: #4a5567;
|
||||
}
|
||||
|
||||
.chip-warning {
|
||||
background: #fff4cd;
|
||||
border-color: #f7dd85;
|
||||
color: #684b00;
|
||||
}
|
||||
|
||||
.chip-success {
|
||||
background: #dff6ea;
|
||||
border-color: #b6e2cb;
|
||||
color: #14543a;
|
||||
}
|
||||
|
||||
.chip-danger {
|
||||
background: #fde4e2;
|
||||
border-color: #f3c0ba;
|
||||
color: #812924;
|
||||
}
|
||||
|
||||
button:disabled {
|
||||
opacity: 0.68;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
@media (min-width: 1460px) {
|
||||
.workspace {
|
||||
grid-template-columns: minmax(0, 1.2fr) minmax(340px, 0.9fr);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 760px) {
|
||||
.section-card {
|
||||
gap: var(--space-4);
|
||||
}
|
||||
|
||||
.section-header {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.detail-header {
|
||||
flex-direction: column;
|
||||
}
|
||||
@@ -444,8 +232,8 @@ button:disabled {
|
||||
}
|
||||
|
||||
@media (max-width: 520px) {
|
||||
th,
|
||||
td {
|
||||
.ui-data-table th,
|
||||
.ui-data-table td {
|
||||
padding: var(--space-2);
|
||||
font-size: 0.86rem;
|
||||
}
|
||||
|
||||
@@ -118,15 +118,15 @@ export class AdminContactRequestsComponent implements OnInit {
|
||||
getStatusChipClass(status?: string): string {
|
||||
const normalized = (status || '').trim().toUpperCase();
|
||||
if (['PENDING', 'NEW', 'OPEN', 'IN_PROGRESS'].includes(normalized)) {
|
||||
return 'chip-warning';
|
||||
return 'ui-status-chip--warning';
|
||||
}
|
||||
if (['DONE', 'COMPLETED', 'RESOLVED', 'CLOSED'].includes(normalized)) {
|
||||
return 'chip-success';
|
||||
return 'ui-status-chip--success';
|
||||
}
|
||||
if (['REJECTED', 'FAILED', 'ERROR', 'SPAM'].includes(normalized)) {
|
||||
return 'chip-danger';
|
||||
return 'ui-status-chip--danger';
|
||||
}
|
||||
return 'chip-light';
|
||||
return 'ui-status-chip--neutral';
|
||||
}
|
||||
|
||||
updateRequestStatus(): void {
|
||||
|
||||
@@ -1,11 +1,18 @@
|
||||
<section class="admin-dashboard">
|
||||
<header class="dashboard-header">
|
||||
<div>
|
||||
<h1>Ordini</h1>
|
||||
<p>Seleziona un ordine a sinistra e gestiscilo nel dettaglio a destra.</p>
|
||||
<section class="admin-dashboard ui-section-card">
|
||||
<header class="dashboard-header ui-section-header">
|
||||
<div class="ui-section-header__copy">
|
||||
<h1 class="ui-section-header__title">Ordini</h1>
|
||||
<p class="ui-section-header__description">
|
||||
Seleziona un ordine a sinistra e gestiscilo nel dettaglio a destra.
|
||||
</p>
|
||||
</div>
|
||||
<div class="header-actions">
|
||||
<button type="button" (click)="loadOrders()" [disabled]="loading">
|
||||
<button
|
||||
type="button"
|
||||
class="ui-button"
|
||||
(click)="loadOrders()"
|
||||
[disabled]="loading"
|
||||
>
|
||||
Aggiorna
|
||||
</button>
|
||||
</div>
|
||||
@@ -13,13 +20,14 @@
|
||||
|
||||
<p class="error" *ngIf="errorMessage">{{ errorMessage }}</p>
|
||||
|
||||
<div class="workspace" *ngIf="!loading; else loadingTpl">
|
||||
<div class="workspace ui-split-workspace" *ngIf="!loading; else loadingTpl">
|
||||
<section class="list-panel">
|
||||
<h2>Lista ordini</h2>
|
||||
<div class="list-toolbar">
|
||||
<label class="toolbar-field" for="order-search">
|
||||
<span>Cerca UUID</span>
|
||||
<input
|
||||
class="ui-form-control"
|
||||
id="order-search"
|
||||
type="search"
|
||||
[ngModel]="orderSearchTerm"
|
||||
@@ -30,6 +38,7 @@
|
||||
<label class="toolbar-field" for="payment-status-filter">
|
||||
<span>Stato pagamento</span>
|
||||
<select
|
||||
class="ui-form-control"
|
||||
id="payment-status-filter"
|
||||
[ngModel]="paymentStatusFilter"
|
||||
(ngModelChange)="onPaymentStatusFilterChange($event)"
|
||||
@@ -45,6 +54,7 @@
|
||||
<label class="toolbar-field" for="order-status-filter">
|
||||
<span>Stato ordine</span>
|
||||
<select
|
||||
class="ui-form-control"
|
||||
id="order-status-filter"
|
||||
[ngModel]="orderStatusFilter"
|
||||
(ngModelChange)="onOrderStatusFilterChange($event)"
|
||||
@@ -58,8 +68,8 @@
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
<div class="table-wrap">
|
||||
<table>
|
||||
<div class="table-wrap ui-table-wrap">
|
||||
<table class="ui-data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Ordine</th>
|
||||
@@ -93,12 +103,13 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="detail-panel" *ngIf="selectedOrder">
|
||||
<section class="detail-panel ui-detail-panel" *ngIf="selectedOrder">
|
||||
<div class="detail-header">
|
||||
<h2>Dettaglio ordine {{ selectedOrder.orderNumber }}</h2>
|
||||
<p class="order-uuid">
|
||||
UUID:
|
||||
<code
|
||||
class="ui-code-pill"
|
||||
[title]="selectedOrder.id"
|
||||
[appCopyOnClick]="selectedOrder.id"
|
||||
>{{ selectedOrder.id }}</code
|
||||
@@ -107,18 +118,18 @@
|
||||
<p *ngIf="detailLoading">Caricamento dettaglio...</p>
|
||||
</div>
|
||||
|
||||
<div class="meta-grid">
|
||||
<div>
|
||||
<div class="meta-grid ui-meta-grid">
|
||||
<div class="ui-meta-item">
|
||||
<strong>Cliente</strong><span>{{ selectedOrder.customerEmail }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<div class="ui-meta-item">
|
||||
<strong>Stato pagamento</strong
|
||||
><span>{{ selectedOrder.paymentStatus || "PENDING" }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<div class="ui-meta-item">
|
||||
<strong>Stato ordine</strong><span>{{ selectedOrder.status }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<div class="ui-meta-item">
|
||||
<strong>Totale</strong
|
||||
><span>{{
|
||||
selectedOrder.totalChf | currency: "CHF" : "symbol" : "1.2-2"
|
||||
@@ -130,6 +141,7 @@
|
||||
<div class="status-editor">
|
||||
<label for="order-status">Stato ordine</label>
|
||||
<select
|
||||
class="ui-form-control"
|
||||
id="order-status"
|
||||
[value]="selectedStatus"
|
||||
(change)="onStatusChange($event)"
|
||||
@@ -139,6 +151,7 @@
|
||||
</option>
|
||||
</select>
|
||||
<button
|
||||
class="ui-button"
|
||||
type="button"
|
||||
(click)="updateStatus()"
|
||||
[disabled]="updatingStatus"
|
||||
@@ -150,6 +163,7 @@
|
||||
<div class="status-editor">
|
||||
<label for="payment-method">Metodo pagamento</label>
|
||||
<select
|
||||
class="ui-form-control"
|
||||
id="payment-method"
|
||||
[value]="selectedPaymentMethod"
|
||||
(change)="onPaymentMethodChange($event)"
|
||||
@@ -162,6 +176,7 @@
|
||||
</option>
|
||||
</select>
|
||||
<button
|
||||
class="ui-button"
|
||||
type="button"
|
||||
(click)="updatePaymentMethod()"
|
||||
[disabled]="confirmingPayment"
|
||||
@@ -174,13 +189,25 @@
|
||||
</div>
|
||||
|
||||
<div class="doc-actions">
|
||||
<button type="button" class="ghost" (click)="downloadConfirmation()">
|
||||
<button
|
||||
type="button"
|
||||
class="ui-button ui-button--ghost"
|
||||
(click)="downloadConfirmation()"
|
||||
>
|
||||
Scarica conferma + QR bill
|
||||
</button>
|
||||
<button type="button" class="ghost" (click)="downloadInvoice()">
|
||||
<button
|
||||
type="button"
|
||||
class="ui-button ui-button--ghost"
|
||||
(click)="downloadInvoice()"
|
||||
>
|
||||
Scarica fattura
|
||||
</button>
|
||||
<button type="button" class="ghost" (click)="openPrintDetails()">
|
||||
<button
|
||||
type="button"
|
||||
class="ui-button ui-button--ghost"
|
||||
(click)="openPrintDetails()"
|
||||
>
|
||||
Dettagli stampa
|
||||
</button>
|
||||
</div>
|
||||
@@ -215,7 +242,7 @@
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
class="ghost"
|
||||
class="ui-button ui-button--ghost"
|
||||
(click)="downloadItemFile(item.id, item.originalFilename)"
|
||||
>
|
||||
Scarica file
|
||||
@@ -224,7 +251,10 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="detail-panel empty" *ngIf="!selectedOrder">
|
||||
<section
|
||||
class="detail-panel ui-detail-panel ui-detail-panel--empty"
|
||||
*ngIf="!selectedOrder"
|
||||
>
|
||||
<h2>Nessun ordine selezionato</h2>
|
||||
<p>Seleziona un ordine dalla lista per vedere i dettagli.</p>
|
||||
</section>
|
||||
@@ -245,39 +275,39 @@
|
||||
<h3>Dettagli stampa ordine {{ selectedOrder.orderNumber }}</h3>
|
||||
<button
|
||||
type="button"
|
||||
class="ghost close-btn"
|
||||
class="ui-button ui-button--ghost close-btn"
|
||||
(click)="closePrintDetails()"
|
||||
>
|
||||
Chiudi
|
||||
</button>
|
||||
</header>
|
||||
|
||||
<div class="modal-grid">
|
||||
<div>
|
||||
<div class="modal-grid ui-meta-grid">
|
||||
<div class="ui-meta-item">
|
||||
<strong>Qualità</strong
|
||||
><span>{{ getQualityLabel(selectedOrder.printLayerHeightMm) }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<div class="ui-meta-item">
|
||||
<strong>Materiale</strong
|
||||
><span>{{ selectedOrder.printMaterialCode || "-" }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<div class="ui-meta-item">
|
||||
<strong>Layer height</strong
|
||||
><span>{{ selectedOrder.printLayerHeightMm || "-" }} mm</span>
|
||||
</div>
|
||||
<div>
|
||||
<div class="ui-meta-item">
|
||||
<strong>Nozzle</strong
|
||||
><span>{{ selectedOrder.printNozzleDiameterMm || "-" }} mm</span>
|
||||
</div>
|
||||
<div>
|
||||
<div class="ui-meta-item">
|
||||
<strong>Infill pattern</strong
|
||||
><span>{{ selectedOrder.printInfillPattern || "-" }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<div class="ui-meta-item">
|
||||
<strong>Infill %</strong
|
||||
><span>{{ selectedOrder.printInfillPercent ?? "-" }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<div class="ui-meta-item">
|
||||
<strong>Supporti</strong
|
||||
><span>{{ selectedOrder.printSupportsEnabled ? "Sì" : "No" }}</span>
|
||||
</div>
|
||||
|
||||
@@ -4,67 +4,16 @@
|
||||
gap: var(--space-5);
|
||||
}
|
||||
|
||||
.dashboard-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
gap: var(--space-4);
|
||||
}
|
||||
|
||||
.dashboard-header h1 {
|
||||
margin: 0;
|
||||
font-size: 1.45rem;
|
||||
}
|
||||
|
||||
.dashboard-header p {
|
||||
margin: var(--space-2) 0 0;
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
display: flex;
|
||||
gap: var(--space-2);
|
||||
}
|
||||
|
||||
.workspace {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1.2fr) minmax(340px, 0.9fr);
|
||||
gap: var(--space-4);
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.list-panel,
|
||||
.detail-panel {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
button {
|
||||
border: 0;
|
||||
border-radius: var(--radius-md);
|
||||
background: var(--color-brand);
|
||||
color: var(--color-neutral-900);
|
||||
padding: var(--space-2) var(--space-4);
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s ease;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
button:hover:not(:disabled) {
|
||||
background: var(--color-brand-hover);
|
||||
}
|
||||
|
||||
button.ghost {
|
||||
background: var(--color-bg-card);
|
||||
color: var(--color-text);
|
||||
border: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
button:disabled {
|
||||
opacity: 0.65;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.list-panel h2 {
|
||||
font-size: 1.05rem;
|
||||
margin-bottom: var(--space-2);
|
||||
@@ -91,50 +40,8 @@ button:disabled {
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
.toolbar-field input,
|
||||
.toolbar-field select {
|
||||
width: 100%;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-md);
|
||||
padding: var(--space-2) var(--space-3);
|
||||
background: var(--color-bg-card);
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
.table-wrap {
|
||||
overflow: auto;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-md);
|
||||
background: var(--color-bg-card);
|
||||
max-height: 72vh;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
min-width: 760px;
|
||||
}
|
||||
|
||||
thead {
|
||||
background: var(--color-neutral-100);
|
||||
}
|
||||
|
||||
th,
|
||||
td {
|
||||
text-align: left;
|
||||
padding: var(--space-3);
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
vertical-align: top;
|
||||
font-size: 0.93rem;
|
||||
}
|
||||
|
||||
tbody tr {
|
||||
cursor: pointer;
|
||||
transition: background-color 0.15s ease;
|
||||
}
|
||||
|
||||
tbody tr:hover {
|
||||
background: #fff9d9;
|
||||
}
|
||||
|
||||
tbody tr.selected {
|
||||
@@ -149,22 +56,6 @@ tbody tr.no-results:hover {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.detail-panel {
|
||||
background: var(--color-bg-card);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-md);
|
||||
padding: var(--space-4);
|
||||
min-height: 520px;
|
||||
}
|
||||
|
||||
.detail-panel.empty {
|
||||
display: grid;
|
||||
align-content: center;
|
||||
justify-items: center;
|
||||
text-align: center;
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
.order-uuid {
|
||||
font-size: 0.84rem;
|
||||
color: var(--color-text-muted);
|
||||
@@ -172,38 +63,12 @@ tbody tr.no-results:hover {
|
||||
|
||||
.order-uuid code {
|
||||
font-size: 0.82rem;
|
||||
color: var(--color-text);
|
||||
background: var(--color-neutral-100);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: 6px;
|
||||
padding: 2px 6px;
|
||||
}
|
||||
|
||||
.detail-header h2 {
|
||||
margin: 0 0 var(--space-2);
|
||||
}
|
||||
|
||||
.meta-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: var(--space-2);
|
||||
margin-bottom: var(--space-3);
|
||||
}
|
||||
|
||||
.meta-grid > div {
|
||||
background: var(--color-neutral-100);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-md);
|
||||
padding: var(--space-3);
|
||||
display: grid;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.meta-grid strong {
|
||||
font-size: 0.78rem;
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
.actions-block {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
@@ -223,15 +88,6 @@ tbody tr.no-results:hover {
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
.status-editor select {
|
||||
min-width: 210px;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-md);
|
||||
padding: var(--space-2) var(--space-3);
|
||||
background: var(--color-bg-card);
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
.doc-actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
@@ -333,26 +189,9 @@ tbody tr.no-results:hover {
|
||||
}
|
||||
|
||||
.modal-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: var(--space-2);
|
||||
margin-bottom: var(--space-4);
|
||||
}
|
||||
|
||||
.modal-grid > div {
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-md);
|
||||
background: var(--color-neutral-100);
|
||||
padding: var(--space-3);
|
||||
display: grid;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.modal-grid strong {
|
||||
font-size: 0.78rem;
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
h4 {
|
||||
margin: 0 0 var(--space-2);
|
||||
}
|
||||
@@ -390,10 +229,6 @@ h4 {
|
||||
}
|
||||
|
||||
@media (max-width: 1280px) {
|
||||
.workspace {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.detail-panel {
|
||||
min-height: unset;
|
||||
}
|
||||
@@ -408,15 +243,6 @@ h4 {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.dashboard-header {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.meta-grid,
|
||||
.modal-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.item {
|
||||
align-items: flex-start;
|
||||
}
|
||||
@@ -446,8 +272,8 @@ h4 {
|
||||
}
|
||||
|
||||
@media (max-width: 520px) {
|
||||
th,
|
||||
td {
|
||||
.ui-data-table th,
|
||||
.ui-data-table td {
|
||||
padding: var(--space-2);
|
||||
font-size: 0.88rem;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
<section class="section-card">
|
||||
<header class="section-header">
|
||||
<div>
|
||||
<h2>Sessioni quote</h2>
|
||||
<p>Sessioni create dal configuratore con stato e conversione ordine.</p>
|
||||
<section class="section-card ui-section-card">
|
||||
<header class="section-header ui-section-header">
|
||||
<div class="ui-section-header__copy">
|
||||
<h2 class="ui-section-header__title">Sessioni quote</h2>
|
||||
<p class="ui-section-header__description">
|
||||
Sessioni create dal configuratore con stato e conversione ordine.
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
class="btn-primary"
|
||||
class="ui-button"
|
||||
(click)="loadSessions()"
|
||||
[disabled]="loading"
|
||||
>
|
||||
@@ -17,8 +19,8 @@
|
||||
<p class="error" *ngIf="errorMessage">{{ errorMessage }}</p>
|
||||
<p class="success" *ngIf="successMessage">{{ successMessage }}</p>
|
||||
|
||||
<div class="table-wrap" *ngIf="!loading; else loadingTpl">
|
||||
<table>
|
||||
<div class="table-wrap ui-table-wrap" *ngIf="!loading; else loadingTpl">
|
||||
<table class="ui-data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Sessione</th>
|
||||
@@ -53,14 +55,14 @@
|
||||
<td class="actions">
|
||||
<button
|
||||
type="button"
|
||||
class="btn-secondary"
|
||||
class="ui-button ui-button--ghost"
|
||||
(click)="toggleSessionDetail(session)"
|
||||
>
|
||||
{{ isDetailOpen(session.id) ? "Nascondi" : "Vedi" }}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn-danger"
|
||||
class="ui-button ui-button--danger"
|
||||
(click)="deleteSession(session)"
|
||||
[disabled]="
|
||||
isDeletingSession(session.id) || !!session.convertedOrderId
|
||||
@@ -92,6 +94,7 @@
|
||||
<div class="detail-session-id">
|
||||
<strong>UUID sessione:</strong>
|
||||
<code
|
||||
class="ui-code-pill"
|
||||
[title]="detail.session.id"
|
||||
[appCopyOnClick]="detail.session.id"
|
||||
>{{ detail.session.id }}</code
|
||||
|
||||
@@ -4,76 +4,6 @@
|
||||
gap: var(--space-5);
|
||||
}
|
||||
|
||||
.section-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
gap: var(--space-4);
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: var(--space-2) 0 0;
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
button {
|
||||
border: 0;
|
||||
border-radius: var(--radius-md);
|
||||
padding: var(--space-2) var(--space-4);
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: var(--color-brand);
|
||||
color: var(--color-neutral-900);
|
||||
}
|
||||
|
||||
.btn-primary:hover:not(:disabled) {
|
||||
background: var(--color-brand-hover);
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
background: var(--color-danger-500);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.btn-danger:hover:not(:disabled) {
|
||||
background: #dc2626;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: transparent;
|
||||
color: var(--color-text);
|
||||
border: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
.btn-secondary:hover:not(:disabled) {
|
||||
background: var(--color-neutral-100);
|
||||
}
|
||||
|
||||
.table-wrap {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
min-width: 920px;
|
||||
}
|
||||
|
||||
th,
|
||||
td {
|
||||
text-align: left;
|
||||
padding: var(--space-3);
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
.error {
|
||||
color: var(--color-danger-500);
|
||||
margin: 0;
|
||||
@@ -111,10 +41,6 @@ td {
|
||||
.detail-session-id code {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
padding: var(--space-2);
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid var(--color-border);
|
||||
background: var(--color-neutral-100);
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
@@ -143,11 +69,6 @@ td {
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.section-header {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.actions {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
@@ -171,8 +92,8 @@ td {
|
||||
gap: var(--space-4);
|
||||
}
|
||||
|
||||
th,
|
||||
td {
|
||||
.ui-data-table th,
|
||||
.ui-data-table td {
|
||||
padding: var(--space-2);
|
||||
font-size: 0.86rem;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<div class="checkout-page">
|
||||
<div class="container hero">
|
||||
<h1 class="section-title">{{ "CHECKOUT.TITLE" | translate }}</h1>
|
||||
<p class="cad-subtitle" *ngIf="isCadSession()">
|
||||
<div class="container ui-page-hero">
|
||||
<h1 class="ui-page-title">{{ "CHECKOUT.TITLE" | translate }}</h1>
|
||||
<p class="ui-page-subtitle cad-subtitle" *ngIf="isCadSession()">
|
||||
Servizio CAD
|
||||
<ng-container *ngIf="cadRequestId()">
|
||||
riferito alla richiesta contatto #{{ cadRequestId() }}
|
||||
@@ -10,7 +10,7 @@
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<div class="checkout-layout">
|
||||
<div class="checkout-layout ui-two-column-layout">
|
||||
<!-- LEFT COLUMN: Form -->
|
||||
<div class="checkout-form-section">
|
||||
<!-- Error Message -->
|
||||
@@ -21,10 +21,10 @@
|
||||
<form [formGroup]="checkoutForm" (ngSubmit)="onSubmit()" *ngIf="!error">
|
||||
<!-- Contact Info Card -->
|
||||
<app-card class="mb-6">
|
||||
<div class="card-header-simple">
|
||||
<h3>{{ "CHECKOUT.CONTACT_INFO" | translate }}</h3>
|
||||
<div class="ui-card-header">
|
||||
<h3 class="ui-card-title">{{ "CHECKOUT.CONTACT_INFO" | translate }}</h3>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="ui-form-row">
|
||||
<app-input
|
||||
formControlName="email"
|
||||
type="email"
|
||||
@@ -47,8 +47,8 @@
|
||||
|
||||
<!-- Billing Address Card -->
|
||||
<app-card class="mb-6">
|
||||
<div class="card-header-simple">
|
||||
<h3>{{ "CHECKOUT.BILLING_ADDR" | translate }}</h3>
|
||||
<div class="ui-card-header">
|
||||
<h3 class="ui-card-title">{{ "CHECKOUT.BILLING_ADDR" | translate }}</h3>
|
||||
</div>
|
||||
<div formGroupName="billingAddress">
|
||||
<!-- User Type Selector -->
|
||||
@@ -61,7 +61,7 @@
|
||||
</app-toggle-selector>
|
||||
|
||||
<!-- Private Person Fields -->
|
||||
<div *ngIf="!isCompany" class="form-row">
|
||||
<div *ngIf="!isCompany" class="ui-form-row">
|
||||
<app-input
|
||||
formControlName="firstName"
|
||||
[label]="'CHECKOUT.FIRST_NAME' | translate"
|
||||
@@ -75,7 +75,10 @@
|
||||
</div>
|
||||
|
||||
<!-- Company Fields -->
|
||||
<div *ngIf="isCompany" class="company-fields mb-4">
|
||||
<div
|
||||
*ngIf="isCompany"
|
||||
class="ui-field-stack ui-field-stack--indented mb-4"
|
||||
>
|
||||
<app-input
|
||||
formControlName="companyName"
|
||||
[label]="'CHECKOUT.COMPANY_NAME' | translate"
|
||||
@@ -100,7 +103,7 @@
|
||||
[label]="'CHECKOUT.ADDRESS_2' | translate"
|
||||
></app-input>
|
||||
|
||||
<div class="form-row three-cols">
|
||||
<div class="ui-form-row ui-form-row--three">
|
||||
<app-input
|
||||
formControlName="zip"
|
||||
[label]="'CHECKOUT.ZIP' | translate"
|
||||
@@ -123,10 +126,10 @@
|
||||
</app-card>
|
||||
|
||||
<!-- Shipping Option -->
|
||||
<div class="shipping-option">
|
||||
<label class="checkbox-container">
|
||||
<div class="shipping-option ui-soft-panel">
|
||||
<label class="ui-checkbox">
|
||||
<input type="checkbox" formControlName="shippingSameAsBilling" />
|
||||
<span class="checkmark"></span>
|
||||
<span class="ui-checkbox__mark"></span>
|
||||
{{ "CHECKOUT.SHIPPING_SAME" | translate }}
|
||||
</label>
|
||||
</div>
|
||||
@@ -136,11 +139,11 @@
|
||||
*ngIf="!checkoutForm.get('shippingSameAsBilling')?.value"
|
||||
class="mb-6"
|
||||
>
|
||||
<div class="card-header-simple">
|
||||
<h3>{{ "CHECKOUT.SHIPPING_ADDR" | translate }}</h3>
|
||||
<div class="ui-card-header">
|
||||
<h3 class="ui-card-title">{{ "CHECKOUT.SHIPPING_ADDR" | translate }}</h3>
|
||||
</div>
|
||||
<div formGroupName="shippingAddress">
|
||||
<div class="form-row">
|
||||
<div class="ui-form-row">
|
||||
<app-input
|
||||
formControlName="firstName"
|
||||
[label]="'CHECKOUT.FIRST_NAME' | translate"
|
||||
@@ -151,7 +154,7 @@
|
||||
></app-input>
|
||||
</div>
|
||||
|
||||
<div *ngIf="isCompany" class="company-fields">
|
||||
<div *ngIf="isCompany" class="ui-field-stack ui-field-stack--indented">
|
||||
<app-input
|
||||
formControlName="companyName"
|
||||
[label]="'CHECKOUT.COMPANY_OPTIONAL' | translate"
|
||||
@@ -167,7 +170,7 @@
|
||||
[label]="'CHECKOUT.ADDRESS_1' | translate"
|
||||
></app-input>
|
||||
|
||||
<div class="form-row three-cols">
|
||||
<div class="ui-form-row ui-form-row--three">
|
||||
<app-input
|
||||
formControlName="zip"
|
||||
[label]="'CHECKOUT.ZIP' | translate"
|
||||
@@ -187,9 +190,9 @@
|
||||
</app-card>
|
||||
|
||||
<div class="legal-consent">
|
||||
<label class="checkbox-container">
|
||||
<label class="ui-checkbox">
|
||||
<input type="checkbox" formControlName="acceptLegal" />
|
||||
<span class="checkmark"></span>
|
||||
<span class="ui-checkbox__mark"></span>
|
||||
<span>
|
||||
{{ "LEGAL.CONSENT.LABEL_PREFIX" | translate }}
|
||||
<a href="/terms" target="_blank" rel="noopener">{{
|
||||
@@ -213,7 +216,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
<div class="ui-actions">
|
||||
<app-button
|
||||
type="submit"
|
||||
[disabled]="checkoutForm.invalid || isSubmitting()"
|
||||
@@ -233,8 +236,8 @@
|
||||
<!-- RIGHT COLUMN: Order Summary -->
|
||||
<div class="checkout-summary-section">
|
||||
<app-card class="sticky-card">
|
||||
<div class="card-header-simple">
|
||||
<h3>{{ "CHECKOUT.SUMMARY_TITLE" | translate }}</h3>
|
||||
<div class="ui-card-header">
|
||||
<h3 class="ui-card-title">{{ "CHECKOUT.SUMMARY_TITLE" | translate }}</h3>
|
||||
</div>
|
||||
|
||||
<div class="summary-items" *ngIf="quoteSession() as session">
|
||||
|
||||
@@ -1,74 +1,5 @@
|
||||
.hero {
|
||||
padding: var(--space-8) 0;
|
||||
text-align: center;
|
||||
|
||||
.section-title {
|
||||
font-size: 2.5rem;
|
||||
margin-bottom: var(--space-2);
|
||||
}
|
||||
}
|
||||
|
||||
.cad-subtitle {
|
||||
margin: 0;
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
.checkout-layout {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 420px;
|
||||
gap: var(--space-8);
|
||||
align-items: start;
|
||||
margin-bottom: var(--space-12);
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
grid-template-columns: 1fr;
|
||||
gap: var(--space-8);
|
||||
}
|
||||
}
|
||||
|
||||
.card-header-simple {
|
||||
margin-bottom: var(--space-6);
|
||||
padding-bottom: var(--space-4);
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
|
||||
h3 {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
color: var(--color-text);
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.form-row {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-4);
|
||||
margin-bottom: var(--space-4);
|
||||
|
||||
@media (min-width: 768px) {
|
||||
flex-direction: row;
|
||||
& > * {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&.no-margin {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
&.three-cols {
|
||||
display: grid;
|
||||
grid-template-columns: 1.5fr 2fr 1fr;
|
||||
gap: var(--space-4);
|
||||
|
||||
@media (max-width: 768px) {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
app-input {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
/* User Type Selector CSS has been extracted to app-toggle-selector component */
|
||||
@@ -78,97 +9,18 @@ app-toggle-selector.user-type-selector-compact {
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
.company-fields {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-4);
|
||||
padding-left: var(--space-4);
|
||||
border-left: 2px solid var(--color-border);
|
||||
margin-bottom: var(--space-4);
|
||||
}
|
||||
|
||||
.shipping-option {
|
||||
margin: var(--space-6) 0;
|
||||
padding: var(--space-4);
|
||||
background: var(--color-neutral-100);
|
||||
border-radius: var(--radius-md);
|
||||
}
|
||||
|
||||
.legal-consent {
|
||||
margin: var(--space-4) 0 var(--space-4);
|
||||
|
||||
.checkbox-container {
|
||||
font-size: 0.95rem;
|
||||
line-height: 1.4;
|
||||
align-items: flex-start;
|
||||
min-height: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Custom Checkbox */
|
||||
.checkbox-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
padding-left: 36px;
|
||||
cursor: pointer;
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
user-select: none;
|
||||
color: var(--color-text);
|
||||
|
||||
input {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
cursor: pointer;
|
||||
height: 0;
|
||||
width: 0;
|
||||
|
||||
&:checked ~ .checkmark {
|
||||
background-color: var(--color-brand);
|
||||
border-color: var(--color-brand);
|
||||
|
||||
&:after {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.checkmark {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 0;
|
||||
transform: translateY(-50%);
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
background-color: var(--color-bg-card);
|
||||
border: 2px solid var(--color-border);
|
||||
border-radius: var(--radius-sm);
|
||||
transition: all 0.2s;
|
||||
|
||||
&:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
display: none;
|
||||
left: 7px;
|
||||
top: 3px;
|
||||
width: 6px;
|
||||
height: 12px;
|
||||
border: solid #000;
|
||||
border-width: 0 2.5px 2.5px 0;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
}
|
||||
|
||||
&:hover input ~ .checkmark {
|
||||
border-color: var(--color-brand);
|
||||
}
|
||||
}
|
||||
|
||||
.consent-error {
|
||||
margin-top: var(--space-2);
|
||||
margin-left: 36px;
|
||||
color: var(--color-danger-500, #ef4444);
|
||||
margin-left: 2.1rem;
|
||||
color: var(--color-danger-500);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
@@ -355,9 +207,7 @@ app-toggle-selector.user-type-selector-compact {
|
||||
padding-right: var(--space-3);
|
||||
}
|
||||
|
||||
.actions {
|
||||
margin-top: var(--space-8);
|
||||
|
||||
.ui-actions {
|
||||
app-button {
|
||||
width: 100%;
|
||||
}
|
||||
@@ -365,11 +215,11 @@ app-toggle-selector.user-type-selector-compact {
|
||||
|
||||
.error-message {
|
||||
color: var(--color-error);
|
||||
background: #fef2f2;
|
||||
background: var(--color-surface-danger);
|
||||
padding: var(--space-4);
|
||||
border-radius: var(--radius-md);
|
||||
margin-bottom: var(--space-6);
|
||||
border: 1px solid #fee2e2;
|
||||
border: 1px solid #fecaca;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,16 +6,18 @@
|
||||
} @else {
|
||||
<form [formGroup]="form" (ngSubmit)="onSubmit()">
|
||||
<!-- Request Type -->
|
||||
<div class="form-group">
|
||||
<label>{{ "CONTACT.REQ_TYPE_LABEL" | translate }} *</label>
|
||||
<select formControlName="requestType" class="form-control">
|
||||
<div class="ui-form-group">
|
||||
<label class="ui-form-label"
|
||||
>{{ "CONTACT.REQ_TYPE_LABEL" | translate }} *</label
|
||||
>
|
||||
<select formControlName="requestType" class="ui-form-control">
|
||||
<option *ngFor="let type of requestTypes" [value]="type.value">
|
||||
{{ type.label | translate }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="ui-form-row">
|
||||
<!-- Phone -->
|
||||
<app-input
|
||||
formControlName="email"
|
||||
@@ -35,22 +37,12 @@
|
||||
</div>
|
||||
|
||||
<!-- User Type Selector (Segmented Control) -->
|
||||
<div class="user-type-selector">
|
||||
<div
|
||||
class="type-option"
|
||||
[class.selected]="!isCompany"
|
||||
(click)="setCompanyMode(false)"
|
||||
>
|
||||
{{ "CONTACT.TYPE_PRIVATE" | translate }}
|
||||
</div>
|
||||
<div
|
||||
class="type-option"
|
||||
[class.selected]="isCompany"
|
||||
(click)="setCompanyMode(true)"
|
||||
>
|
||||
{{ "CONTACT.TYPE_COMPANY" | translate }}
|
||||
</div>
|
||||
</div>
|
||||
<app-toggle-selector
|
||||
class="mb-4"
|
||||
[options]="customerTypeOptions"
|
||||
[selectedValue]="isCompany"
|
||||
(selectionChange)="setCompanyMode($event)"
|
||||
></app-toggle-selector>
|
||||
|
||||
<!-- Personal Name (Only if NOT Company) -->
|
||||
<app-input
|
||||
@@ -61,7 +53,7 @@
|
||||
></app-input>
|
||||
|
||||
<!-- Company Fields (Only if Company) -->
|
||||
<div *ngIf="isCompany" class="company-fields">
|
||||
<div *ngIf="isCompany" class="ui-field-stack ui-field-stack--indented">
|
||||
<app-input
|
||||
formControlName="companyName"
|
||||
[label]="('CONTACT.COMPANY_NAME' | translate) + ' *'"
|
||||
@@ -74,20 +66,24 @@
|
||||
></app-input>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>{{ "CONTACT.LABEL_MESSAGE" | translate }}</label>
|
||||
<div class="ui-form-group">
|
||||
<label class="ui-form-label">{{
|
||||
"CONTACT.LABEL_MESSAGE" | translate
|
||||
}}</label>
|
||||
<textarea
|
||||
formControlName="message"
|
||||
class="form-control"
|
||||
class="ui-form-control"
|
||||
rows="4"
|
||||
></textarea>
|
||||
</div>
|
||||
|
||||
<!-- File Upload Section -->
|
||||
<div class="form-group">
|
||||
<label>{{ "CONTACT.UPLOAD_LABEL" | translate }}</label>
|
||||
<p class="hint">{{ "CONTACT.UPLOAD_HINT" | translate }}</p>
|
||||
<p class="hint upload-privacy-note">
|
||||
<div class="ui-form-group">
|
||||
<label class="ui-form-label">{{
|
||||
"CONTACT.UPLOAD_LABEL" | translate
|
||||
}}</label>
|
||||
<p class="ui-form-hint">{{ "CONTACT.UPLOAD_HINT" | translate }}</p>
|
||||
<p class="ui-form-hint upload-privacy-note">
|
||||
{{ "LEGAL.CONSENT.UPLOAD_NOTICE_PREFIX" | translate }}
|
||||
<a href="/privacy" target="_blank" rel="noopener">{{
|
||||
"LEGAL.CONSENT.UPLOAD_NOTICE_LINK" | translate
|
||||
@@ -160,9 +156,9 @@
|
||||
</div>
|
||||
|
||||
<div class="legal-consent">
|
||||
<label class="checkbox-container">
|
||||
<label class="ui-checkbox">
|
||||
<input type="checkbox" formControlName="acceptLegal" />
|
||||
<span class="checkmark"></span>
|
||||
<span class="ui-checkbox__mark"></span>
|
||||
<span>
|
||||
{{ "LEGAL.CONSENT.LABEL_PREFIX" | translate }}
|
||||
<a href="/terms" target="_blank" rel="noopener">{{
|
||||
@@ -185,7 +181,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
<div class="ui-actions ui-actions--end">
|
||||
<app-button type="submit" [disabled]="form.invalid || sent()">
|
||||
{{
|
||||
sent()
|
||||
|
||||
@@ -1,109 +1,12 @@
|
||||
.form-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-bottom: var(--space-4);
|
||||
}
|
||||
label {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
margin-bottom: var(--space-2);
|
||||
color: var(--color-text);
|
||||
}
|
||||
.hint {
|
||||
font-size: 0.75rem;
|
||||
color: var(--color-text-muted);
|
||||
margin-bottom: var(--space-2);
|
||||
}
|
||||
.upload-privacy-note {
|
||||
margin-top: calc(var(--space-2) * -1);
|
||||
font-size: 0.78rem;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
padding: 0.5rem 0.75rem;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-md);
|
||||
width: 100%;
|
||||
background: var(--color-bg-card);
|
||||
color: var(--color-text);
|
||||
font-family: inherit;
|
||||
&:focus {
|
||||
outline: none;
|
||||
border-color: var(--color-brand);
|
||||
}
|
||||
}
|
||||
|
||||
select.form-control {
|
||||
appearance: none;
|
||||
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e");
|
||||
background-repeat: no-repeat;
|
||||
background-position: right 1rem center;
|
||||
background-size: 1em;
|
||||
}
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-4);
|
||||
margin-bottom: var(--space-4);
|
||||
@media (min-width: 768px) {
|
||||
flex-direction: row;
|
||||
.col {
|
||||
flex: 1;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
app-input.col {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* User Type Selector Styles */
|
||||
.user-type-selector {
|
||||
display: flex;
|
||||
background-color: var(--color-neutral-100);
|
||||
border-radius: var(--radius-md);
|
||||
padding: 4px;
|
||||
margin-bottom: var(--space-4);
|
||||
gap: 4px;
|
||||
width: 100%; /* Full width */
|
||||
max-width: 400px; /* Limit on desktop */
|
||||
}
|
||||
|
||||
.type-option {
|
||||
flex: 1; /* Equal width */
|
||||
text-align: center;
|
||||
padding: 8px 16px;
|
||||
border-radius: var(--radius-sm);
|
||||
cursor: pointer;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
color: var(--color-text-muted);
|
||||
transition: all 0.2s ease;
|
||||
user-select: none;
|
||||
|
||||
&:hover {
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
&.selected {
|
||||
background-color: var(--color-brand);
|
||||
color: #000;
|
||||
font-weight: 600;
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
}
|
||||
|
||||
.company-fields {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-4);
|
||||
padding-left: var(--space-4);
|
||||
border-left: 2px solid var(--color-border);
|
||||
margin-bottom: var(--space-4);
|
||||
}
|
||||
|
||||
/* File Upload Styles */
|
||||
.drop-zone {
|
||||
border: 2px dashed var(--color-border);
|
||||
@@ -204,66 +107,6 @@ app-input.col {
|
||||
|
||||
.legal-consent {
|
||||
margin: var(--space-4) 0 var(--space-4);
|
||||
}
|
||||
|
||||
.checkbox-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
padding-left: 36px;
|
||||
cursor: pointer;
|
||||
font-size: 0.95rem;
|
||||
font-weight: 500;
|
||||
user-select: none;
|
||||
color: var(--color-text);
|
||||
line-height: 1.4;
|
||||
|
||||
input {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
cursor: pointer;
|
||||
height: 0;
|
||||
width: 0;
|
||||
|
||||
&:checked ~ .checkmark {
|
||||
background-color: var(--color-brand);
|
||||
border-color: var(--color-brand);
|
||||
|
||||
&:after {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.checkmark {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 0;
|
||||
transform: translateY(-50%);
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
background-color: var(--color-bg-card);
|
||||
border: 2px solid var(--color-border);
|
||||
border-radius: var(--radius-sm);
|
||||
transition: all 0.2s;
|
||||
|
||||
&:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
display: none;
|
||||
left: 7px;
|
||||
top: 3px;
|
||||
width: 6px;
|
||||
height: 12px;
|
||||
border: solid #000;
|
||||
border-width: 0 2.5px 2.5px 0;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
}
|
||||
|
||||
&:hover input ~ .checkmark {
|
||||
border-color: var(--color-brand);
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color-brand);
|
||||
@@ -273,8 +116,8 @@ app-input.col {
|
||||
|
||||
.consent-error {
|
||||
margin-top: var(--space-2);
|
||||
margin-left: 36px;
|
||||
color: var(--color-danger-500, #ef4444);
|
||||
margin-left: 2.1rem;
|
||||
color: var(--color-danger-500);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,10 @@ import {
|
||||
import { TranslateModule, TranslateService } from '@ngx-translate/core';
|
||||
import { AppInputComponent } from '../../../../shared/components/app-input/app-input.component';
|
||||
import { AppButtonComponent } from '../../../../shared/components/app-button/app-button.component';
|
||||
import {
|
||||
AppToggleSelectorComponent,
|
||||
ToggleOption,
|
||||
} from '../../../../shared/components/app-toggle-selector/app-toggle-selector.component';
|
||||
import { QuoteEstimatorService } from '../../../calculator/services/quote-estimator.service';
|
||||
import { QuoteRequestService } from '../../../../core/services/quote-request.service';
|
||||
import { LanguageService } from '../../../../core/services/language.service';
|
||||
@@ -30,6 +34,7 @@ import { SuccessStateComponent } from '../../../../shared/components/success-sta
|
||||
TranslateModule,
|
||||
AppInputComponent,
|
||||
AppButtonComponent,
|
||||
AppToggleSelectorComponent,
|
||||
SuccessStateComponent,
|
||||
],
|
||||
templateUrl: './contact-form.component.html',
|
||||
@@ -52,6 +57,10 @@ export class ContactFormComponent implements OnDestroy {
|
||||
{ value: 'consult', label: 'CONTACT.REQ_TYPE_CONSULT' },
|
||||
{ value: 'question', label: 'CONTACT.REQ_TYPE_QUESTION' },
|
||||
];
|
||||
customerTypeOptions: ToggleOption[] = [
|
||||
{ label: 'CONTACT.TYPE_PRIVATE', value: false },
|
||||
{ label: 'CONTACT.TYPE_COMPANY', value: true },
|
||||
];
|
||||
|
||||
private quoteRequestService = inject(QuoteRequestService);
|
||||
private languageService = inject(LanguageService);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<div class="container hero">
|
||||
<h1>
|
||||
<div class="container ui-page-hero ui-page-hero--spacious">
|
||||
<h1 class="ui-page-title">
|
||||
{{ "TRACKING.TITLE" | translate }}
|
||||
<ng-container *ngIf="order()">
|
||||
<br /><span class="order-id-title"
|
||||
@@ -7,7 +7,7 @@
|
||||
>
|
||||
</ng-container>
|
||||
</h1>
|
||||
<p class="subtitle">{{ "TRACKING.SUBTITLE" | translate }}</p>
|
||||
<p class="ui-page-subtitle subtitle">{{ "TRACKING.SUBTITLE" | translate }}</p>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
@@ -69,18 +69,18 @@
|
||||
</div>
|
||||
</app-card>
|
||||
|
||||
<div class="payment-layout">
|
||||
<div class="payment-layout ui-two-column-layout">
|
||||
<div class="payment-main">
|
||||
<app-card class="mb-6">
|
||||
<div class="card-header-simple">
|
||||
<h3>{{ "PAYMENT.METHOD" | translate }}</h3>
|
||||
<div class="ui-card-header">
|
||||
<h3 class="ui-card-title">{{ "PAYMENT.METHOD" | translate }}</h3>
|
||||
</div>
|
||||
|
||||
<div class="payment-selection">
|
||||
<div class="methods-grid">
|
||||
<div class="ui-choice-grid">
|
||||
<div
|
||||
class="type-option"
|
||||
[class.selected]="selectedPaymentMethod === 'twint'"
|
||||
class="ui-choice-card"
|
||||
[class.is-selected]="selectedPaymentMethod === 'twint'"
|
||||
(click)="selectPayment('twint')"
|
||||
>
|
||||
<span class="method-name">{{
|
||||
@@ -88,8 +88,8 @@
|
||||
}}</span>
|
||||
</div>
|
||||
<div
|
||||
class="type-option"
|
||||
[class.selected]="selectedPaymentMethod === 'bill'"
|
||||
class="ui-choice-card"
|
||||
[class.is-selected]="selectedPaymentMethod === 'bill'"
|
||||
(click)="selectPayment('bill')"
|
||||
>
|
||||
<span class="method-name">{{
|
||||
@@ -100,7 +100,7 @@
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="payment-details fade-in text-center"
|
||||
class="payment-details ui-soft-panel fade-in text-center"
|
||||
*ngIf="selectedPaymentMethod === 'twint'"
|
||||
>
|
||||
<div class="details-header">
|
||||
@@ -119,22 +119,9 @@
|
||||
{{ "PAYMENT.BILLING_INFO_HINT" | translate }}
|
||||
</p>
|
||||
<div class="twint-mobile-action twint-button-container">
|
||||
<button
|
||||
style="
|
||||
width: auto;
|
||||
height: 58px;
|
||||
border-radius: 6px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
align-items: center;
|
||||
"
|
||||
(click)="openTwintPayment()"
|
||||
>
|
||||
<button type="button" class="twint-launch-button" (click)="openTwintPayment()">
|
||||
<img
|
||||
style="width: auto; height: 58px"
|
||||
class="twint-launch-button__image"
|
||||
[attr.alt]="'PAYMENT.TWINT_BUTTON_ALT' | translate"
|
||||
[src]="getTwintButtonImageUrl()"
|
||||
/>
|
||||
@@ -148,7 +135,7 @@
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="payment-details fade-in text-center"
|
||||
class="payment-details ui-soft-panel fade-in text-center"
|
||||
*ngIf="selectedPaymentMethod === 'bill'"
|
||||
>
|
||||
<div class="details-header">
|
||||
@@ -167,7 +154,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
<div class="ui-actions">
|
||||
<app-button
|
||||
variant="outline"
|
||||
(click)="completeOrder()"
|
||||
@@ -188,9 +175,11 @@
|
||||
|
||||
<div class="payment-summary">
|
||||
<app-card class="sticky-card">
|
||||
<div class="card-header-simple">
|
||||
<h3>{{ "PAYMENT.SUMMARY_TITLE" | translate }}</h3>
|
||||
<p class="order-id">#{{ getDisplayOrderNumber(o) }}</p>
|
||||
<div class="ui-card-header">
|
||||
<h3 class="ui-card-title">{{ "PAYMENT.SUMMARY_TITLE" | translate }}</h3>
|
||||
<p class="ui-card-subtitle order-id">
|
||||
#{{ getDisplayOrderNumber(o) }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<app-price-breakdown
|
||||
|
||||
@@ -1,99 +1,13 @@
|
||||
.hero {
|
||||
padding: var(--space-12) 0 var(--space-8);
|
||||
text-align: center;
|
||||
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
margin-bottom: var(--space-2);
|
||||
}
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 1.125rem;
|
||||
color: var(--color-text-muted);
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.payment-layout {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 400px;
|
||||
gap: var(--space-8);
|
||||
align-items: start;
|
||||
margin-bottom: var(--space-12);
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
grid-template-columns: 1fr;
|
||||
gap: var(--space-8);
|
||||
}
|
||||
}
|
||||
|
||||
.card-header-simple {
|
||||
margin-bottom: var(--space-6);
|
||||
padding-bottom: var(--space-4);
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
|
||||
h3 {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
color: var(--color-text);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.order-id {
|
||||
font-size: 0.875rem;
|
||||
color: var(--color-text-muted);
|
||||
margin-top: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.payment-selection {
|
||||
margin-bottom: var(--space-6);
|
||||
}
|
||||
|
||||
.methods-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: var(--space-4);
|
||||
|
||||
@media (max-width: 600px) {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.type-option {
|
||||
border: 2px solid var(--color-border);
|
||||
border-radius: var(--radius-md);
|
||||
padding: var(--space-6);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
background: var(--color-bg-card);
|
||||
text-align: center;
|
||||
font-weight: 600;
|
||||
color: var(--color-text-muted);
|
||||
|
||||
&:hover {
|
||||
border-color: var(--color-brand);
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
&.selected {
|
||||
border-color: var(--color-brand);
|
||||
background-color: var(--color-neutral-100);
|
||||
color: #000;
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
}
|
||||
|
||||
.payment-details {
|
||||
background: var(--color-neutral-100);
|
||||
border-radius: var(--radius-md);
|
||||
padding: var(--space-6);
|
||||
margin-bottom: var(--space-6);
|
||||
border: 1px solid var(--color-border);
|
||||
|
||||
&.text-center {
|
||||
text-align: center;
|
||||
@@ -127,6 +41,23 @@
|
||||
}
|
||||
}
|
||||
|
||||
.twint-launch-button {
|
||||
width: auto;
|
||||
height: 58px;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: transparent;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.twint-launch-button__image {
|
||||
width: auto;
|
||||
height: 58px;
|
||||
}
|
||||
|
||||
.qr-placeholder {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -184,10 +115,6 @@
|
||||
top: var(--space-6);
|
||||
}
|
||||
|
||||
.actions {
|
||||
margin-top: var(--space-8);
|
||||
}
|
||||
|
||||
.fade-in {
|
||||
animation: fadeIn 0.4s ease-out;
|
||||
}
|
||||
|
||||
@@ -60,3 +60,20 @@
|
||||
color: var(--color-text);
|
||||
}
|
||||
}
|
||||
|
||||
.btn-ghost {
|
||||
background-color: var(--color-bg-card);
|
||||
border-color: var(--color-border);
|
||||
color: var(--color-text);
|
||||
&:hover:not(:disabled) {
|
||||
background-color: var(--color-surface-muted);
|
||||
}
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
background-color: var(--color-danger-500);
|
||||
color: #fff;
|
||||
&:hover:not(:disabled) {
|
||||
background-color: #dc2626;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,9 @@ import { CommonModule } from '@angular/common';
|
||||
styleUrl: './app-button.component.scss',
|
||||
})
|
||||
export class AppButtonComponent {
|
||||
variant = input<'primary' | 'secondary' | 'outline' | 'text'>('primary');
|
||||
variant = input<
|
||||
'primary' | 'secondary' | 'outline' | 'text' | 'ghost' | 'danger'
|
||||
>('primary');
|
||||
type = input<'button' | 'submit' | 'reset'>('button');
|
||||
disabled = input<boolean>(false);
|
||||
fullWidth = input<boolean>(false);
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
<div class="card">
|
||||
<ng-content></ng-content>
|
||||
@if (title() || subtitle()) {
|
||||
<header class="card-header">
|
||||
@if (title()) {
|
||||
<h3 class="card-title">{{ title() }}</h3>
|
||||
}
|
||||
@if (subtitle()) {
|
||||
<p class="card-subtitle">{{ subtitle() }}</p>
|
||||
}
|
||||
</header>
|
||||
}
|
||||
<div class="card-body">
|
||||
<ng-content></ng-content>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
border-radius: var(--radius-lg);
|
||||
border: 1px solid var(--color-border);
|
||||
box-shadow: var(--shadow-sm);
|
||||
padding: var(--space-6);
|
||||
transition:
|
||||
box-shadow 0.2s ease,
|
||||
transform 0.2s ease,
|
||||
@@ -22,3 +21,23 @@
|
||||
border-color: var(--color-neutral-300);
|
||||
}
|
||||
}
|
||||
|
||||
.card-header {
|
||||
padding: var(--space-6) var(--space-6) var(--space-4);
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
.card-title {
|
||||
margin: 0;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.card-subtitle {
|
||||
margin: var(--space-2) 0 0;
|
||||
color: var(--color-text-muted);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.card-body {
|
||||
padding: var(--space-6);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Component, input } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-card',
|
||||
@@ -6,4 +6,7 @@ import { Component } from '@angular/core';
|
||||
templateUrl: './app-card.component.html',
|
||||
styleUrl: './app-card.component.scss',
|
||||
})
|
||||
export class AppCardComponent {}
|
||||
export class AppCardComponent {
|
||||
title = input<string>('');
|
||||
subtitle = input<string>('');
|
||||
}
|
||||
|
||||
@@ -14,20 +14,24 @@ label {
|
||||
margin-left: 2px;
|
||||
}
|
||||
.form-control {
|
||||
padding: 0.5rem 0.75rem;
|
||||
padding: 0.625rem 0.75rem;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-md);
|
||||
font-size: 1rem;
|
||||
width: 100%;
|
||||
background: var(--color-bg-card);
|
||||
color: var(--color-text);
|
||||
transition:
|
||||
border-color 0.2s ease,
|
||||
box-shadow 0.2s ease,
|
||||
background-color 0.2s ease;
|
||||
&:focus {
|
||||
outline: none;
|
||||
border-color: var(--color-brand);
|
||||
box-shadow: 0 0 0 2px rgba(250, 207, 10, 0.25);
|
||||
box-shadow: var(--focus-ring);
|
||||
}
|
||||
&:disabled {
|
||||
background: var(--color-neutral-100);
|
||||
background: var(--color-surface-muted);
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,16 +10,20 @@ label {
|
||||
color: var(--color-text);
|
||||
}
|
||||
.form-control {
|
||||
padding: 0.5rem 0.75rem;
|
||||
padding: 0.625rem 0.75rem;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-md);
|
||||
font-size: 1rem;
|
||||
width: 100%;
|
||||
background: var(--color-bg-card);
|
||||
color: var(--color-text);
|
||||
transition:
|
||||
border-color 0.2s ease,
|
||||
box-shadow 0.2s ease;
|
||||
&:focus {
|
||||
outline: none;
|
||||
border-color: var(--color-brand);
|
||||
box-shadow: var(--focus-ring);
|
||||
}
|
||||
}
|
||||
.error-text {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/* src/styles.scss */
|
||||
@use "./styles/theme";
|
||||
@use "./styles/patterns";
|
||||
@use "./styles/ui";
|
||||
|
||||
/* Reset / Base */
|
||||
*,
|
||||
|
||||
509
frontend/src/styles/_ui.scss
Normal file
509
frontend/src/styles/_ui.scss
Normal file
@@ -0,0 +1,509 @@
|
||||
.ui-page-hero {
|
||||
padding: var(--space-8) 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.ui-page-hero--spacious {
|
||||
padding: var(--space-12) 0 var(--space-8);
|
||||
}
|
||||
|
||||
.ui-page-title {
|
||||
margin: 0 0 var(--space-2);
|
||||
font-size: clamp(2rem, 4vw, 2.75rem);
|
||||
}
|
||||
|
||||
.ui-page-subtitle {
|
||||
max-width: 680px;
|
||||
margin: 0 auto;
|
||||
color: var(--color-text-muted);
|
||||
font-size: 1.05rem;
|
||||
}
|
||||
|
||||
.ui-two-column-layout {
|
||||
--ui-sidebar-width: 400px;
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) minmax(300px, var(--ui-sidebar-width));
|
||||
gap: var(--space-8);
|
||||
align-items: start;
|
||||
margin-bottom: var(--space-12);
|
||||
}
|
||||
|
||||
.ui-section-card {
|
||||
background: var(--color-bg-card);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: clamp(12px, 2vw, 24px);
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.ui-section-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
gap: var(--space-4);
|
||||
margin-bottom: var(--space-5);
|
||||
}
|
||||
|
||||
.ui-section-header__copy {
|
||||
display: grid;
|
||||
gap: var(--space-1);
|
||||
}
|
||||
|
||||
.ui-section-header__title {
|
||||
margin: 0;
|
||||
font-size: 1.35rem;
|
||||
}
|
||||
|
||||
.ui-section-header__description {
|
||||
margin: 0;
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
.ui-section-header__meta {
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.ui-card-header {
|
||||
margin-bottom: var(--space-6);
|
||||
padding-bottom: var(--space-4);
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
.ui-card-title {
|
||||
margin: 0;
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.ui-card-subtitle {
|
||||
margin: var(--space-2) 0 0;
|
||||
color: var(--color-text-muted);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.ui-form-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-2);
|
||||
margin-bottom: var(--space-4);
|
||||
}
|
||||
|
||||
.ui-form-label {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.ui-form-hint {
|
||||
margin: 0;
|
||||
font-size: 0.78rem;
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
.ui-form-control {
|
||||
width: 100%;
|
||||
padding: 0.625rem 0.75rem;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-md);
|
||||
background: var(--color-bg-card);
|
||||
color: var(--color-text);
|
||||
font: inherit;
|
||||
transition:
|
||||
border-color 0.2s ease,
|
||||
box-shadow 0.2s ease,
|
||||
background-color 0.2s ease;
|
||||
}
|
||||
|
||||
.ui-form-control:focus {
|
||||
outline: none;
|
||||
border-color: var(--color-brand);
|
||||
box-shadow: var(--focus-ring);
|
||||
}
|
||||
|
||||
.ui-form-control:disabled {
|
||||
background: var(--color-surface-muted);
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
select.ui-form-control {
|
||||
appearance: none;
|
||||
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23101820' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e");
|
||||
background-repeat: no-repeat;
|
||||
background-position: right 0.9rem center;
|
||||
background-size: 1rem;
|
||||
padding-right: 2.5rem;
|
||||
}
|
||||
|
||||
.ui-form-row {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-4);
|
||||
margin-bottom: var(--space-4);
|
||||
}
|
||||
|
||||
.ui-form-row > * {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.ui-form-row--three {
|
||||
display: grid;
|
||||
grid-template-columns: 1.5fr 2fr 1fr;
|
||||
}
|
||||
|
||||
.ui-field-stack {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-4);
|
||||
margin-bottom: var(--space-4);
|
||||
}
|
||||
|
||||
.ui-field-stack--indented {
|
||||
padding-left: var(--space-4);
|
||||
border-left: 2px solid var(--color-border);
|
||||
}
|
||||
|
||||
.ui-checkbox {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 0.85rem;
|
||||
cursor: pointer;
|
||||
color: var(--color-text);
|
||||
line-height: 1.45;
|
||||
}
|
||||
|
||||
.ui-checkbox input {
|
||||
position: absolute;
|
||||
inset: 0 auto auto 0;
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
opacity: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.ui-checkbox__mark {
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
margin-top: 0.1rem;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-sm);
|
||||
background: var(--color-bg-card);
|
||||
flex-shrink: 0;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition:
|
||||
background-color 0.2s ease,
|
||||
border-color 0.2s ease,
|
||||
box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
.ui-checkbox__mark::after {
|
||||
content: "";
|
||||
width: 0.4rem;
|
||||
height: 0.72rem;
|
||||
border: solid var(--color-neutral-900);
|
||||
border-width: 0 2px 2px 0;
|
||||
transform: rotate(45deg) scale(0);
|
||||
transition: transform 0.16s ease;
|
||||
}
|
||||
|
||||
.ui-checkbox input:focus-visible + .ui-checkbox__mark {
|
||||
box-shadow: var(--focus-ring);
|
||||
}
|
||||
|
||||
.ui-checkbox input:checked + .ui-checkbox__mark {
|
||||
background: var(--color-brand);
|
||||
border-color: var(--color-brand);
|
||||
}
|
||||
|
||||
.ui-checkbox input:checked + .ui-checkbox__mark::after {
|
||||
transform: rotate(45deg) scale(1);
|
||||
}
|
||||
|
||||
.ui-soft-panel {
|
||||
padding: var(--space-4);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-md);
|
||||
background: var(--color-surface-muted);
|
||||
}
|
||||
|
||||
.ui-choice-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: var(--space-4);
|
||||
}
|
||||
|
||||
.ui-choice-card {
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-md);
|
||||
padding: var(--space-5);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: var(--color-bg-card);
|
||||
color: var(--color-text-muted);
|
||||
text-align: center;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition:
|
||||
border-color 0.2s ease,
|
||||
box-shadow 0.2s ease,
|
||||
background-color 0.2s ease,
|
||||
color 0.2s ease,
|
||||
transform 0.2s ease;
|
||||
}
|
||||
|
||||
.ui-choice-card:hover {
|
||||
border-color: var(--color-brand);
|
||||
color: var(--color-text);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.ui-choice-card.is-selected,
|
||||
.ui-choice-card.selected {
|
||||
border-color: var(--color-brand);
|
||||
background: var(--color-surface-muted);
|
||||
color: var(--color-neutral-900);
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.ui-actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--space-3);
|
||||
margin-top: var(--space-6);
|
||||
}
|
||||
|
||||
.ui-actions--end {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.ui-actions--stack > * {
|
||||
flex: 1 1 220px;
|
||||
}
|
||||
|
||||
.ui-button {
|
||||
border: 0;
|
||||
border-radius: var(--radius-md);
|
||||
padding: var(--space-2) var(--space-4);
|
||||
font: inherit;
|
||||
font-weight: 600;
|
||||
line-height: 1.2;
|
||||
cursor: pointer;
|
||||
transition:
|
||||
background-color 0.2s ease,
|
||||
border-color 0.2s ease,
|
||||
color 0.2s ease,
|
||||
opacity 0.2s ease;
|
||||
background: var(--color-brand);
|
||||
color: var(--color-neutral-900);
|
||||
}
|
||||
|
||||
.ui-button:hover:not(:disabled) {
|
||||
background: var(--color-brand-hover);
|
||||
}
|
||||
|
||||
.ui-button:disabled {
|
||||
opacity: 0.65;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.ui-button--ghost {
|
||||
background: var(--color-bg-card);
|
||||
border: 1px solid var(--color-border);
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.ui-button--ghost:hover:not(:disabled) {
|
||||
background: var(--color-surface-muted);
|
||||
}
|
||||
|
||||
.ui-button--danger {
|
||||
background: var(--color-danger-500);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.ui-button--danger:hover:not(:disabled) {
|
||||
background: #dc2626;
|
||||
}
|
||||
|
||||
.ui-pill,
|
||||
.ui-status-chip {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.35rem;
|
||||
padding: 0.45rem 0.7rem;
|
||||
border-radius: 999px;
|
||||
border: 1px solid var(--color-border);
|
||||
background: var(--color-surface-muted);
|
||||
color: var(--color-text-muted);
|
||||
font-size: 0.78rem;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.ui-status-chip--neutral {
|
||||
background: var(--color-surface-muted);
|
||||
}
|
||||
|
||||
.ui-status-chip--warning {
|
||||
background: var(--color-surface-warning);
|
||||
color: #854d0e;
|
||||
border-color: #f5d66d;
|
||||
}
|
||||
|
||||
.ui-status-chip--success {
|
||||
background: var(--color-surface-success);
|
||||
color: #166534;
|
||||
border-color: #bbf7d0;
|
||||
}
|
||||
|
||||
.ui-status-chip--danger {
|
||||
background: var(--color-surface-danger);
|
||||
color: #991b1b;
|
||||
border-color: #fecaca;
|
||||
}
|
||||
|
||||
.ui-split-workspace {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1.2fr) minmax(340px, 0.9fr);
|
||||
gap: var(--space-4);
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.ui-table-wrap {
|
||||
overflow: auto;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-md);
|
||||
background: var(--color-bg-card);
|
||||
max-height: 72vh;
|
||||
}
|
||||
|
||||
.ui-data-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
min-width: 760px;
|
||||
}
|
||||
|
||||
.ui-data-table thead {
|
||||
background: var(--color-surface-muted);
|
||||
}
|
||||
|
||||
.ui-data-table th,
|
||||
.ui-data-table td {
|
||||
text-align: left;
|
||||
padding: var(--space-3);
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
vertical-align: top;
|
||||
font-size: 0.93rem;
|
||||
}
|
||||
|
||||
.ui-data-table th {
|
||||
font-size: 0.78rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.03em;
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
.ui-data-table tbody tr {
|
||||
transition: background-color 0.15s ease;
|
||||
}
|
||||
|
||||
.ui-data-table tbody tr:hover {
|
||||
background: #fff9d9;
|
||||
}
|
||||
|
||||
.ui-detail-panel {
|
||||
background: var(--color-bg-card);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-md);
|
||||
padding: var(--space-4);
|
||||
min-height: 500px;
|
||||
}
|
||||
|
||||
.ui-detail-panel--empty {
|
||||
display: grid;
|
||||
align-content: center;
|
||||
justify-items: center;
|
||||
text-align: center;
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
.ui-meta-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
||||
gap: var(--space-2);
|
||||
}
|
||||
|
||||
.ui-meta-item {
|
||||
margin: 0;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-md);
|
||||
padding: var(--space-3);
|
||||
background: var(--color-surface-muted);
|
||||
display: grid;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.ui-meta-item strong,
|
||||
.ui-meta-item dt {
|
||||
margin: 0;
|
||||
font-size: 0.78rem;
|
||||
font-weight: 700;
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
.ui-meta-item span,
|
||||
.ui-meta-item dd {
|
||||
margin: 0;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.ui-code-pill {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 3px 8px;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: 7px;
|
||||
background: var(--color-surface-muted);
|
||||
color: var(--color-text);
|
||||
font-size: 0.82rem;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.ui-form-row {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.ui-form-row > * {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
.ui-two-column-layout,
|
||||
.ui-split-workspace {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.ui-form-row--three,
|
||||
.ui-choice-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.ui-section-header {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.ui-page-hero {
|
||||
padding: var(--space-6) 0;
|
||||
}
|
||||
}
|
||||
@@ -5,10 +5,17 @@
|
||||
/* Semantic Colors - Theming Layer */
|
||||
--color-bg: #faf9f6;
|
||||
--color-bg-card: #ffffff;
|
||||
--color-surface-card: var(--color-bg-card);
|
||||
--color-surface-muted: var(--color-neutral-100);
|
||||
--color-surface-success: var(--color-success-100);
|
||||
--color-surface-warning: var(--color-warning-100);
|
||||
--color-surface-danger: var(--color-danger-100);
|
||||
--color-text: var(--color-neutral-900);
|
||||
--color-text-main: var(--color-text);
|
||||
--color-text-muted: var(--color-secondary-500);
|
||||
|
||||
--color-brand: var(--color-primary-500);
|
||||
--color-brand-600: var(--color-primary-600);
|
||||
--color-brand-hover: var(--color-primary-600);
|
||||
|
||||
--color-border: var(--color-neutral-200);
|
||||
@@ -16,6 +23,7 @@
|
||||
--color-success: var(--color-success-500);
|
||||
--color-warning: var(--color-warning-500);
|
||||
--color-error: var(--color-danger-500);
|
||||
--focus-ring: 0 0 0 2px rgb(250 207 10 / 0.28);
|
||||
|
||||
/* Font */
|
||||
--font-family-sans: "IBM Plex Sans", "Space Grotesk", sans-serif;
|
||||
|
||||
@@ -9,13 +9,18 @@
|
||||
--color-secondary-600: #514d43;
|
||||
|
||||
--color-success-500: #22c55e;
|
||||
--color-success-100: #dcfce7;
|
||||
--color-warning-500: #eab308;
|
||||
--color-warning-100: #fef3c7;
|
||||
--color-danger-500: #ef4444;
|
||||
--color-danger-600: #dc2626;
|
||||
--color-danger-100: #fee2e2;
|
||||
|
||||
--color-neutral-50: #ffffff;
|
||||
--color-neutral-100: #efede7;
|
||||
--color-neutral-200: #dedad1;
|
||||
--color-neutral-300: #c6c1b6;
|
||||
--color-neutral-400: #a8a396;
|
||||
--color-neutral-800: #1f2933;
|
||||
--color-neutral-900: #101820;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user