SPA zbudowane w React 19 + Vite 8 z pełnym zestawem funkcjonalności: autentykacja z 2FA, kreator rezerwacji, panel admina, analityka, GraphQL (Apollo Client + SchemaLink), React Query, Storybook, testy jednostkowe (Vitest + RTL) i e2e (Playwright).
163 lines
3.3 KiB
SCSS
163 lines
3.3 KiB
SCSS
@use '../styles/variables' as *;
|
|
|
|
.analytics {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: $spacing-6;
|
|
|
|
&__toolbar {
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
}
|
|
|
|
&__export-btn {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: $spacing-2;
|
|
padding: $spacing-2 $spacing-4;
|
|
background: transparent;
|
|
border: 1px solid var(--clr-border);
|
|
border-radius: $radius-base;
|
|
font-size: $font-size-sm;
|
|
font-weight: $font-weight-medium;
|
|
font-family: $font-family-base;
|
|
color: var(--clr-text-secondary);
|
|
cursor: pointer;
|
|
transition: background $transition-fast;
|
|
|
|
&:hover { background: var(--clr-surface-raised); color: var(--clr-text); }
|
|
}
|
|
|
|
&__cards {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
|
|
gap: $spacing-4;
|
|
}
|
|
|
|
&__charts {
|
|
display: grid;
|
|
grid-template-columns: 1fr 1fr;
|
|
gap: $spacing-4;
|
|
|
|
@media (max-width: 800px) { grid-template-columns: 1fr; }
|
|
}
|
|
|
|
&__chart-box {
|
|
background: var(--clr-surface-raised, #{$gray-50});
|
|
border: 1px solid var(--clr-border);
|
|
border-radius: $radius-lg;
|
|
padding: $spacing-5;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: $spacing-4;
|
|
}
|
|
|
|
&__chart-title {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: $spacing-2;
|
|
font-size: $font-size-sm;
|
|
font-weight: $font-weight-semibold;
|
|
color: var(--clr-text);
|
|
margin: 0;
|
|
}
|
|
|
|
&__empty {
|
|
font-size: $font-size-sm;
|
|
color: var(--clr-text-secondary);
|
|
font-style: italic;
|
|
}
|
|
}
|
|
|
|
// Stat card
|
|
.stat {
|
|
background: var(--clr-surface);
|
|
border: 1px solid var(--clr-border);
|
|
border-radius: $radius-lg;
|
|
padding: $spacing-4;
|
|
display: flex;
|
|
align-items: flex-start;
|
|
gap: $spacing-3;
|
|
|
|
&__icon {
|
|
width: 40px;
|
|
height: 40px;
|
|
border-radius: $radius-base;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
flex-shrink: 0;
|
|
background: $gray-100;
|
|
color: $gray-600;
|
|
}
|
|
|
|
&--blue &__icon { background: $primary-100; color: $primary-600; }
|
|
&--green &__icon { background: $accent-100; color: $accent-600; }
|
|
&--yellow &__icon { background: #fef9c3; color: #ca8a04; }
|
|
&--red &__icon { background: #fee2e2; color: #dc2626; }
|
|
|
|
&__body { min-width: 0; }
|
|
|
|
&__value {
|
|
font-size: $font-size-xl;
|
|
font-weight: $font-weight-bold;
|
|
color: var(--clr-text);
|
|
margin: 0;
|
|
line-height: 1.2;
|
|
}
|
|
|
|
&__label {
|
|
font-size: $font-size-xs;
|
|
color: var(--clr-text-secondary);
|
|
margin: 2px 0 0;
|
|
}
|
|
|
|
&__sub {
|
|
font-size: 11px;
|
|
color: var(--clr-text-muted);
|
|
margin: 2px 0 0;
|
|
}
|
|
}
|
|
|
|
// Bar chart
|
|
.bar-chart {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: $spacing-2;
|
|
|
|
&__row {
|
|
display: grid;
|
|
grid-template-columns: 100px 1fr 36px;
|
|
align-items: center;
|
|
gap: $spacing-2;
|
|
}
|
|
|
|
&__label {
|
|
font-size: 11px;
|
|
color: var(--clr-text-secondary);
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
&__track {
|
|
height: 10px;
|
|
background: var(--clr-border);
|
|
border-radius: $radius-full;
|
|
overflow: hidden;
|
|
}
|
|
|
|
&__fill {
|
|
height: 100%;
|
|
border-radius: $radius-full;
|
|
transition: width 0.5s ease;
|
|
}
|
|
|
|
&__val {
|
|
font-size: 11px;
|
|
font-weight: $font-weight-semibold;
|
|
color: var(--clr-text);
|
|
text-align: right;
|
|
}
|
|
}
|