diff --git a/src/app/components/calendar-year/calendar-year.scss b/src/app/components/calendar-year/calendar-year.scss
index baa9817..1ac06c6 100644
--- a/src/app/components/calendar-year/calendar-year.scss
+++ b/src/app/components/calendar-year/calendar-year.scss
@@ -1,40 +1,41 @@
-@use '@angular/material' as mat;
+:host {
+ display: block;
+ min-width: 0;
+}
.calendar-year {
- display: flex;
- flex-direction: row;
- flex-wrap: wrap;
- justify-content: space-evenly;
+ display: grid;
+ container: calendar-year / inline-size;
+ gap: 1.25rem;
width: 100%;
- max-width: 100%;
+ min-width: 0;
position: relative;
}
.loading-overlay {
position: absolute;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- background-color: rgba(255, 255, 255, 0.9);
+ inset: 0;
display: flex;
+ flex-wrap: wrap;
justify-content: center;
align-items: center;
- border-radius: 4px;
- z-index: 1000;
- background-color: var(--loading-overlay-bg);
+ min-height: 18rem;
+ border-radius: var(--ui-radius-lg, 1rem);
+ z-index: 1;
+ background: var(--loading-overlay-bg, rgba(255, 255, 255, 0.92));
}
.loading-spinner {
display: flex;
+ flex-wrap: wrap;
flex-direction: column;
align-items: center;
- gap: 16px;
+ gap: 1rem;
p {
- font-size: 16px;
+ font-size: 1rem;
font-weight: 500;
- color: var(--loading-text-color);
+ color: var(--loading-text-color, #333);
margin: 0;
}
}
@@ -43,24 +44,183 @@
position: relative;
display: inline-block;
- ::ng-deep .mat-mdc-progress-spinner {
- // Override Material spinner size
- width: 100px;
- height: 100px;
- }
-
.spinner-text {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
- font-size: 20px;
+ font-size: 1.125rem;
font-weight: bold;
- color: var(--loading-text-color);
+ color: var(--loading-text-color, #333);
pointer-events: none;
}
}
-.card {
+.results-header {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: flex-start;
+ justify-content: space-between;
+ gap: 1rem;
+ padding-block-end: 1rem;
+ border-block-end: 1px solid var(--ui-border, #c7c7d0);
+}
+
+.results-heading {
+ display: grid;
+ gap: 0.5rem;
+ min-width: 0;
+
+ h2 {
+ margin: 0;
+ font-size: 1.5rem;
+ font-weight: 700;
+ line-height: 1.2;
+ text-wrap: balance;
+ }
+}
+
+.planning-status {
+ display: inline-flex;
+ flex-wrap: wrap;
+ align-items: center;
+ gap: 0.5rem;
+ width: fit-content;
+ max-width: 65ch;
+ margin: 0;
+ padding: 0.5rem 0.75rem;
+ border-radius: var(--ui-radius-sm, 0.5rem);
+ font-weight: 600;
+ line-height: 1.4;
+
+ mat-icon {
+ flex-shrink: 0;
+ }
+
+ &.success {
+ background: var(--ui-success-bg, #d8f5df);
+ color: var(--ui-success-text, #174d26);
+ }
+
+ &.warning {
+ background: var(--ui-warning-bg, #fff4d6);
+ color: var(--ui-warning-text, #4f3800);
+ }
+
+ &.error {
+ background: var(--ui-error-bg, #ffe0e0);
+ color: var(--ui-error-text, #751b1b);
+ }
+}
+
+.plan-summary {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(min(100%, 11rem), 1fr));
+ gap: 0.75rem;
+ margin: 0;
+
+ div {
+ display: flex;
+ flex-wrap: wrap;
+ flex-direction: column;
+ gap: 0.25rem;
+ min-width: 0;
+ padding: 0.875rem 1rem;
+ background: var(--ui-panel-bg, #f7f7fa);
+ border-radius: var(--ui-radius-md, 0.75rem);
+ }
+
+ dt {
+ color: var(--ui-text-secondary, #45464f);
+ font-size: 0.875rem;
+ line-height: 1.4;
+ }
+
+ dd {
+ order: -1;
+ margin: 0;
+ font-size: 1.5rem;
+ font-weight: 700;
+ font-variant-numeric: tabular-nums;
+ line-height: 1.1;
+ }
+}
+
+.calendar-legend {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ gap: 0.75rem 1rem;
+ padding: 0.75rem 1rem;
+ background: var(--ui-panel-bg, #f7f7fa);
+ border-radius: var(--ui-radius-md, 0.75rem);
+
+ strong {
+ font-size: 0.875rem;
+ }
+
+ ul {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ gap: 0.5rem 1rem;
+ margin: 0;
+ padding: 0;
+ list-style: none;
+ }
+
+ li {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.375rem;
+ color: var(--ui-text-secondary, #45464f);
+ font-size: 0.875rem;
+ }
+}
+
+.legend-swatch {
+ display: inline-block;
+ flex-shrink: 0;
+ width: 1rem;
+ height: 1rem;
+ box-sizing: border-box;
+ border-radius: 0.25rem;
+ background: var(--ui-card-bg, #f1f1f5);
+
+ &.weekend {
+ background: var(--weekend-bg, #ffca39);
+ }
+
+ &.closed-day {
+ border: 0.125rem solid var(--closed-day-bg, #0ee3ff);
+ }
+
+ &.cp {
+ background: var(--cp-bg, #eca0ff);
+ }
+
+ &.rtt {
+ background: var(--rtt-bg, #99ff87);
+ }
+
+ &.other {
+ background: var(--other-bg, #ff9b9b);
+ }
+
+ &.today {
+ border: 0.125rem dashed var(--today-border-color, #1976d2);
+ }
+}
+
+.month-grid {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+ justify-content: space-evenly;
+ width: 100%;
+ max-width: 100%;
+}
+
+.month-card {
margin: 16px;
}
diff --git a/src/app/components/calendar-year/calendar-year.ts b/src/app/components/calendar-year/calendar-year.ts
index bb6b332..e41abdc 100644
--- a/src/app/components/calendar-year/calendar-year.ts
+++ b/src/app/components/calendar-year/calendar-year.ts
@@ -1,4 +1,4 @@
-import { Component, effect, signal, inject, untracked } from '@angular/core';
+import { Component, computed, effect, signal, inject, untracked } from '@angular/core';
import { UserInputService } from '../../services/user-input-service';
import { MatCard, MatCardContent } from '@angular/material/card';
import { CalendarMonth } from '../calendar-month/calendar-month';
@@ -9,10 +9,19 @@ import { RuleService } from '../../services/rule-service';
import { TranslationService } from '../../services/translation-service';
import { PlanOutcome, PlanningOutcome } from '../../services/leave-planner.service';
import { SelectableDayType, VacDay } from '../../services/calendar-service';
+import { MatIcon } from '@angular/material/icon';
+import { CalendarSettings } from '../calendar-settings/calendar-settings';
@Component({
selector: 'app-calendar-year',
- imports: [MatCard, CalendarMonth, MatCardContent, MatProgressSpinner],
+ imports: [
+ MatCard,
+ CalendarMonth,
+ MatCardContent,
+ MatProgressSpinner,
+ MatIcon,
+ CalendarSettings,
+ ],
templateUrl: './calendar-year.html',
styleUrl: './calendar-year.scss',
})
@@ -31,6 +40,14 @@ export class CalendarYear {
protected loadingProgress = signal(0);
protected planningOutcome = signal
(null);
+ protected displayedPlan = computed(() => {
+ const outcome = this.planningOutcome();
+ return outcome?.kind === 'Plan'
+ ? outcome
+ : outcome?.kind === 'PlanningIncomplete'
+ ? outcome.incumbent
+ : undefined;
+ });
private calculationVersion = 0;
protected hasPlannerError(): boolean {
@@ -127,12 +144,7 @@ export class CalendarYear {
private applyOutcome(outcome: PlanningOutcome): void {
this.planningOutcome.set(outcome);
- const plan =
- outcome.kind === 'Plan'
- ? outcome
- : outcome.kind === 'PlanningIncomplete'
- ? outcome.incumbent
- : undefined;
+ const plan = this.displayedPlan();
if (plan) {
this.selectedDates = this.calendarService.selectedDatesForPlan(plan);
this.userInput.remainingVacationDaysSignal.set(this.toLegacyBalances(plan));
diff --git a/src/app/components/dark-light-mode-button/dark-light-mode-button.scss b/src/app/components/dark-light-mode-button/dark-light-mode-button.scss
index 716ffbb..27e9da2 100644
--- a/src/app/components/dark-light-mode-button/dark-light-mode-button.scss
+++ b/src/app/components/dark-light-mode-button/dark-light-mode-button.scss
@@ -2,13 +2,18 @@ button {
margin: 0;
padding: 0;
display: flex;
+ flex-wrap: wrap;
align-items: center;
justify-content: center;
border: none;
border-radius: 50%;
- width: 40px;
- height: 40px;
- transition: all 0.3s ease;
+ width: 2.75rem;
+ height: 2.75rem;
+ min-width: 2.75rem;
+ min-height: 2.75rem;
+ transition:
+ background-color 200ms ease,
+ color 200ms ease;
&:hover {
background-color: rgba(0, 0, 0, 0);
diff --git a/src/app/components/rules-panel/rules-panel.html b/src/app/components/rules-panel/rules-panel.html
index 80db3fe..4161592 100644
--- a/src/app/components/rules-panel/rules-panel.html
+++ b/src/app/components/rules-panel/rules-panel.html
@@ -1,8 +1,9 @@
- rule
- {{ tr().rules.title }}
+ rule
+ {{ tr().rules.title }}
+ {{ activeRuleCount }} {{ tr().rules.active }}
{{ tr().rules.subtitle }}
@@ -37,6 +38,22 @@
>
+
+