Enhances Services module with detail view and interactions

Adds comprehensive service detail view with multiple tabs and dynamic interactions
Implements client-side navigation between service list and detail views
Introduces mock service data catalog for flexible component rendering
Extends localization support for new service detail screens

Improves user experience by adding edit capabilities and smooth view transitions
This commit is contained in:
Janus C. H. Knudsen 2026-01-16 22:03:22 +01:00
parent fad5e46dfb
commit 120367acbb
22 changed files with 1780 additions and 597 deletions

View file

@ -557,6 +557,10 @@ swp-section-label {
padding-bottom: var(--spacing-4);
border-bottom: 1px solid var(--color-border);
margin-bottom: var(--spacing-6);
&.spaced {
margin-top: var(--spacing-6);
}
}
/* Section header - wrapper when action link is needed */
@ -727,6 +731,281 @@ swp-form-input {
resize: vertical;
min-height: 80px;
}
&.date-range {
display: flex;
align-items: center;
gap: var(--spacing-3);
input {
flex: 1;
}
span {
color: var(--color-text-secondary);
}
}
}
/* ===========================================
DRAWER FORM PATTERNS
Scoped styles for forms inside drawers
=========================================== */
[data-drawer] {
/* Form row - vertical layout */
swp-form-row {
display: flex;
flex-direction: column;
gap: 6px;
margin-bottom: 16px;
&.spaced {
margin-top: 24px;
}
}
/* Form labels - uppercase style */
swp-form-label {
font-size: 11px;
font-weight: 400;
color: var(--color-text-secondary);
text-transform: uppercase;
letter-spacing: 0.5px;
.optional,
.auto {
font-weight: 400;
text-transform: none;
color: var(--color-text-muted);
}
}
/* Form value - read-only display */
swp-form-value {
font-size: 15px;
font-weight: 500;
color: var(--color-text);
}
/* Form divider */
swp-form-divider {
display: block;
height: 1px;
background: var(--color-border);
margin: 20px 0;
}
/* Form hint text */
swp-form-hint {
display: block;
font-size: 12px;
color: var(--color-text-muted);
margin: -8px 0 16px 0;
line-height: 1.4;
}
/* Form group - gray card background */
swp-form-group {
display: block;
padding: 16px;
background: var(--color-background-alt);
border-radius: 8px;
margin-top: 16px;
swp-form-row:last-child {
margin-bottom: 0;
}
}
/* Form select wrapper */
swp-form-select {
display: block;
select {
width: 100%;
padding: 10px 12px;
border: 1px solid var(--color-border);
border-radius: 6px;
font-size: 14px;
font-family: var(--font-family);
color: var(--color-text);
background: var(--color-surface);
cursor: pointer;
&:focus {
outline: none;
border-color: var(--color-teal);
}
}
}
/* Text inputs */
input[type="text"],
input[type="date"] {
width: 100%;
padding: 10px 12px;
border: 1px solid var(--color-border);
border-radius: 6px;
font-size: 14px;
font-family: var(--font-family);
color: var(--color-text);
background: var(--color-surface);
&::placeholder {
color: var(--color-text-muted);
}
&:focus {
outline: none;
border-color: var(--color-teal);
}
}
/* Textarea */
textarea {
width: 100%;
padding: 10px 12px;
border: 1px solid var(--color-border);
border-radius: 6px;
font-size: 14px;
font-family: var(--font-family);
color: var(--color-text);
background: var(--color-surface);
resize: vertical;
&::placeholder {
color: var(--color-text-muted);
}
&:focus {
outline: none;
border-color: var(--color-teal);
}
}
/* Date range inputs */
swp-date-range {
display: flex;
align-items: center;
gap: 12px;
input {
flex: 1;
&.inactive {
opacity: 0.5;
background: var(--color-background-alt);
&:focus {
opacity: 1;
background: var(--color-surface);
}
}
}
span {
color: var(--color-text-secondary);
}
}
/* Drawer header with background */
swp-drawer-header {
background: var(--color-background-alt);
padding: 20px 24px;
}
swp-drawer-title {
font-size: 18px;
}
/* Drawer body padding */
swp-drawer-body {
padding: 24px;
}
/* Drawer footer with background */
swp-drawer-footer {
display: flex;
gap: 12px;
padding: 20px 24px;
border-top: 1px solid var(--color-border);
background: var(--color-background-alt);
swp-btn {
flex: 1;
}
}
}
/* ===========================================
DRAWER DATA ROWS (checkbox + label + input)
For rate-style drawer content
=========================================== */
[data-drawer] swp-data-table {
display: grid;
grid-template-columns: 28px 1fr 100px;
}
[data-drawer] swp-data-row {
display: grid;
grid-column: 1 / -1;
grid-template-columns: subgrid;
align-items: center;
gap: 12px;
padding: 12px 0;
border-bottom: 1px solid var(--color-border);
&:last-child {
border-bottom: none;
}
input[type="checkbox"] {
width: 18px;
height: 18px;
accent-color: var(--color-teal);
}
}
[data-drawer] swp-data-label {
font-size: var(--font-size-base);
&.disabled {
opacity: 0.4;
}
}
[data-drawer] swp-data-input {
display: flex;
align-items: center;
justify-self: end;
gap: 4px;
font-size: var(--font-size-sm);
color: var(--color-text-secondary);
input {
width: 100px;
padding: 6px 8px;
border: 1px solid var(--color-border);
border-radius: var(--radius-sm);
font-size: var(--font-size-sm);
font-family: var(--font-mono);
text-align: right;
}
&.disabled input {
opacity: 0.4;
background: var(--color-background);
}
}
[data-drawer] swp-section-label {
margin-bottom: 12px;
}
[data-drawer] swp-data-section {
margin-top: 24px;
padding-top: 16px;
border-top: 1px solid var(--color-border);
}
/* ===========================================
@ -751,3 +1030,271 @@ swp-empty-state {
color: var(--color-text-secondary);
}
}
/* ===========================================
TAGS (Generic)
=========================================== */
swp-tags-row {
display: flex;
align-items: center;
gap: var(--spacing-2);
flex-wrap: wrap;
}
swp-tag {
display: inline-flex;
align-items: center;
gap: var(--spacing-1);
padding: var(--spacing-1) var(--spacing-3);
font-size: var(--font-size-xs);
font-weight: var(--font-weight-semibold);
text-transform: uppercase;
letter-spacing: 0.3px;
border-radius: var(--radius-sm);
background: var(--color-background);
color: var(--color-text-secondary);
&.master {
background: var(--bg-purple-strong);
color: var(--color-purple);
}
&.senior {
background: var(--bg-blue-strong);
color: var(--color-blue);
}
&.junior {
background: var(--bg-amber-strong);
color: #b45309;
}
&.cert {
background: var(--bg-teal-strong);
color: var(--color-teal);
}
&.popular {
background: var(--bg-amber-strong);
color: #b45309;
}
&.combo {
background: var(--bg-teal-strong);
color: var(--color-teal);
}
&.color {
background: var(--bg-purple-strong);
color: var(--color-purple);
}
}
/* ===========================================
STATUS INDICATOR (Generic)
=========================================== */
swp-status-indicator {
display: inline-flex;
align-items: center;
gap: var(--spacing-2);
padding: var(--spacing-2) var(--spacing-4);
font-size: var(--font-size-sm);
font-weight: var(--font-weight-medium);
border-radius: var(--radius-md);
cursor: pointer;
transition: all var(--transition-fast);
margin-left: auto;
&[data-active="true"] {
background: var(--bg-green-strong);
color: var(--color-green);
border: 1px solid var(--bg-green-border);
}
&[data-active="false"] {
background: var(--bg-red-medium);
color: var(--color-red);
border: 1px solid var(--bg-red-border);
}
.icon {
font-size: var(--font-size-base);
}
}
/* ===========================================
FACT BOXES (Inline)
=========================================== */
swp-fact-boxes-inline {
display: flex;
gap: var(--spacing-12);
margin-top: var(--spacing-1);
flex-wrap: wrap;
}
swp-fact-inline {
display: flex;
align-items: baseline;
gap: var(--spacing-2);
swp-fact-inline-value {
font-size: var(--font-size-lg);
font-weight: var(--font-weight-semibold);
font-family: var(--font-mono);
color: var(--color-text);
}
swp-fact-inline-label {
font-size: var(--font-size-sm);
color: var(--color-text-secondary);
}
}
/* ===========================================
EDIT SECTION (Grid + Subgrid)
=========================================== */
swp-edit-section {
display: grid;
grid-template-columns: 140px 1fr;
gap: var(--spacing-4);
}
swp-edit-row {
display: grid;
grid-column: 1 / -1;
grid-template-columns: subgrid;
align-items: center;
input {
font-size: var(--font-size-base);
padding: var(--spacing-4) var(--spacing-5);
border-radius: var(--radius-sm);
background: var(--color-background-alt);
border: 1px solid var(--color-border);
color: var(--color-text);
transition: all var(--transition-fast);
cursor: text;
&:hover {
background: var(--color-background);
}
&:focus {
outline: none;
background: var(--color-surface);
border-color: var(--color-teal);
}
&[data-type="number"] {
font-family: var(--font-mono);
text-align: right;
width: 150px;
justify-self: end;
}
}
}
swp-edit-label {
font-size: var(--font-size-md);
color: var(--color-text-secondary);
}
swp-edit-value {
font-size: var(--font-size-base);
color: var(--color-text);
padding: var(--spacing-4) var(--spacing-5);
border-radius: var(--radius-sm);
background: var(--color-background-alt);
border: 1px solid transparent;
transition: all var(--transition-fast);
cursor: text;
&:hover {
background: var(--color-background);
}
&:focus {
outline: none;
background: var(--color-surface);
border-color: var(--color-teal);
}
&.mono {
font-family: var(--font-mono);
width: 150px;
text-align: right;
justify-self: end;
}
}
swp-edit-select {
display: block;
select {
width: 100%;
padding: var(--spacing-2) var(--spacing-4);
font-size: var(--font-size-base);
border: 1px solid var(--color-border);
border-radius: var(--radius-sm);
background: var(--color-surface);
cursor: pointer;
&:focus {
outline: none;
border-color: var(--color-teal);
}
}
}
/* ===========================================
VIEW TRANSITIONS (List/Detail swap)
=========================================== */
.view-fade-out {
opacity: 0;
}
.view-fade-in {
opacity: 1;
}
/* ===========================================
BACK LINK
=========================================== */
swp-back-link {
display: inline-flex;
align-items: center;
gap: var(--spacing-2);
color: var(--color-text-secondary);
font-size: var(--font-size-sm);
cursor: pointer;
transition: color var(--transition-fast);
&:hover {
color: var(--color-teal);
}
i {
font-size: 16px;
}
}
/* ===========================================
DETAIL GRID (2-column layout)
=========================================== */
swp-detail-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: var(--spacing-8);
> div {
display: flex;
flex-direction: column;
gap: var(--spacing-8);
}
}
@media (max-width: 900px) {
swp-detail-grid {
grid-template-columns: 1fr;
}
}