+
@@ -13,6 +14,7 @@
} @else {
+
@if (isMedium()) {
diff --git a/src/app/core/components/root/root.component.ts b/src/app/core/components/root/root.component.ts
index c230d6860..064fde8b6 100644
--- a/src/app/core/components/root/root.component.ts
+++ b/src/app/core/components/root/root.component.ts
@@ -7,7 +7,7 @@ import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
import { toSignal } from '@angular/core/rxjs-interop';
import { RouterOutlet } from '@angular/router';
-import { SidenavComponent } from '@core/components';
+import { MaintenanceBannerComponent, SidenavComponent } from '@core/components';
import { BreadcrumbComponent } from '@core/components/breadcrumb/breadcrumb.component';
import { FooterComponent } from '@core/components/footer/footer.component';
import { HeaderComponent } from '@core/components/header/header.component';
@@ -25,6 +25,7 @@ import { IS_MEDIUM, IS_WEB } from '@osf/shared/helpers';
BreadcrumbComponent,
RouterOutlet,
SidenavComponent,
+ MaintenanceBannerComponent,
TranslatePipe,
],
templateUrl: './root.component.html',
diff --git a/src/app/core/models/maintenance.model.ts b/src/app/core/models/maintenance.model.ts
new file mode 100644
index 000000000..3fc82e1f1
--- /dev/null
+++ b/src/app/core/models/maintenance.model.ts
@@ -0,0 +1,9 @@
+export interface Maintenance {
+ level: number;
+ message: string;
+ start: string;
+ end: string;
+ severity?: MaintenanceSeverity;
+}
+
+export type MaintenanceSeverity = 'info' | 'warn' | 'error';
diff --git a/src/app/core/services/maintenance.service.ts b/src/app/core/services/maintenance.service.ts
new file mode 100644
index 000000000..d14e78248
--- /dev/null
+++ b/src/app/core/services/maintenance.service.ts
@@ -0,0 +1,39 @@
+import { catchError, Observable, of } from 'rxjs';
+import { map } from 'rxjs/operators';
+
+import { HttpClient } from '@angular/common/http';
+import { inject, Injectable } from '@angular/core';
+
+import { Maintenance, MaintenanceSeverity } from '../models/maintenance.model';
+
+import { environment } from 'src/environments/environment';
+
+@Injectable({ providedIn: 'root' })
+export class MaintenanceService {
+ private readonly http = inject(HttpClient);
+ private readonly apiUrl = `${environment.apiDomainUrl}/v2`;
+
+ fetchMaintenanceStatus(): Observable {
+ return this.http.get<{ maintenance?: Maintenance }>(`${this.apiUrl}/status/`).pipe(
+ map((data) => {
+ const maintenance = data.maintenance;
+ if (maintenance && this.isWithinMaintenanceWindow(maintenance)) {
+ return { ...maintenance, severity: this.getSeverity(maintenance.level) };
+ }
+ return null;
+ }),
+ catchError(() => of(null))
+ );
+ }
+
+ getSeverity(level: number): MaintenanceSeverity {
+ const map: Record = { 1: 'info', 2: 'warn', 3: 'error' };
+ return map[level] ?? 'info';
+ }
+
+ private isWithinMaintenanceWindow(maintenance: Maintenance): boolean {
+ if (!maintenance.start || !maintenance.end) return false;
+ const now = new Date();
+ return now >= new Date(maintenance.start) && now <= new Date(maintenance.end);
+ }
+}