Compare commits

...

63 Commits

Author SHA1 Message Date
Nawaz Dhandala
b198dc0ec8 refactor: Enhance lazy loading of components in StatusPage for improved type safety and maintainability 2026-02-14 19:54:05 +00:00
Nawaz Dhandala
b0a3f8d60f refactor: Enable tree shaking in esbuild configuration for optimized bundle size 2026-02-14 19:42:53 +00:00
Nawaz Dhandala
83a13635cf Refactor route components to improve readability and consistency in prop spreading 2026-02-14 18:38:45 +00:00
Nawaz Dhandala
a2c1744e8c refactor: Enhance mermaid plugin to serve pre-bundled CJS file with ESM export for improved compatibility 2026-02-14 15:00:28 +00:00
Nawaz Dhandala
4271ddbdcb refactor: Consolidate lazy loading of components into AllPages for improved organization and performance 2026-02-14 14:52:55 +00:00
Nawaz Dhandala
6ffb081a02 refactor: Consolidate route imports into AllRoutes for improved organization and maintainability 2026-02-14 14:51:01 +00:00
Nawaz Dhandala
16a9edbfcd Refactor route imports in StatusPagesRoutes, TracesRoutes, UserSettingsRoutes, and WorkflowRoutes for improved readability and performance by replacing lazy loading with direct imports. Remove unnecessary Suspense components around routes. 2026-02-14 11:00:15 +00:00
Nawaz Dhandala
b8cd3ce1c1 Merge branch 'release' of https://github.com/OneUptime/oneuptime into release 2026-02-13 20:57:59 +00:00
Nawaz Dhandala
b86aee7f2a refactor: Update comments for channel name normalization and sanitization in Slack and Teams utilities 2026-02-13 20:57:25 +00:00
Nawaz Dhandala
2cde167445 refactor: Enhance channel name normalization to remove invalid characters for Microsoft Teams 2026-02-13 20:56:22 +00:00
Nawaz Dhandala
9bd6b011fe refactor: Sanitize Slack channel names to remove invalid characters 2026-02-13 20:52:02 +00:00
Nawaz Dhandala
538eef5660 chore: Bump version to 9.5.10 2026-02-13 19:27:29 +00:00
Simon Larsen
e1a343ae38 Merge pull request #2300 from OneUptime/master
Release
2026-02-13 19:27:00 +00:00
Nawaz Dhandala
8b42af35c1 refactor: Remove unused noteModalVisible prop from NotesSection component 2026-02-13 17:12:49 +00:00
Nawaz Dhandala
fc9026a8d8 refactor: Clean up component code and improve formatting across multiple files 2026-02-13 17:11:00 +00:00
Nawaz Dhandala
86edee35c1 style: Update color scheme and improve UI consistency across components 2026-02-13 17:09:12 +00:00
Nawaz Dhandala
109c276bc5 feat: Update GlassCard component to support opaque prop and apply it to multiple cards 2026-02-13 16:55:15 +00:00
Nawaz Dhandala
8040dd0f56 Refactor UI components and enhance styling
- Removed unused theme variable from IncidentsScreen.
- Updated SettingsScreen to use GlassCard and LinearGradient for improved UI.
- Refactored LoginScreen and ServerUrlScreen to utilize GradientHeader and GlassCard for consistent styling.
- Introduced GradientButton component for reusable gradient-styled buttons.
- Added Logo component for consistent branding across screens.
- Created NotesSection component to manage and display internal notes.
- Implemented SectionHeader component for better organization of section titles.
- Enhanced color theme with new gradient and accent colors.
2026-02-13 16:45:48 +00:00
Nawaz Dhandala
00d4148b6b Enhance UI and UX across multiple screens
- Updated BiometricLockScreen with improved icon layout and shadow effects.
- Refined HomeScreen layout for better spacing and added header gradient.
- Enhanced IncidentDetailScreen with gradient backgrounds and improved badge styling.
- Replaced error handling in IncidentsScreen with a reusable EmptyState component.
- Improved SettingsScreen with a profile header and added icons to settings rows.
- Revamped LoginScreen and ServerUrlScreen with gradient headers and refined input fields.
- Updated color theme to include new gradient properties for better visual consistency.
2026-02-13 16:14:15 +00:00
Nawaz Dhandala
dec03bc3a8 feat: Add expo-font dependency to package.json and package-lock.json 2026-02-13 15:45:30 +00:00
Nawaz Dhandala
46a9f95fc0 fix: Update error handling in getUserMiddleware to use NotAuthenticatedException 2026-02-13 15:42:10 +00:00
Simon Larsen
8b2f9bc778 Merge pull request #2299 from OneUptime/dna-monitor
DNS monitor
2026-02-13 14:04:51 +00:00
Nawaz Dhandala
fcc6223850 refactor: Remove unused variables and improve type annotations in hooks and screens 2026-02-13 14:04:23 +00:00
Nawaz Dhandala
c9bc214e86 fix: Correct CAA record resolution method and typo in value formatting 2026-02-13 13:57:31 +00:00
Simon Larsen
2897a937ba Merge pull request #2298 from OneUptime/refactor-mob-app
Refactor mob app
2026-02-13 13:49:05 +00:00
Nawaz Dhandala
f3cd7be143 refactor: Clean up code formatting and improve readability across multiple components 2026-02-13 13:48:46 +00:00
Nawaz Dhandala
f324a4e864 feat: Implement DNS monitoring configuration and secret handling 2026-02-13 13:42:45 +00:00
Nawaz Dhandala
f6a8cef649 feat: Add DNS monitoring capabilities
- Introduced new DNS monitor types and criteria checks in CriteriaFilter.ts.
- Implemented DNS monitor instance creation in MonitorCriteriaInstance.ts.
- Enhanced MonitorStep to support DNS configurations.
- Added DNS response handling in ProbeMonitorResponse.ts.
- Created DNS monitor forms and summary views in the Dashboard.
- Developed DNS query utilities and response handling in MonitorTypes/DnsMonitor.ts.
- Added DNS record types and response structures for better data handling.
- Implemented criteria evaluation for DNS monitoring in DnsMonitorCriteria.ts.
2026-02-13 13:29:01 +00:00
Nawaz Dhandala
770ef007a4 refactor: Enhance response normalization in API client to handle serialized types 2026-02-13 12:10:42 +00:00
Nawaz Dhandala
dafa0cc5d9 refactor: Update styling in AppContent and RootNavigator for consistent theme integration 2026-02-13 11:48:31 +00:00
Nawaz Dhandala
196e9cae10 refactor: Enhance error handling in getBlogPost method to return null on failure 2026-02-13 11:33:02 +00:00
Nawaz Dhandala
d0d26d20b2 refactor: Integrate SplashScreen to hide native splash screen after loading completes 2026-02-13 11:25:13 +00:00
Nawaz Dhandala
6a90ee97bf Merge branch 'master' of https://github.com/OneUptime/oneuptime 2026-02-13 11:12:22 +00:00
Nawaz Dhandala
f2a2644b0e Merge branch 'release' 2026-02-13 11:12:12 +00:00
Nawaz Dhandala
5cb2ac8c8b fix: Use absolute path for npm in ts-node installation to ensure correct execution 2026-02-13 11:10:34 +00:00
Nawaz Dhandala
6751d59b2f refactor: Remove totalActive calculation and active issues summary from HomeScreen 2026-02-13 11:05:09 +00:00
Nawaz Dhandala
aefc649743 refactor: Integrate useAuth in ProjectProvider for authentication handling 2026-02-13 10:25:57 +00:00
Nawaz Dhandala
cfba73665c refactor: Update loading state to use 'isPending' instead of 'isLoading' in multiple hooks 2026-02-13 10:21:06 +00:00
Nawaz Dhandala
049c5d003c refactor: Add flex styling to SectionList in AlertsScreen and IncidentsScreen for better layout 2026-02-13 10:15:17 +00:00
Simon Larsen
fd8998952d Merge pull request #2297 from OneUptime/chore/npm-audit-fix
chore: npm audit fix
2026-02-13 10:06:30 +00:00
Nawaz Dhandala
5ca85e4915 refactor: Update header comment in fetchProjects function for clarity on multi-tenant request 2026-02-13 09:58:09 +00:00
Nawaz Dhandala
aa401291b6 refactor: Update SettingsRow component to consolidate version display in SettingsScreen 2026-02-13 09:01:30 +00:00
Nawaz Dhandala
bf3d90871d refactor: Add feed fetching functions and integrate FeedTimeline component in detail screens 2026-02-13 09:00:20 +00:00
Nawaz Dhandala
4a4dff9264 refactor: Add 'muted' prop to AlertCard, EpisodeCard, and IncidentCard; implement opacity adjustment based on prop value 2026-02-13 08:51:36 +00:00
Nawaz Dhandala
fd3f75e4e2 refactor: Consolidate API fetching logic for alerts, incidents, and episodes; streamline hooks to use unified fetching methods 2026-02-13 08:46:59 +00:00
Nawaz Dhandala
43fc5acdda Refactor HomeScreen to consolidate project counts using useAllProjectCounts hook; remove unused hooks and simplify loading logic
Remove ProjectSelectionScreen as it is no longer needed; integrate project selection into other screens

Update IncidentDetailScreen and IncidentEpisodeDetailScreen to directly receive projectId from route params

Refactor IncidentsScreen to utilize useAllProjectIncidents, useAllProjectIncidentEpisodes, and useAllProjectIncidentStates hooks for better data management

Add new hooks: useAllProjectCounts, useAllProjectAlerts, useAllProjectAlertEpisodes, useAllProjectAlertStates, useAllProjectIncidentEpisodes, and useAllProjectIncidentStates for improved data fetching and state management

Remove useProject hook usage from SettingsScreen and adjust project-related logic accordingly
2026-02-13 08:39:24 +00:00
simlarsen
c7ca6138f3 chore: npm audit fix 2026-02-13 02:34:23 +00:00
Nawaz Dhandala
87475b00c4 Enhance UI components and improve accessibility across multiple screens
- Added SectionHeader component for consistent section titles in IncidentEpisodeDetailScreen.
- Updated styles for various text elements to improve readability and accessibility.
- Refined button styles in LoginScreen and ServerUrlScreen for better visual feedback.
- Introduced focus states for input fields in LoginScreen and ServerUrlScreen.
- Enhanced ProjectSelectionScreen with improved project item display.
- Implemented SectionCard component in SettingsScreen for better layout organization.
- Updated color tokens in theme for better visual consistency and added new accent colors.
- Adjusted Tailwind CSS configuration for improved shadow effects and new color variables.
2026-02-12 22:36:25 +00:00
Nawaz Dhandala
d5613cc4bd refactor: Integrate Ionicons for improved iconography in EmptyState, BiometricLockScreen, HomeScreen, and SettingsScreen 2026-02-12 22:19:24 +00:00
Nawaz Dhandala
b1c9d9a645 refactor: Add Ionicons for tab bar icons in MainTabNavigator 2026-02-12 22:17:33 +00:00
Nawaz Dhandala
01c6101ae9 Refactor navigation and screens for incidents and alerts
- Removed AlertEpisodesStackNavigator and IncidentEpisodesStackNavigator.
- Integrated AlertEpisodeDetailScreen and IncidentEpisodeDetailScreen into AlertsStackNavigator and IncidentsStackNavigator respectively.
- Updated MainTabNavigator to remove references to the deleted stack navigators.
- Adjusted RootNavigator linking to reflect the new navigation structure.
- Modified types to remove unused stack parameter lists.
- Updated handlers for notifications to navigate to the correct screens.
- Refactored AlertsScreen and IncidentsScreen to support segmented control for toggling between alerts and episodes.
- Added SegmentedControl component for better UI navigation.
- Cleaned up unused screen components and hooks related to episodes.
2026-02-12 22:14:55 +00:00
Nawaz Dhandala
ec56609bf4 refactor: Update biometric labels for clarity in SettingsScreen 2026-02-12 22:07:44 +00:00
Nawaz Dhandala
e5f652a950 refactor: Remove Notification Preferences screen and related navigation; update settings stack and types accordingly 2026-02-12 22:06:36 +00:00
Nawaz Dhandala
749bd2e41d refactor: Replace react-native-keychain with AsyncStorage for token management 2026-02-12 22:03:50 +00:00
Nawaz Dhandala
cc23416ad8 refactor: Move SafeAreaProvider to wrap the PersistQueryClientProvider for improved layout handling 2026-02-12 21:56:58 +00:00
Nawaz Dhandala
86fda9ba16 refactor: Update Tailwind CSS configuration and clean up imports; remove unused tailwind.config.ts file 2026-02-12 21:54:17 +00:00
Nawaz Dhandala
969983043b feat: Add babel-preset-expo to dependencies for improved Babel configuration 2026-02-12 21:39:30 +00:00
Nawaz Dhandala
2b64dd0b1d Refactor Settings, Login, and Server URL screens to use Tailwind CSS for styling; remove unused styles and theme properties; integrate NativeWind for utility-first styling approach; update theme context to support dark mode; enhance accessibility and maintainability. 2026-02-12 21:31:38 +00:00
Nawaz Dhandala
3a514969dc Refactor UI components to enhance styling and shadows
- Updated background colors for various components to use `backgroundElevated` instead of `backgroundSecondary`.
- Increased padding and border radius for cards and buttons across multiple screens for a more modern look.
- Introduced shadow styles for components to improve depth perception.
- Adjusted header styles in navigators to use `backgroundPrimary` and added subtle borders.
- Added a new `shadows` utility to manage shadow styles consistently across the app.
- Modified text styles for better readability and visual hierarchy.
- Updated spacing constants to include larger values for improved layout.
2026-02-12 21:02:46 +00:00
Nawaz Dhandala
10d006890c Refactor code structure for improved readability and maintainability 2026-02-12 20:46:28 +00:00
Nawaz Dhandala
2cb719d53a feat: Add check to skip localhost instances in OpenSourceDeploymentAPI 2026-02-12 20:34:30 +00:00
Nawaz Dhandala
eafb543371 Merge branch 'master' of https://github.com/OneUptime/oneuptime 2026-02-12 19:35:57 +00:00
Nawaz Dhandala
5800fe4f7a feat: Implement aggressive disk cleanup in release workflow to optimize build space 2026-02-12 18:55:47 +00:00
144 changed files with 9959 additions and 11066 deletions

View File

@@ -1913,12 +1913,51 @@ jobs:
env:
CI_PIPELINE_ID: ${{github.run_number}}
steps:
# Docker compose needs a lot of space to build images, so we need to free up some space first in the GitHub Actions runner
# Aggressively free disk space before anything else
- name: Aggressive Disk Cleanup
run: |
echo "=== Disk space BEFORE cleanup ==="
df -h /
# Remove pre-installed software not needed for this job
sudo rm -rf /usr/share/dotnet || true
sudo rm -rf /usr/local/lib/android || true
sudo rm -rf /opt/ghc || true
sudo rm -rf /opt/hostedtoolcache || true
sudo rm -rf /usr/local/share/boost || true
sudo rm -rf /usr/local/graalvm/ || true
sudo rm -rf /usr/local/share/powershell || true
sudo rm -rf /usr/local/share/chromium || true
sudo rm -rf /usr/local/lib/node_modules || true
sudo rm -rf /usr/share/swift || true
sudo rm -rf /usr/share/miniconda || true
sudo rm -rf /usr/lib/google-cloud-sdk || true
sudo rm -rf /usr/lib/jvm || true
sudo rm -rf /usr/lib/firefox || true
sudo rm -rf /usr/lib/heroku || true
sudo rm -rf /usr/local/julia* || true
sudo rm -rf /opt/az || true
sudo rm -rf /opt/microsoft || true
sudo rm -rf /opt/pipx || true
sudo rm -rf /opt/actionarchivecache || true
sudo rm -rf /imagegeneration || true
sudo rm -rf /usr/share/az_* || true
sudo rm -rf /usr/share/sbt || true
sudo rm -rf /usr/share/gradle* || true
sudo rm -rf /usr/share/kotlinc || true
sudo rm -rf /usr/share/ri || true
sudo rm -rf /usr/local/.ghcup || true
# Clean apt cache
sudo apt-get clean || true
sudo rm -rf /var/lib/apt/lists/* || true
# Clean temp files
sudo rm -rf /tmp/* || true
# Docker cleanup
docker system prune -af --volumes || true
echo "=== Disk space AFTER aggressive cleanup ==="
df -h /
- name: Free Disk Space (Ubuntu)
uses: jlumbroso/free-disk-space@main
with:
# this might remove tools that are actually needed,
# if set to "true" but frees about 6 GB
tool-cache: true
android: true
dotnet: true
@@ -1926,6 +1965,10 @@ jobs:
large-packages: true
docker-images: true
swap-storage: true
- name: Final Disk Space Check
run: |
echo "=== Disk space after all cleanup ==="
df -h /
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
@@ -2001,12 +2044,51 @@ jobs:
env:
CI_PIPELINE_ID: ${{github.run_number}}
steps:
# Docker compose needs a lot of space to build images, so we need to free up some space first in the GitHub Actions runner
# Aggressively free disk space before anything else
- name: Aggressive Disk Cleanup
run: |
echo "=== Disk space BEFORE cleanup ==="
df -h /
# Remove pre-installed software not needed for this job
sudo rm -rf /usr/share/dotnet || true
sudo rm -rf /usr/local/lib/android || true
sudo rm -rf /opt/ghc || true
sudo rm -rf /opt/hostedtoolcache || true
sudo rm -rf /usr/local/share/boost || true
sudo rm -rf /usr/local/graalvm/ || true
sudo rm -rf /usr/local/share/powershell || true
sudo rm -rf /usr/local/share/chromium || true
sudo rm -rf /usr/local/lib/node_modules || true
sudo rm -rf /usr/share/swift || true
sudo rm -rf /usr/share/miniconda || true
sudo rm -rf /usr/lib/google-cloud-sdk || true
sudo rm -rf /usr/lib/jvm || true
sudo rm -rf /usr/lib/firefox || true
sudo rm -rf /usr/lib/heroku || true
sudo rm -rf /usr/local/julia* || true
sudo rm -rf /opt/az || true
sudo rm -rf /opt/microsoft || true
sudo rm -rf /opt/pipx || true
sudo rm -rf /opt/actionarchivecache || true
sudo rm -rf /imagegeneration || true
sudo rm -rf /usr/share/az_* || true
sudo rm -rf /usr/share/sbt || true
sudo rm -rf /usr/share/gradle* || true
sudo rm -rf /usr/share/kotlinc || true
sudo rm -rf /usr/share/ri || true
sudo rm -rf /usr/local/.ghcup || true
# Clean apt cache
sudo apt-get clean || true
sudo rm -rf /var/lib/apt/lists/* || true
# Clean temp files
sudo rm -rf /tmp/* || true
# Docker cleanup
docker system prune -af --volumes || true
echo "=== Disk space AFTER aggressive cleanup ==="
df -h /
- name: Free Disk Space (Ubuntu)
uses: jlumbroso/free-disk-space@main
with:
# this might remove tools that are actually needed,
# if set to "true" but frees about 6 GB
tool-cache: true
android: true
dotnet: true
@@ -2014,6 +2096,10 @@ jobs:
large-packages: true
docker-images: true
swap-storage: true
- name: Final Disk Space Check
run: |
echo "=== Disk space after all cleanup ==="
df -h /
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:

View File

@@ -521,6 +521,19 @@ const dataTypeDetails: Dictionary<DataTypePageData> = {
},
],
},
{
name: "dnsMonitor",
type: "MonitorStepDnsMonitor",
required: false,
description:
"Configuration for DNS monitoring. Required for DNS monitor type. Defines query name (domain), record type, optional DNS server, port, timeout, and retry settings. See MonitorStepDnsMonitor.",
typeLinks: [
{
label: "MonitorStepDnsMonitor",
path: "monitor-step-dns-monitor",
},
],
},
],
values: [],
jsonExample: JSON.stringify(
@@ -2560,6 +2573,31 @@ const dataTypeDetails: Dictionary<DataTypePageData> = {
description:
"Whether the SNMP device is reachable. Use with 'True' or 'False'. Applies to: SNMP monitors.",
},
{
value: "DNS Response Time (in ms)",
description:
"The DNS query response time in milliseconds. Use with numeric FilterTypes. Applies to: DNS monitors.",
},
{
value: "DNS Is Online",
description:
"Whether the DNS resolution succeeded. Use with 'True' or 'False'. Applies to: DNS monitors.",
},
{
value: "DNS Record Value",
description:
"The value of a DNS record returned by the query. Use with string FilterTypes (Contains, EqualTo, etc.). Applies to: DNS monitors.",
},
{
value: "DNSSEC Is Valid",
description:
"Whether DNSSEC validation passed (AD flag present). Use with 'True' or 'False'. Applies to: DNS monitors.",
},
{
value: "DNS Record Exists",
description:
"Whether any DNS records were returned for the query. Use with 'True' or 'False'. Applies to: DNS monitors.",
},
{
value: "JavaScript Expression",
description:
@@ -3112,6 +3150,93 @@ const dataTypeDetails: Dictionary<DataTypePageData> = {
2,
),
},
"monitor-step-dns-monitor": {
title: "MonitorStepDnsMonitor",
description:
"Configuration for a DNS monitor step. Defines the domain to query, record type, optional custom DNS server, and timeout settings. Used as the 'dnsMonitor' property on a MonitorStep when the monitor type is 'DNS'. The criteria filters can then use 'DNS Is Online', 'DNS Response Time (in ms)', 'DNS Record Value', 'DNSSEC Is Valid', and 'DNS Record Exists' as CheckOn values.",
isEnum: false,
relatedTypes: [
{
name: "MonitorStep",
path: "monitor-step",
relationship: "Parent that holds this as dnsMonitor property",
},
{
name: "CheckOn",
path: "check-on",
relationship: "Use DNS-specific CheckOn values with DNS monitors",
},
],
properties: [
{
name: "queryName",
type: "string",
required: true,
description: "The domain name to query (e.g., 'example.com').",
},
{
name: "recordType",
type: "string (enum)",
required: true,
description:
"The DNS record type to query. Possible values: 'A', 'AAAA', 'CNAME', 'MX', 'NS', 'TXT', 'SOA', 'PTR', 'SRV', 'CAA'.",
},
{
name: "hostname",
type: "string",
required: false,
description:
"Custom DNS server to use for the query (e.g., '8.8.8.8'). Leave empty to use system default DNS resolver.",
},
{
name: "port",
type: "number",
required: false,
description: "DNS port. Default is 53.",
},
{
name: "timeout",
type: "number",
required: false,
description:
"Timeout for DNS queries in milliseconds. Default is 5000 (5 seconds).",
},
{
name: "retries",
type: "number",
required: false,
description: "Number of retries for failed DNS queries. Default is 3.",
},
],
values: [],
jsonExample: JSON.stringify(
{
"// Example 1: Basic A record lookup": {
queryName: "example.com",
recordType: "A",
timeout: 5000,
retries: 3,
},
"// Example 2: MX record with custom DNS server": {
queryName: "example.com",
recordType: "MX",
hostname: "8.8.8.8",
port: 53,
timeout: 5000,
retries: 3,
},
"// Example 3: TXT record for SPF verification": {
queryName: "example.com",
recordType: "TXT",
hostname: "1.1.1.1",
timeout: 5000,
retries: 3,
},
},
null,
2,
),
},
};
export default class ServiceHandler {

6
App/package-lock.json generated
View File

@@ -4192,9 +4192,9 @@
"dev": true
},
"node_modules/qs": {
"version": "6.14.1",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz",
"integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==",
"version": "6.14.2",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz",
"integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==",
"license": "BSD-3-Clause",
"dependencies": {
"side-channel": "^1.1.0"

View File

@@ -39,6 +39,14 @@ export default class OpenSourceDeploymentAPI extends BaseAPI<
(body["oneuptimeVersion"] as string) || "unknown";
deployment.instanceUrl = (body["instanceUrl"] as string) || "";
// Skip localhost instances - these are default/unconfigured deployments.
if (
deployment.instanceUrl === "http://localhost/" ||
deployment.instanceUrl === "http://localhost"
) {
return Response.sendEmptySuccessResponse(req, res);
}
await OpenSourceDeploymentService.create({
data: deployment,
props: {

View File

@@ -177,10 +177,15 @@ export default class UserMiddleware {
try {
oneuptimeRequest.userAuthorization = JSONWebToken.decode(accessToken);
} catch (err) {
// if the token is invalid or expired, it'll throw this error.
// if the token is invalid or expired, return 401 so clients can refresh the token.
logger.error(err);
oneuptimeRequest.userType = UserType.Public;
return next();
return Response.sendErrorResponse(
req,
res,
new NotAuthenticatedException(
"AccessToken is invalid or expired. Please refresh your token.",
),
);
}
if (oneuptimeRequest.userAuthorization.isMasterAdmin) {

View File

@@ -127,6 +127,14 @@ export class Service extends DatabaseService<Model> {
monitorDestination = `${monitorDestination}:${port}`;
}
}
// For DNS monitors, use the queryName from dnsMonitor config
if (monitorType === MonitorType.DNS && firstStep?.data?.dnsMonitor) {
monitorDestination = firstStep.data.dnsMonitor.queryName || "";
if (firstStep.data.dnsMonitor.hostname) {
monitorDestination = `${monitorDestination} @${firstStep.data.dnsMonitor.hostname}`;
}
}
}
}

View File

@@ -1805,9 +1805,18 @@ export class Service extends DatabaseService<WorkspaceNotificationRule> {
logger.debug("New channel template name:");
logger.debug(notificationChannels);
/*
* Sanitize the suffix for workspace channel names.
* When no custom prefix is set, the default "#" prefix (e.g. "#42") produces
* invalid Slack channel names (e.g. "oneuptime-alert-#42"). Strip characters
* that are not valid in Slack/Teams channel names.
*/
const sanitizedSuffix: string = data.channelNameSiffix
.toLowerCase()
.replace(/[^a-z0-9\-_]/g, "");
// add suffix and then check if it is already added or not.
const channelName: string =
notificationChannels + data.channelNameSiffix;
const channelName: string = notificationChannels + sanitizedSuffix;
logger.debug("Final channel name with suffix:");
logger.debug(channelName);

View File

@@ -0,0 +1,183 @@
import DataToProcess from "../DataToProcess";
import CompareCriteria from "./CompareCriteria";
import {
CheckOn,
CriteriaFilter,
FilterType,
} from "../../../../Types/Monitor/CriteriaFilter";
import DnsMonitorResponse from "../../../../Types/Monitor/DnsMonitor/DnsMonitorResponse";
import ProbeMonitorResponse from "../../../../Types/Probe/ProbeMonitorResponse";
import EvaluateOverTime from "./EvaluateOverTime";
import CaptureSpan from "../../Telemetry/CaptureSpan";
import logger from "../../Logger";
export default class DnsMonitorCriteria {
@CaptureSpan()
public static async isMonitorInstanceCriteriaFilterMet(input: {
dataToProcess: DataToProcess;
criteriaFilter: CriteriaFilter;
}): Promise<string | null> {
let threshold: number | string | undefined | null =
input.criteriaFilter.value;
const dataToProcess: ProbeMonitorResponse =
input.dataToProcess as ProbeMonitorResponse;
const dnsResponse: DnsMonitorResponse | undefined =
dataToProcess.dnsResponse;
let overTimeValue: Array<number | boolean> | number | boolean | undefined =
undefined;
if (
input.criteriaFilter.evaluateOverTime &&
input.criteriaFilter.evaluateOverTimeOptions
) {
try {
overTimeValue = await EvaluateOverTime.getValueOverTime({
projectId: (input.dataToProcess as ProbeMonitorResponse).projectId,
monitorId: input.dataToProcess.monitorId!,
evaluateOverTimeOptions: input.criteriaFilter.evaluateOverTimeOptions,
metricType: input.criteriaFilter.checkOn,
});
if (Array.isArray(overTimeValue) && overTimeValue.length === 0) {
overTimeValue = undefined;
}
} catch (err) {
logger.error(
`Error in getting over time value for ${input.criteriaFilter.checkOn}`,
);
logger.error(err);
overTimeValue = undefined;
}
}
// Check if DNS is online
if (input.criteriaFilter.checkOn === CheckOn.DnsIsOnline) {
const currentIsOnline: boolean | Array<boolean> =
(overTimeValue as Array<boolean>) ||
(input.dataToProcess as ProbeMonitorResponse).isOnline;
return CompareCriteria.compareCriteriaBoolean({
value: currentIsOnline,
criteriaFilter: input.criteriaFilter,
});
}
// Check DNS response time
if (input.criteriaFilter.checkOn === CheckOn.DnsResponseTime) {
threshold = CompareCriteria.convertToNumber(threshold);
if (threshold === null || threshold === undefined) {
return null;
}
const currentResponseTime: number | Array<number> =
(overTimeValue as Array<number>) ||
dnsResponse?.responseTimeInMs ||
(input.dataToProcess as ProbeMonitorResponse).responseTimeInMs;
if (currentResponseTime === null || currentResponseTime === undefined) {
return null;
}
return CompareCriteria.compareCriteriaNumbers({
value: currentResponseTime,
threshold: threshold as number,
criteriaFilter: input.criteriaFilter,
});
}
// Check if DNS record exists
if (input.criteriaFilter.checkOn === CheckOn.DnsRecordExists) {
const exists: boolean = Boolean(
dnsResponse?.records && dnsResponse.records.length > 0,
);
const isTrue: boolean =
input.criteriaFilter.filterType === FilterType.True;
const isFalse: boolean =
input.criteriaFilter.filterType === FilterType.False;
if (exists && isTrue) {
return `DNS records exist for the query.`;
}
if (!exists && isFalse) {
return `No DNS records found for the query.`;
}
return null;
}
// Check DNSSEC validity
if (input.criteriaFilter.checkOn === CheckOn.DnssecIsValid) {
const isTrue: boolean =
input.criteriaFilter.filterType === FilterType.True;
const isFalse: boolean =
input.criteriaFilter.filterType === FilterType.False;
if (dnsResponse?.isDnssecValid === undefined) {
return null;
}
if (dnsResponse.isDnssecValid && isTrue) {
return `DNSSEC is valid.`;
}
if (!dnsResponse.isDnssecValid && isFalse) {
return `DNSSEC is not valid.`;
}
return null;
}
// Check DNS record value
if (input.criteriaFilter.checkOn === CheckOn.DnsRecordValue) {
if (!dnsResponse?.records || dnsResponse.records.length === 0) {
return null;
}
// Check if any record value matches the criteria
for (const record of dnsResponse.records) {
const recordValue: string = record.value;
// Try numeric comparison first
if (
typeof threshold === "number" ||
(typeof threshold === "string" && !isNaN(Number(threshold)))
) {
const numericThreshold: number | null =
CompareCriteria.convertToNumber(threshold);
if (numericThreshold !== null && !isNaN(Number(recordValue))) {
const result: string | null =
CompareCriteria.compareCriteriaNumbers({
value: Number(recordValue),
threshold: numericThreshold,
criteriaFilter: input.criteriaFilter,
});
if (result) {
return `DNS record (${record.type}): ${result}`;
}
}
}
// String comparison
const result: string | null = CompareCriteria.compareCriteriaStrings({
value: recordValue,
threshold: String(threshold),
criteriaFilter: input.criteriaFilter,
});
if (result) {
return `DNS record (${record.type}): ${result}`;
}
}
}
return null;
}
}

View File

@@ -12,6 +12,7 @@ import MetricMonitorCriteria from "./Criteria/MetricMonitorCriteria";
import TraceMonitorCriteria from "./Criteria/TraceMonitorCriteria";
import ExceptionMonitorCriteria from "./Criteria/ExceptionMonitorCriteria";
import SnmpMonitorCriteria from "./Criteria/SnmpMonitorCriteria";
import DnsMonitorCriteria from "./Criteria/DnsMonitorCriteria";
import MonitorCriteriaMessageBuilder from "./MonitorCriteriaMessageBuilder";
import MonitorCriteriaDataExtractor from "./MonitorCriteriaDataExtractor";
import MonitorCriteriaMessageFormatter from "./MonitorCriteriaMessageFormatter";
@@ -493,6 +494,18 @@ ${contextBlock}
}
}
if (input.monitor.monitorType === MonitorType.DNS) {
const dnsMonitorResult: string | null =
await DnsMonitorCriteria.isMonitorInstanceCriteriaFilterMet({
dataToProcess: input.dataToProcess,
criteriaFilter: input.criteriaFilter,
});
if (dnsMonitorResult) {
return dnsMonitorResult;
}
}
return null;
}

View File

@@ -14,6 +14,9 @@ import SyntheticMonitorResponse from "../../../Types/Monitor/SyntheticMonitors/S
import SnmpMonitorResponse, {
SnmpOidResponse,
} from "../../../Types/Monitor/SnmpMonitor/SnmpMonitorResponse";
import DnsMonitorResponse, {
DnsRecordResponse,
} from "../../../Types/Monitor/DnsMonitor/DnsMonitorResponse";
import Typeof from "../../../Types/Typeof";
import VMUtil from "../VM/VMAPI";
import DataToProcess from "./DataToProcess";
@@ -240,6 +243,40 @@ export default class MonitorTemplateUtil {
}
}
}
if (data.monitorType === MonitorType.DNS) {
const dnsResponse: DnsMonitorResponse | undefined = (
data.dataToProcess as ProbeMonitorResponse
).dnsResponse;
storageMap = {
isOnline: (data.dataToProcess as ProbeMonitorResponse).isOnline,
responseTimeInMs: dnsResponse?.responseTimeInMs,
failureCause: dnsResponse?.failureCause,
isTimeout: dnsResponse?.isTimeout,
isDnssecValid: dnsResponse?.isDnssecValid,
} as JSONObject;
// Add DNS records
if (dnsResponse?.records) {
storageMap["records"] = dnsResponse.records.map(
(record: DnsRecordResponse) => {
return {
type: record.type,
value: record.value,
ttl: record.ttl,
};
},
);
// Add record values as a flat array for easier templating
storageMap["recordValues"] = dnsResponse.records.map(
(record: DnsRecordResponse) => {
return record.value;
},
);
}
}
} catch (err) {
logger.error(err);
}

View File

@@ -780,13 +780,14 @@ export default class MicrosoftTeamsUtil extends WorkspaceBase {
const workspaceChannels: Array<WorkspaceChannel> = [];
for (let channelName of data.channelNames) {
// Normalize channel name - Teams has different naming requirements
if (channelName && channelName.startsWith("#")) {
channelName = channelName.substring(1);
}
// Teams channels cannot have spaces in the name for some operations
const normalizedChannelName: string = channelName.replace(/\s+/g, "-");
for (const channelName of data.channelNames) {
/*
* Normalize channel name: replace spaces with hyphens, then strip
* characters not valid in Teams channel names (e.g. #, %, &, *, etc.).
*/
const normalizedChannelName: string = channelName
.replace(/\s+/g, "-")
.replace(/[^a-zA-Z0-9\-_]/g, "");
// Check if channel exists
const existingChannel: WorkspaceChannel | null =
@@ -839,6 +840,9 @@ export default class MicrosoftTeamsUtil extends WorkspaceBase {
}): Promise<WorkspaceChannel> {
const teamId: string = data.teamId;
// Sanitize channel name: strip characters not valid in Teams channel names.
data.channelName = data.channelName.replace(/[^a-zA-Z0-9\-_\s]/g, "");
// Get valid access token
const accessToken: string = await this.getValidAccessToken({
authToken: data.authToken,

View File

@@ -442,12 +442,14 @@ export default class SlackUtil extends WorkspaceBase {
const workspaceChannels: Array<WorkspaceChannel> = [];
for (let channelName of data.channelNames) {
// Normalize channel name
if (channelName && channelName.startsWith("#")) {
channelName = channelName.substring(1);
}
channelName = channelName.toLowerCase();
channelName = channelName.replace(/\s+/g, "-");
/*
* Normalize channel name: replace spaces with hyphens, then strip
* any characters not valid in Slack channel names.
*/
channelName = channelName
.toLowerCase()
.replace(/\s+/g, "-")
.replace(/[^a-z0-9\-_]/g, "");
// Check if channel exists using optimized method
const existingChannel: WorkspaceChannel | null =
@@ -1345,12 +1347,13 @@ export default class SlackUtil extends WorkspaceBase {
channelName: string;
projectId: ObjectID;
}): Promise<WorkspaceChannel> {
if (data.channelName && data.channelName.startsWith("#")) {
data.channelName = data.channelName.substring(1);
}
// lower case channel name
data.channelName = data.channelName.toLowerCase();
/*
* Sanitize channel name: Slack only allows lowercase letters, numbers,
* hyphens, and underscores. Remove all other characters (including #).
*/
data.channelName = data.channelName
.toLowerCase()
.replace(/[^a-z0-9\-_]/g, "");
logger.debug("Creating channel with data:");
logger.debug(data);

View File

@@ -14,6 +14,7 @@ import Response from "../../../Server/Utils/Response";
import Dictionary from "../../../Types/Dictionary";
import Email from "../../../Types/Email";
import BadDataException from "../../../Types/Exception/BadDataException";
import NotAuthenticatedException from "../../../Types/Exception/NotAuthenticatedException";
import SsoAuthorizationException from "../../../Types/Exception/SsoAuthorizationException";
import HashedString from "../../../Types/HashedString";
import JSONFunctions from "../../../Types/JSONFunctions";
@@ -330,7 +331,7 @@ describe("UserMiddleware", () => {
expect(JSONWebToken.decode).not.toHaveBeenCalled();
});
test("should call function 'next' and return, when accessToken can not be decoded", async () => {
test("should call Response.sendErrorResponse with NotAuthenticatedException, when accessToken can not be decoded", async () => {
const error: Error = new Error("Invalid access token");
const spyJWTDecode: jest.SpyInstance = getJestSpyOn(
@@ -342,7 +343,14 @@ describe("UserMiddleware", () => {
await UserMiddleware.getUserMiddleware(req, res, next);
expect(next).toHaveBeenCalled();
expect(Response.sendErrorResponse).toHaveBeenCalledWith(
req,
res,
new NotAuthenticatedException(
"AccessToken is invalid or expired. Please refresh your token.",
),
);
expect(next).not.toHaveBeenCalled();
expect(spyJWTDecode).toHaveBeenCalledWith(mockedAccessToken);
expect(UserService.updateOneBy).not.toHaveBeenCalled();
});

View File

@@ -60,6 +60,13 @@ export enum CheckOn {
SnmpOidExists = "SNMP OID Exists",
SnmpResponseTime = "SNMP Response Time (in ms)",
SnmpIsOnline = "SNMP Device Is Online",
// DNS monitors.
DnsResponseTime = "DNS Response Time (in ms)",
DnsIsOnline = "DNS Is Online",
DnsRecordValue = "DNS Record Value",
DnssecIsValid = "DNSSEC Is Valid",
DnsRecordExists = "DNS Record Exists",
}
export interface ServerMonitorOptions {
@@ -141,7 +148,11 @@ export class CriteriaFilterUtil {
}): boolean {
const { checkOn } = data;
if (checkOn === CheckOn.IsOnline || checkOn === CheckOn.SnmpIsOnline) {
if (
checkOn === CheckOn.IsOnline ||
checkOn === CheckOn.SnmpIsOnline ||
checkOn === CheckOn.DnsIsOnline
) {
return false;
}
@@ -149,7 +160,11 @@ export class CriteriaFilterUtil {
return false;
}
if (checkOn === CheckOn.SnmpOidExists) {
if (
checkOn === CheckOn.SnmpOidExists ||
checkOn === CheckOn.DnssecIsValid ||
checkOn === CheckOn.DnsRecordExists
) {
return false;
}
@@ -204,7 +219,9 @@ export class CriteriaFilterUtil {
checkOn === CheckOn.MemoryUsagePercent ||
checkOn === CheckOn.IsOnline ||
checkOn === CheckOn.SnmpResponseTime ||
checkOn === CheckOn.SnmpIsOnline
checkOn === CheckOn.SnmpIsOnline ||
checkOn === CheckOn.DnsResponseTime ||
checkOn === CheckOn.DnsIsOnline
);
}
}

View File

@@ -0,0 +1,16 @@
import DnsRecordType from "./DnsRecordType";
export interface DnsRecordResponse {
type: DnsRecordType;
value: string;
ttl?: number | undefined;
}
export default interface DnsMonitorResponse {
isOnline: boolean;
responseTimeInMs: number;
failureCause: string;
records: Array<DnsRecordResponse>;
isDnssecValid?: boolean | undefined;
isTimeout?: boolean | undefined;
}

View File

@@ -0,0 +1,14 @@
enum DnsRecordType {
A = "A",
AAAA = "AAAA",
CNAME = "CNAME",
MX = "MX",
NS = "NS",
TXT = "TXT",
SOA = "SOA",
PTR = "PTR",
SRV = "SRV",
CAA = "CAA",
}
export default DnsRecordType;

View File

@@ -394,6 +394,33 @@ export default class MonitorCriteriaInstance extends DatabaseProperty {
return monitorCriteriaInstance;
}
if (arg.monitorType === MonitorType.DNS) {
const monitorCriteriaInstance: MonitorCriteriaInstance =
new MonitorCriteriaInstance();
monitorCriteriaInstance.data = {
id: ObjectID.generate().toString(),
monitorStatusId: arg.monitorStatusId,
filterCondition: FilterCondition.All,
filters: [
{
checkOn: CheckOn.DnsIsOnline,
filterType: FilterType.True,
value: undefined,
},
],
incidents: [],
alerts: [],
createAlerts: false,
changeMonitorStatus: true,
createIncidents: false,
name: `Check if ${arg.monitorName} is online`,
description: `This criteria checks if the ${arg.monitorName} DNS resolution is online`,
};
return monitorCriteriaInstance;
}
return null;
}
@@ -495,6 +522,46 @@ export default class MonitorCriteriaInstance extends DatabaseProperty {
};
}
if (arg.monitorType === MonitorType.DNS) {
monitorCriteriaInstance.data = {
id: ObjectID.generate().toString(),
monitorStatusId: arg.monitorStatusId,
filterCondition: FilterCondition.Any,
filters: [
{
checkOn: CheckOn.DnsIsOnline,
filterType: FilterType.False,
value: undefined,
},
],
incidents: [
{
title: `${arg.monitorName} is offline`,
description: `${arg.monitorName} DNS resolution is currently failing.`,
incidentSeverityId: arg.incidentSeverityId,
autoResolveIncident: true,
id: ObjectID.generate().toString(),
onCallPolicyIds: [],
},
],
changeMonitorStatus: true,
createIncidents: true,
createAlerts: false,
alerts: [
{
title: `${arg.monitorName} is offline`,
description: `${arg.monitorName} DNS resolution is currently failing.`,
alertSeverityId: arg.alertSeverityId,
autoResolveAlert: true,
id: ObjectID.generate().toString(),
onCallPolicyIds: [],
},
],
name: `Check if ${arg.monitorName} is offline`,
description: `This criteria checks if the ${arg.monitorName} DNS resolution is failing`,
};
}
if (
arg.monitorType === MonitorType.API ||
arg.monitorType === MonitorType.Website

View File

@@ -29,6 +29,9 @@ import MonitorStepExceptionMonitor, {
import MonitorStepSnmpMonitor, {
MonitorStepSnmpMonitorUtil,
} from "./MonitorStepSnmpMonitor";
import MonitorStepDnsMonitor, {
MonitorStepDnsMonitorUtil,
} from "./MonitorStepDnsMonitor";
import Zod, { ZodSchema } from "../../Utils/Schema/Zod";
export interface MonitorStepType {
@@ -72,6 +75,9 @@ export interface MonitorStepType {
// SNMP monitor
snmpMonitor?: MonitorStepSnmpMonitor | undefined;
// DNS monitor
dnsMonitor?: MonitorStepDnsMonitor | undefined;
}
export default class MonitorStep extends DatabaseProperty {
@@ -98,6 +104,7 @@ export default class MonitorStep extends DatabaseProperty {
metricMonitor: undefined,
exceptionMonitor: undefined,
snmpMonitor: undefined,
dnsMonitor: undefined,
};
}
@@ -129,6 +136,7 @@ export default class MonitorStep extends DatabaseProperty {
metricMonitor: undefined,
exceptionMonitor: undefined,
snmpMonitor: undefined,
dnsMonitor: undefined,
};
return monitorStep;
@@ -224,6 +232,11 @@ export default class MonitorStep extends DatabaseProperty {
return this;
}
public setDnsMonitor(dnsMonitor: MonitorStepDnsMonitor): MonitorStep {
this.data!.dnsMonitor = dnsMonitor;
return this;
}
public setCustomCode(customCode: string): MonitorStep {
this.data!.customCode = customCode;
return this;
@@ -332,6 +345,16 @@ export default class MonitorStep extends DatabaseProperty {
}
}
if (monitorType === MonitorType.DNS) {
if (!value.data.dnsMonitor) {
return "DNS configuration is required";
}
if (!value.data.dnsMonitor.queryName) {
return "DNS query name (domain) is required";
}
}
return null;
}
@@ -377,6 +400,9 @@ export default class MonitorStep extends DatabaseProperty {
snmpMonitor: this.data.snmpMonitor
? MonitorStepSnmpMonitorUtil.toJSON(this.data.snmpMonitor)
: undefined,
dnsMonitor: this.data.dnsMonitor
? MonitorStepDnsMonitorUtil.toJSON(this.data.dnsMonitor)
: undefined,
},
});
}
@@ -482,6 +508,9 @@ export default class MonitorStep extends DatabaseProperty {
snmpMonitor: json["snmpMonitor"]
? (json["snmpMonitor"] as JSONObject)
: undefined,
dnsMonitor: json["dnsMonitor"]
? (json["dnsMonitor"] as JSONObject)
: undefined,
}) as any;
return monitorStep;
@@ -507,6 +536,7 @@ export default class MonitorStep extends DatabaseProperty {
traceMonitor: Zod.any().optional(),
metricMonitor: Zod.any().optional(),
snmpMonitor: Zod.any().optional(),
dnsMonitor: Zod.any().optional(),
}).openapi({
type: "object",
example: {

View File

@@ -0,0 +1,46 @@
import { JSONObject } from "../JSON";
import DnsRecordType from "./DnsMonitor/DnsRecordType";
export default interface MonitorStepDnsMonitor {
queryName: string;
recordType: DnsRecordType;
hostname?: string | undefined; // DNS server (e.g. "8.8.8.8"), empty = system default
port: number;
timeout: number;
retries: number;
}
export class MonitorStepDnsMonitorUtil {
public static getDefault(): MonitorStepDnsMonitor {
return {
queryName: "",
recordType: DnsRecordType.A,
hostname: "",
port: 53,
timeout: 5000,
retries: 3,
};
}
public static fromJSON(json: JSONObject): MonitorStepDnsMonitor {
return {
queryName: (json["queryName"] as string) || "",
recordType: (json["recordType"] as DnsRecordType) || DnsRecordType.A,
hostname: (json["hostname"] as string) || undefined,
port: (json["port"] as number) || 53,
timeout: (json["timeout"] as number) || 5000,
retries: (json["retries"] as number) || 3,
};
}
public static toJSON(monitor: MonitorStepDnsMonitor): JSONObject {
return {
queryName: monitor.queryName,
recordType: monitor.recordType,
hostname: monitor.hostname,
port: monitor.port,
timeout: monitor.timeout,
retries: monitor.retries,
};
}
}

View File

@@ -26,6 +26,9 @@ enum MonitorType {
// Network device monitoring
SNMP = "SNMP",
// DNS monitoring
DNS = "DNS",
}
export default MonitorType;
@@ -179,6 +182,13 @@ export class MonitorTypeHelper {
"This monitor type lets you monitor network devices like switches, routers, and firewalls via SNMP.",
icon: IconProp.Signal,
},
{
monitorType: MonitorType.DNS,
title: "DNS",
description:
"This monitor type lets you monitor DNS resolution for your domains, verify record values, and check DNSSEC validity.",
icon: IconProp.GlobeAlt,
},
];
return monitorTypeProps;
@@ -224,7 +234,8 @@ export class MonitorTypeHelper {
monitorType === MonitorType.SSLCertificate ||
monitorType === MonitorType.SyntheticMonitor ||
monitorType === MonitorType.CustomJavaScriptCode ||
monitorType === MonitorType.SNMP;
monitorType === MonitorType.SNMP ||
monitorType === MonitorType.DNS;
return isProbeableMonitor;
}
@@ -246,6 +257,7 @@ export class MonitorTypeHelper {
MonitorType.Traces,
MonitorType.Exceptions,
MonitorType.SNMP,
MonitorType.DNS,
];
}
@@ -278,7 +290,8 @@ export class MonitorTypeHelper {
monitorType === MonitorType.SSLCertificate ||
monitorType === MonitorType.SyntheticMonitor ||
monitorType === MonitorType.CustomJavaScriptCode ||
monitorType === MonitorType.SNMP
monitorType === MonitorType.SNMP ||
monitorType === MonitorType.DNS
) {
return true;
}

View File

@@ -7,6 +7,7 @@ import CustomCodeMonitorResponse from "../Monitor/CustomCodeMonitor/CustomCodeMo
import SslMonitorResponse from "../Monitor/SSLMonitor/SslMonitorResponse";
import SyntheticMonitorResponse from "../Monitor/SyntheticMonitors/SyntheticMonitorResponse";
import SnmpMonitorResponse from "../Monitor/SnmpMonitor/SnmpMonitorResponse";
import DnsMonitorResponse from "../Monitor/DnsMonitor/DnsMonitorResponse";
import MonitorEvaluationSummary from "../Monitor/MonitorEvaluationSummary";
import ObjectID from "../ObjectID";
import Port from "../Port";
@@ -30,6 +31,7 @@ export default interface ProbeMonitorResponse {
syntheticMonitorResponse?: Array<SyntheticMonitorResponse> | undefined;
customCodeMonitorResponse?: CustomCodeMonitorResponse | undefined;
snmpResponse?: SnmpMonitorResponse | undefined;
dnsResponse?: DnsMonitorResponse | undefined;
monitoredAt: Date;
isTimeout?: boolean | undefined;
ingestedAt?: Date | undefined;

View File

@@ -8,8 +8,63 @@ import React, {
import ReactMarkdown from "react-markdown";
// https://github.com/remarkjs/remark-gfm
import remarkGfm from "remark-gfm";
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import SyntaxHighlighter from "react-syntax-highlighter/dist/esm/prism-light";
import { a11yDark } from "react-syntax-highlighter/dist/esm/styles/prism";
import javascript from "react-syntax-highlighter/dist/esm/languages/prism/javascript";
import typescript from "react-syntax-highlighter/dist/esm/languages/prism/typescript";
import jsx from "react-syntax-highlighter/dist/esm/languages/prism/jsx";
import tsx from "react-syntax-highlighter/dist/esm/languages/prism/tsx";
import python from "react-syntax-highlighter/dist/esm/languages/prism/python";
import bash from "react-syntax-highlighter/dist/esm/languages/prism/bash";
import json from "react-syntax-highlighter/dist/esm/languages/prism/json";
import yaml from "react-syntax-highlighter/dist/esm/languages/prism/yaml";
import sql from "react-syntax-highlighter/dist/esm/languages/prism/sql";
import go from "react-syntax-highlighter/dist/esm/languages/prism/go";
import java from "react-syntax-highlighter/dist/esm/languages/prism/java";
import css from "react-syntax-highlighter/dist/esm/languages/prism/css";
import markup from "react-syntax-highlighter/dist/esm/languages/prism/markup";
import markdown from "react-syntax-highlighter/dist/esm/languages/prism/markdown";
import docker from "react-syntax-highlighter/dist/esm/languages/prism/docker";
import rust from "react-syntax-highlighter/dist/esm/languages/prism/rust";
import c from "react-syntax-highlighter/dist/esm/languages/prism/c";
import cpp from "react-syntax-highlighter/dist/esm/languages/prism/cpp";
import csharp from "react-syntax-highlighter/dist/esm/languages/prism/csharp";
import ruby from "react-syntax-highlighter/dist/esm/languages/prism/ruby";
import php from "react-syntax-highlighter/dist/esm/languages/prism/php";
import graphql from "react-syntax-highlighter/dist/esm/languages/prism/graphql";
import http from "react-syntax-highlighter/dist/esm/languages/prism/http";
SyntaxHighlighter.registerLanguage("javascript", javascript);
SyntaxHighlighter.registerLanguage("js", javascript);
SyntaxHighlighter.registerLanguage("typescript", typescript);
SyntaxHighlighter.registerLanguage("ts", typescript);
SyntaxHighlighter.registerLanguage("jsx", jsx);
SyntaxHighlighter.registerLanguage("tsx", tsx);
SyntaxHighlighter.registerLanguage("python", python);
SyntaxHighlighter.registerLanguage("bash", bash);
SyntaxHighlighter.registerLanguage("shell", bash);
SyntaxHighlighter.registerLanguage("json", json);
SyntaxHighlighter.registerLanguage("yaml", yaml);
SyntaxHighlighter.registerLanguage("yml", yaml);
SyntaxHighlighter.registerLanguage("sql", sql);
SyntaxHighlighter.registerLanguage("go", go);
SyntaxHighlighter.registerLanguage("java", java);
SyntaxHighlighter.registerLanguage("css", css);
SyntaxHighlighter.registerLanguage("markup", markup);
SyntaxHighlighter.registerLanguage("html", markup);
SyntaxHighlighter.registerLanguage("xml", markup);
SyntaxHighlighter.registerLanguage("markdown", markdown);
SyntaxHighlighter.registerLanguage("md", markdown);
SyntaxHighlighter.registerLanguage("docker", docker);
SyntaxHighlighter.registerLanguage("dockerfile", docker);
SyntaxHighlighter.registerLanguage("rust", rust);
SyntaxHighlighter.registerLanguage("c", c);
SyntaxHighlighter.registerLanguage("cpp", cpp);
SyntaxHighlighter.registerLanguage("csharp", csharp);
SyntaxHighlighter.registerLanguage("ruby", ruby);
SyntaxHighlighter.registerLanguage("php", php);
SyntaxHighlighter.registerLanguage("graphql", graphql);
SyntaxHighlighter.registerLanguage("http", http);
import mermaid from "mermaid";
// Initialize mermaid

View File

@@ -39,6 +39,54 @@ function createRefractorCompatibilityPlugin() {
};
}
// Plugin to force mermaid to use its pre-bundled CJS build (no dynamic imports)
function createMermaidPlugin() {
const candidateRoots = [
path.resolve(__dirname, '../node_modules/mermaid'),
path.resolve(__dirname, '../../node_modules/mermaid'),
];
const mermaidRoot = candidateRoots.find((p) => fs.existsSync(p));
return {
name: 'mermaid-prebundled',
setup(build) {
if (!mermaidRoot) return;
const bundlePath = path.join(mermaidRoot, 'dist', 'mermaid.min.js');
// Intercept bare "mermaid" imports and serve the pre-bundled CJS file
// with an ESM export appended. The CJS file declares a local var
// __esbuild_esm_mermaid_nm and assigns .mermaid on it, so we inline
// the file contents and export from the same scope.
build.onResolve({ filter: /^mermaid$/ }, () => {
return { path: 'mermaid-wrapper', namespace: 'mermaid-ns' };
});
build.onLoad({ filter: /^mermaid-wrapper$/, namespace: 'mermaid-ns' }, () => {
let cjsSource = fs.readFileSync(bundlePath, 'utf8');
// The CJS bundle ends with a line that tries globalThis.__esbuild_esm_mermaid_nm
// which fails because the var is local-scoped when bundled. Strip it and
// expose the local var on globalThis ourselves before that line.
cjsSource = cjsSource.replace(
/globalThis\["mermaid"\]\s*=\s*globalThis\.__esbuild_esm_mermaid_nm\["mermaid"\]\.default;?\s*$/,
''
);
const contents = cjsSource + `
;globalThis.__esbuild_esm_mermaid_nm = typeof __esbuild_esm_mermaid_nm !== "undefined" ? __esbuild_esm_mermaid_nm : {};
var _mermaid_export = __esbuild_esm_mermaid_nm.mermaid;
if (_mermaid_export && _mermaid_export.default) { _mermaid_export = _mermaid_export.default; }
export default _mermaid_export;
export { _mermaid_export as mermaid };
`;
return {
contents,
loader: 'js',
resolveDir: path.dirname(bundlePath),
};
});
},
};
}
// CSS Plugin to handle CSS/SCSS files
function createCSSPlugin() {
return {
@@ -161,12 +209,13 @@ function createConfig(options) {
entryPoints: [entryPoint],
bundle: true,
outdir,
format: 'esm', // Changed from 'iife' to 'esm' to support splitting
format: 'esm',
platform: 'browser',
target: 'es2017',
sourcemap: isDev ? 'inline' : false,
minify: false,
splitting: true, // Now supported with ESM format
treeShaking: true,
splitting: true,
publicPath,
define: {
'process.env.NODE_ENV': JSON.stringify(isDev ? 'development' : 'production'),
@@ -177,7 +226,7 @@ function createConfig(options) {
'react': path.resolve('./node_modules/react'),
...additionalAlias,
},
plugins: [createRefractorCompatibilityPlugin(), createCSSPlugin(), createFileLoaderPlugin()],
plugins: [createMermaidPlugin(), createRefractorCompatibilityPlugin(), createCSSPlugin(), createFileLoaderPlugin()],
loader: {
'.tsx': 'tsx',
'.ts': 'ts',

View File

@@ -85,7 +85,8 @@ class MonitorMetricTypeUtil {
monitorType === MonitorType.Ping ||
monitorType === MonitorType.IP ||
monitorType === MonitorType.Port ||
monitorType === MonitorType.SNMP
monitorType === MonitorType.SNMP ||
monitorType === MonitorType.DNS
) {
return [MonitorMetricType.IsOnline, MonitorMetricType.ResponseTime];
}

View File

@@ -423,7 +423,6 @@
"integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@ampproject/remapping": "^2.2.0",
"@babel/code-frame": "^7.26.0",
@@ -926,7 +925,6 @@
"resolved": "https://registry.npmjs.org/@bull-board/ui/-/ui-5.23.0.tgz",
"integrity": "sha512-iI/Ssl8T5ZEn9s899Qz67m92M6RU8thf/aqD7cUHB2yHmkCjqbw7s7NaODTsyArAsnyu7DGJMWm7EhbfFXDNgQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"@bull-board/api": "5.23.0"
}
@@ -2366,7 +2364,6 @@
"resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz",
"integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==",
"license": "Apache-2.0",
"peer": true,
"engines": {
"node": ">=8.0.0"
}
@@ -5516,7 +5513,6 @@
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.12.tgz",
"integrity": "sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw==",
"license": "MIT",
"peer": true,
"dependencies": {
"@types/prop-types": "*",
"csstype": "^3.0.2"
@@ -5692,7 +5688,8 @@
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-1.0.6.tgz",
"integrity": "sha512-230RC8sFeHoT6sSUlRO6a8cAnclO06eeiq1QDfiv2FGCLWFvvERWgwIQD4FWqD9A69BN7Lzee4OXwoMVnnsWDw==",
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/@types/unist": {
"version": "2.0.11",
@@ -5862,7 +5859,6 @@
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"license": "MIT",
"peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -6755,7 +6751,6 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"caniuse-lite": "^1.0.30001669",
"electron-to-chromium": "^1.5.41",
@@ -7593,7 +7588,6 @@
"resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.33.1.tgz",
"integrity": "sha512-iJc4TwyANnOGR1OmWhsS9ayRS3s+XQ185FmuHObThD+5AeJCakAAbWv8KimMTt08xCCLNgneQwFp+JRJOr9qGQ==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=0.10"
}
@@ -8015,7 +8009,6 @@
"resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
"integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
"license": "ISC",
"peer": true,
"engines": {
"node": ">=12"
}
@@ -10795,7 +10788,6 @@
"integrity": "sha512-N4GT5on8UkZgH0O5LUavMRV1EDEhNTL0KEfRmDIeZHSV7p2XgLoY9t9VDUgL6o+yfdgYHVxuz81G8oB9VG5uyA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@jest/core": "^28.1.3",
"@jest/types": "^28.1.3",
@@ -14511,7 +14503,6 @@
"resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz",
"integrity": "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==",
"license": "MIT",
"peer": true,
"dependencies": {
"pg-connection-string": "^2.9.1",
"pg-pool": "^3.10.1",
@@ -14894,7 +14885,6 @@
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
"license": "MIT",
"peer": true,
"dependencies": {
"loose-envify": "^1.4.0",
"object-assign": "^4.1.1",
@@ -15085,9 +15075,9 @@
}
},
"node_modules/qs": {
"version": "6.14.1",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz",
"integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==",
"version": "6.14.2",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz",
"integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==",
"license": "BSD-3-Clause",
"dependencies": {
"side-channel": "^1.1.0"
@@ -15198,7 +15188,6 @@
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"loose-envify": "^1.1.0"
},
@@ -15283,7 +15272,6 @@
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
"integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
"license": "MIT",
"peer": true,
"dependencies": {
"loose-envify": "^1.1.0",
"scheduler": "^0.23.2"
@@ -15804,8 +15792,7 @@
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz",
"integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==",
"license": "Apache-2.0",
"peer": true
"license": "Apache-2.0"
},
"node_modules/refractor": {
"version": "5.0.0",
@@ -19102,7 +19089,6 @@
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
"license": "MIT",
"peer": true,
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
@@ -19111,8 +19097,7 @@
"version": "0.14.10",
"resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.14.10.tgz",
"integrity": "sha512-YGAhaO7J5ywOXW6InXNlLmfU194F8lVgu7bRntUF3TiG8Y3nBK0x1UJJuHUP/e8IyihkjCYqhCScpSwnlaSRkQ==",
"license": "MIT",
"peer": true
"license": "MIT"
},
"node_modules/zustand": {
"version": "4.5.5",

View File

@@ -16051,9 +16051,9 @@
}
},
"../Common/node_modules/qs": {
"version": "6.14.1",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz",
"integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==",
"version": "6.14.2",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz",
"integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==",
"license": "BSD-3-Clause",
"dependencies": {
"side-channel": "^1.1.0"
@@ -21660,9 +21660,9 @@
"license": "MIT"
},
"node_modules/qs": {
"version": "6.14.1",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz",
"integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==",
"version": "6.14.2",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz",
"integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==",
"license": "BSD-3-Clause",
"dependencies": {
"side-channel": "^1.1.0"

View File

@@ -27,211 +27,195 @@ import useAsyncEffect from "use-async-effect";
import PageComponentProps from "./Pages/PageComponentProps";
import PageLoader from "Common/UI/Components/Loader/PageLoader";
import { RoutesProps } from "./Types/RoutesProps";
// Lazy load route components
// Static page imports
import Welcome from "./Pages/Onboarding/Welcome";
import Home from "./Pages/Home/Home";
import Sso from "./Pages/Onboarding/SSO";
import NotOperationalMonitors from "./Pages/Home/NotOperationalMonitors";
import HomeActiveAlerts from "./Pages/Home/ActiveAlerts";
import OngoingScheduledEvents from "./Pages/Home/OngoingScheduledMaintenance";
import HomeActiveEpisodes from "./Pages/Home/ActiveEpisodes";
import HomeActiveIncidentEpisodes from "./Pages/Home/ActiveIncidentEpisodes";
import SettingsDangerZone from "./Pages/Settings/DangerZone";
import Logout from "./Pages/Logout/Logout";
import UserProfilePicture from "./Pages/Global/UserProfile/Picture";
import UserProfileOverview from "./Pages/Global/UserProfile/Index";
import UserProfilePassword from "./Pages/Global/UserProfile/Password";
import UseTwoFactorAuth from "./Pages/Global/UserProfile/TwoFactorAuth";
import UserProfileDelete from "./Pages/Global/UserProfile/DeleteAccount";
import ProjectInvitations from "./Pages/Global/ProjectInvitations";
import ActiveIncidents from "./Pages/Global/ActiveIncidents";
import ActiveAlerts from "./Pages/Global/ActiveAlerts";
import ActiveAlertEpisodes from "./Pages/Global/ActiveAlertEpisodes";
import ActiveIncidentEpisodes from "./Pages/Global/ActiveIncidentEpisodes";
import MyOnCallPolicies from "./Pages/Global/MyOnCallPolicies";
import PageNotFound from "./Pages/PageNotFound/PageNotFound";
// Lazy-loaded route bundles (all routes in one bundle to minimize chunk count)
type AllRoutesModule = typeof import("./Routes/AllRoutes");
const InitRoutes: React.LazyExoticComponent<
React.FunctionComponent<RoutesProps>
> = lazy(() => {
return import("./Routes/InitRoutes");
});
const Welcome: React.LazyExoticComponent<React.FunctionComponent<any>> = lazy(
() => {
return import("./Pages/Onboarding/Welcome");
},
);
const Home: React.LazyExoticComponent<React.FunctionComponent<any>> = lazy(
() => {
return import("./Pages/Home/Home");
},
);
const Sso: React.LazyExoticComponent<
React.FunctionComponent<PageComponentProps>
> = lazy(() => {
return import("./Pages/Onboarding/SSO");
});
const NotOperationalMonitors: React.LazyExoticComponent<
React.FunctionComponent<PageComponentProps>
> = lazy(() => {
return import("./Pages/Home/NotOperationalMonitors");
});
const HomeActiveAlerts: React.LazyExoticComponent<
React.FunctionComponent<PageComponentProps>
> = lazy(() => {
return import("./Pages/Home/ActiveAlerts");
});
const OngoingScheduledEvents: React.LazyExoticComponent<
React.FunctionComponent<PageComponentProps>
> = lazy(() => {
return import("./Pages/Home/OngoingScheduledMaintenance");
});
const HomeActiveEpisodes: React.LazyExoticComponent<
React.FunctionComponent<PageComponentProps>
> = lazy(() => {
return import("./Pages/Home/ActiveEpisodes");
});
const HomeActiveIncidentEpisodes: React.LazyExoticComponent<
React.FunctionComponent<PageComponentProps>
> = lazy(() => {
return import("./Pages/Home/ActiveIncidentEpisodes");
});
const LogsRoutes: React.LazyExoticComponent<
React.FunctionComponent<PageComponentProps>
> = lazy(() => {
return import("./Routes/LogsRoutes");
});
const LogsRoutes: React.LazyExoticComponent<AllRoutesModule["LogsRoutes"]> =
lazy(() => {
return import("./Routes/AllRoutes").then((m: AllRoutesModule) => {
return { default: m.LogsRoutes };
});
});
const MetricsRoutes: React.LazyExoticComponent<
React.FunctionComponent<PageComponentProps>
AllRoutesModule["MetricsRoutes"]
> = lazy(() => {
return import("./Routes/MetricsRoutes");
return import("./Routes/AllRoutes").then((m: AllRoutesModule) => {
return {
default: m.MetricsRoutes,
};
});
});
const TracesRoutes: React.LazyExoticComponent<
React.FunctionComponent<PageComponentProps>
const TracesRoutes: React.LazyExoticComponent<AllRoutesModule["TracesRoutes"]> =
lazy(() => {
return import("./Routes/AllRoutes").then((m: AllRoutesModule) => {
return {
default: m.TracesRoutes,
};
});
});
const ExceptionsRoutes: React.LazyExoticComponent<
AllRoutesModule["ExceptionsRoutes"]
> = lazy(() => {
return import("./Routes/TracesRoutes");
});
const MonitorsRoutes: React.LazyExoticComponent<
React.FunctionComponent<PageComponentProps>
> = lazy(() => {
return import("./Routes/MonitorsRoutes");
});
const WorkflowRoutes: React.LazyExoticComponent<
React.FunctionComponent<PageComponentProps>
> = lazy(() => {
return import("./Routes/WorkflowRoutes");
});
const StatusPagesRoutes: React.LazyExoticComponent<
React.FunctionComponent<PageComponentProps>
> = lazy(() => {
return import("./Routes/StatusPagesRoutes");
});
const DashboardRoutes: React.LazyExoticComponent<
React.FunctionComponent<PageComponentProps>
> = lazy(() => {
return import("./Routes/DashboardRoutes");
});
const ServiceRoutes: React.LazyExoticComponent<
React.FunctionComponent<PageComponentProps>
> = lazy(() => {
return import("./Routes/ServiceRoutes");
});
const CodeRepositoryRoutes: React.LazyExoticComponent<
React.FunctionComponent<PageComponentProps>
> = lazy(() => {
return import("./Routes/CodeRepositoryRoutes");
return import("./Routes/AllRoutes").then((m: AllRoutesModule) => {
return {
default: m.ExceptionsRoutes,
};
});
});
const IncidentsRoutes: React.LazyExoticComponent<
React.FunctionComponent<PageComponentProps>
AllRoutesModule["IncidentsRoutes"]
> = lazy(() => {
return import("./Routes/IncidentsRoutes");
});
const AlertsRoutes: React.LazyExoticComponent<
React.FunctionComponent<PageComponentProps>
> = lazy(() => {
return import("./Routes/AlertRoutes");
return import("./Routes/AllRoutes").then((m: AllRoutesModule) => {
return {
default: m.IncidentsRoutes,
};
});
});
const AlertsRoutes: React.LazyExoticComponent<AllRoutesModule["AlertsRoutes"]> =
lazy(() => {
return import("./Routes/AllRoutes").then((m: AllRoutesModule) => {
return {
default: m.AlertsRoutes,
};
});
});
const ScheduledMaintenanceEventsRoutes: React.LazyExoticComponent<
React.FunctionComponent<PageComponentProps>
AllRoutesModule["ScheduledMaintenanceEventsRoutes"]
> = lazy(() => {
return import("./Routes/ScheduleMaintenanceEventsRoutes");
});
const SettingsRoutes: React.LazyExoticComponent<
React.FunctionComponent<PageComponentProps>
> = lazy(() => {
return import("./Routes/SettingsRoutes");
});
const SettingsDangerZone: React.LazyExoticComponent<
React.FunctionComponent<any>
> = lazy(() => {
return import("./Pages/Settings/DangerZone");
return import("./Routes/AllRoutes").then((m: AllRoutesModule) => {
return {
default: m.ScheduledMaintenanceEventsRoutes,
};
});
});
const OnCallDutyRoutes: React.LazyExoticComponent<
React.FunctionComponent<PageComponentProps>
AllRoutesModule["OnCallDutyRoutes"]
> = lazy(() => {
return import("./Routes/OnCallDutyRoutes");
return import("./Routes/AllRoutes").then((m: AllRoutesModule) => {
return {
default: m.OnCallDutyRoutes,
};
});
});
const Logout: React.LazyExoticComponent<
React.FunctionComponent<PageComponentProps>
const MonitorsRoutes: React.LazyExoticComponent<
AllRoutesModule["MonitorsRoutes"]
> = lazy(() => {
return import("./Pages/Logout/Logout");
});
const UserProfilePicture: React.LazyExoticComponent<
React.FunctionComponent<PageComponentProps>
> = lazy(() => {
return import("./Pages/Global/UserProfile/Picture");
});
const UserProfileOverview: React.LazyExoticComponent<
React.FunctionComponent<PageComponentProps>
> = lazy(() => {
return import("./Pages/Global/UserProfile/Index");
});
const UserProfilePassword: React.LazyExoticComponent<
React.FunctionComponent<PageComponentProps>
> = lazy(() => {
return import("./Pages/Global/UserProfile/Password");
});
const UseTwoFactorAuth: React.LazyExoticComponent<
React.FunctionComponent<PageComponentProps>
> = lazy(() => {
return import("./Pages/Global/UserProfile/TwoFactorAuth");
});
const UserProfileDelete: React.LazyExoticComponent<
React.FunctionComponent<PageComponentProps>
> = lazy(() => {
return import("./Pages/Global/UserProfile/DeleteAccount");
});
const ProjectInvitations: React.LazyExoticComponent<
React.FunctionComponent<PageComponentProps>
> = lazy(() => {
return import("./Pages/Global/ProjectInvitations");
});
const ActiveIncidents: React.LazyExoticComponent<
React.FunctionComponent<PageComponentProps>
> = lazy(() => {
return import("./Pages/Global/ActiveIncidents");
});
const ActiveAlerts: React.LazyExoticComponent<
React.FunctionComponent<PageComponentProps>
> = lazy(() => {
return import("./Pages/Global/ActiveAlerts");
});
const ActiveAlertEpisodes: React.LazyExoticComponent<
React.FunctionComponent<PageComponentProps>
> = lazy(() => {
return import("./Pages/Global/ActiveAlertEpisodes");
});
const ActiveIncidentEpisodes: React.LazyExoticComponent<
React.FunctionComponent<PageComponentProps>
> = lazy(() => {
return import("./Pages/Global/ActiveIncidentEpisodes");
});
const MyOnCallPolicies: React.LazyExoticComponent<
React.FunctionComponent<PageComponentProps>
> = lazy(() => {
return import("./Pages/Global/MyOnCallPolicies");
});
const UserSettingsRoutes: React.LazyExoticComponent<
React.FunctionComponent<PageComponentProps>
> = lazy(() => {
return import("./Routes/UserSettingsRoutes");
return import("./Routes/AllRoutes").then((m: AllRoutesModule) => {
return {
default: m.MonitorsRoutes,
};
});
});
const MonitorGroupRoutes: React.LazyExoticComponent<
React.FunctionComponent<PageComponentProps>
AllRoutesModule["MonitorGroupRoutes"]
> = lazy(() => {
return import("./Routes/MonitorGroupRoutes");
return import("./Routes/AllRoutes").then((m: AllRoutesModule) => {
return {
default: m.MonitorGroupRoutes,
};
});
});
const WorkflowRoutes: React.LazyExoticComponent<
AllRoutesModule["WorkflowRoutes"]
> = lazy(() => {
return import("./Routes/AllRoutes").then((m: AllRoutesModule) => {
return {
default: m.WorkflowRoutes,
};
});
});
const StatusPagesRoutes: React.LazyExoticComponent<
AllRoutesModule["StatusPagesRoutes"]
> = lazy(() => {
return import("./Routes/AllRoutes").then((m: AllRoutesModule) => {
return {
default: m.StatusPagesRoutes,
};
});
});
const DashboardRoutes: React.LazyExoticComponent<
AllRoutesModule["DashboardRoutes"]
> = lazy(() => {
return import("./Routes/AllRoutes").then((m: AllRoutesModule) => {
return {
default: m.DashboardRoutes,
};
});
});
const ServiceRoutes: React.LazyExoticComponent<
AllRoutesModule["ServiceRoutes"]
> = lazy(() => {
return import("./Routes/AllRoutes").then((m: AllRoutesModule) => {
return {
default: m.ServiceRoutes,
};
});
});
const CodeRepositoryRoutes: React.LazyExoticComponent<
AllRoutesModule["CodeRepositoryRoutes"]
> = lazy(() => {
return import("./Routes/AllRoutes").then((m: AllRoutesModule) => {
return {
default: m.CodeRepositoryRoutes,
};
});
});
const AIAgentTasksRoutes: React.LazyExoticComponent<
React.FunctionComponent<PageComponentProps>
AllRoutesModule["AIAgentTasksRoutes"]
> = lazy(() => {
return import("./Routes/AIAgentTasksRoutes");
return import("./Routes/AllRoutes").then((m: AllRoutesModule) => {
return {
default: m.AIAgentTasksRoutes,
};
});
});
const ExceptionsRoutes: React.LazyExoticComponent<
React.FunctionComponent<PageComponentProps>
const SettingsRoutes: React.LazyExoticComponent<
AllRoutesModule["SettingsRoutes"]
> = lazy(() => {
return import("./Routes/ExceptionsRoutes");
return import("./Routes/AllRoutes").then((m: AllRoutesModule) => {
return {
default: m.SettingsRoutes,
};
});
});
const PageNotFound: React.LazyExoticComponent<
React.FunctionComponent<PageComponentProps>
const UserSettingsRoutes: React.LazyExoticComponent<
AllRoutesModule["UserSettingsRoutes"]
> = lazy(() => {
return import("./Pages/PageNotFound/PageNotFound");
return import("./Routes/AllRoutes").then((m: AllRoutesModule) => {
return {
default: m.UserSettingsRoutes,
};
});
});
const App: () => JSX.Element = () => {

View File

@@ -0,0 +1,169 @@
import React, { FunctionComponent, ReactElement, useState } from "react";
import MonitorStepDnsMonitor from "Common/Types/Monitor/MonitorStepDnsMonitor";
import DnsRecordType from "Common/Types/Monitor/DnsMonitor/DnsRecordType";
import Input, { InputType } from "Common/UI/Components/Input/Input";
import Dropdown, {
DropdownOption,
DropdownValue,
} from "Common/UI/Components/Dropdown/Dropdown";
import FieldLabelElement from "Common/UI/Components/Forms/Fields/FieldLabel";
import Button, { ButtonStyleType } from "Common/UI/Components/Button/Button";
import DropdownUtil from "Common/UI/Utils/Dropdown";
export interface ComponentProps {
monitorStepDnsMonitor: MonitorStepDnsMonitor;
onChange: (value: MonitorStepDnsMonitor) => void;
}
const DnsMonitorStepForm: FunctionComponent<ComponentProps> = (
props: ComponentProps,
): ReactElement => {
const [showAdvancedOptions, setShowAdvancedOptions] =
useState<boolean>(false);
const recordTypeOptions: Array<DropdownOption> =
DropdownUtil.getDropdownOptionsFromEnum(DnsRecordType);
return (
<div className="space-y-5">
<div>
<FieldLabelElement
title="Domain Name"
description="The domain name to query (e.g. example.com)"
required={true}
/>
<Input
initialValue={props.monitorStepDnsMonitor.queryName}
placeholder="example.com"
onChange={(value: string) => {
props.onChange({
...props.monitorStepDnsMonitor,
queryName: value,
});
}}
/>
</div>
<div>
<FieldLabelElement
title="Record Type"
description="The type of DNS record to query"
required={true}
/>
<Dropdown
options={recordTypeOptions}
initialValue={recordTypeOptions.find((option: DropdownOption) => {
return option.value === props.monitorStepDnsMonitor.recordType;
})}
onChange={(value: DropdownValue | Array<DropdownValue> | null) => {
props.onChange({
...props.monitorStepDnsMonitor,
recordType: value as DnsRecordType,
});
}}
/>
</div>
<div>
<FieldLabelElement
title="DNS Server (Optional)"
description="Custom DNS server to use. Leave empty to use system default."
required={false}
/>
<Input
initialValue={props.monitorStepDnsMonitor.hostname || ""}
placeholder="8.8.8.8 or leave empty for system default"
onChange={(value: string) => {
props.onChange({
...props.monitorStepDnsMonitor,
hostname: value || undefined,
});
}}
/>
</div>
{!showAdvancedOptions && (
<div className="mt-1 -ml-3">
<Button
title="Advanced: Port, Timeout and Retries"
buttonStyle={ButtonStyleType.SECONDARY_LINK}
onClick={() => {
setShowAdvancedOptions(true);
}}
/>
</div>
)}
{showAdvancedOptions && (
<div className="space-y-4 border p-4 rounded-md bg-gray-50">
<h4 className="font-medium">Advanced Options</h4>
<div>
<FieldLabelElement
title="Port"
description="DNS port (default: 53)"
required={false}
/>
<Input
initialValue={
props.monitorStepDnsMonitor.port?.toString() || "53"
}
placeholder="53"
type={InputType.NUMBER}
onChange={(value: string) => {
props.onChange({
...props.monitorStepDnsMonitor,
port: parseInt(value) || 53,
});
}}
/>
</div>
<div>
<FieldLabelElement
title="Timeout (ms)"
description="How long to wait for a response before timing out"
required={false}
/>
<Input
initialValue={
props.monitorStepDnsMonitor.timeout?.toString() || "5000"
}
placeholder="5000"
type={InputType.NUMBER}
onChange={(value: string) => {
props.onChange({
...props.monitorStepDnsMonitor,
timeout: parseInt(value) || 5000,
});
}}
/>
</div>
<div>
<FieldLabelElement
title="Retries"
description="Number of times to retry on failure"
required={false}
/>
<Input
initialValue={
props.monitorStepDnsMonitor.retries?.toString() || "3"
}
placeholder="3"
type={InputType.NUMBER}
onChange={(value: string) => {
props.onChange({
...props.monitorStepDnsMonitor,
retries: parseInt(value) || 3,
});
}}
/>
</div>
</div>
)}
</div>
);
};
export default DnsMonitorStepForm;

View File

@@ -75,6 +75,10 @@ import SnmpMonitorStepForm from "./SnmpMonitor/SnmpMonitorStepForm";
import MonitorStepSnmpMonitor, {
MonitorStepSnmpMonitorUtil,
} from "Common/Types/Monitor/MonitorStepSnmpMonitor";
import DnsMonitorStepForm from "./DnsMonitor/DnsMonitorStepForm";
import MonitorStepDnsMonitor, {
MonitorStepDnsMonitorUtil,
} from "Common/Types/Monitor/MonitorStepDnsMonitor";
export interface ComponentProps {
monitorStatusDropdownOptions: Array<DropdownOption>;
@@ -790,6 +794,24 @@ return {
</Card>
)}
{props.monitorType === MonitorType.DNS && (
<Card
title="DNS Monitor Configuration"
description="Configure the DNS monitoring settings"
>
<DnsMonitorStepForm
monitorStepDnsMonitor={
monitorStep.data?.dnsMonitor ||
MonitorStepDnsMonitorUtil.getDefault()
}
onChange={(value: MonitorStepDnsMonitor) => {
monitorStep.setDnsMonitor(value);
props.onChange?.(MonitorStep.clone(monitorStep));
}}
/>
</Card>
)}
{/* Code Monitor Section */}
{isCodeMonitor && (
<Card

View File

@@ -0,0 +1,127 @@
import OneUptimeDate from "Common/Types/Date";
import ProbeMonitorResponse from "Common/Types/Probe/ProbeMonitorResponse";
import DnsMonitorResponse, {
DnsRecordResponse,
} from "Common/Types/Monitor/DnsMonitor/DnsMonitorResponse";
import InfoCard from "Common/UI/Components/InfoCard/InfoCard";
import React, { FunctionComponent, ReactElement } from "react";
export interface ComponentProps {
probeMonitorResponse: ProbeMonitorResponse;
probeName?: string | undefined;
}
const DnsMonitorView: FunctionComponent<ComponentProps> = (
props: ComponentProps,
): ReactElement => {
const dnsResponse: DnsMonitorResponse | undefined =
props.probeMonitorResponse?.dnsResponse;
let responseTimeInMs: number = dnsResponse?.responseTimeInMs || 0;
if (responseTimeInMs > 0) {
responseTimeInMs = Math.round(responseTimeInMs);
}
type GetDnssecStatusText = () => string;
const getDnssecStatusText: GetDnssecStatusText = (): string => {
if (dnsResponse?.isDnssecValid === undefined) {
return "Unknown";
}
return dnsResponse.isDnssecValid ? "Valid" : "Invalid";
};
return (
<div className="space-y-5">
<div className="flex space-x-3">
<InfoCard
className="w-1/5 shadow-none border-2 border-gray-100"
title="Probe"
value={props.probeName || "-"}
/>
<InfoCard
className="w-1/5 shadow-none border-2 border-gray-100"
title="Status"
value={props.probeMonitorResponse.isOnline ? "Online" : "Offline"}
/>
<InfoCard
className="w-1/5 shadow-none border-2 border-gray-100"
title="Response Time"
value={responseTimeInMs ? responseTimeInMs + " ms" : "-"}
/>
<InfoCard
className="w-1/5 shadow-none border-2 border-gray-100"
title="DNSSEC"
value={getDnssecStatusText()}
/>
<InfoCard
className="w-1/5 shadow-none border-2 border-gray-100"
title="Monitored At"
value={
props.probeMonitorResponse?.monitoredAt
? OneUptimeDate.getDateAsUserFriendlyLocalFormattedString(
props.probeMonitorResponse.monitoredAt,
)
: "-"
}
/>
</div>
{props.probeMonitorResponse.failureCause && (
<div className="flex space-x-3">
<InfoCard
className="w-full shadow-none border-2 border-gray-100"
title="Error"
value={props.probeMonitorResponse.failureCause?.toString() || "-"}
/>
</div>
)}
{/* DNS Records Section */}
{dnsResponse?.records && dnsResponse.records.length > 0 && (
<div className="space-y-3">
<h3 className="text-sm font-medium text-gray-700">DNS Records</h3>
<div className="border rounded-md overflow-hidden">
<table className="min-w-full divide-y divide-gray-200">
<thead className="bg-gray-50">
<tr>
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Type
</th>
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Value
</th>
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
TTL
</th>
</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{dnsResponse.records.map(
(record: DnsRecordResponse, index: number) => {
return (
<tr key={index}>
<td className="px-4 py-2 text-sm text-gray-500 font-mono">
{record.type}
</td>
<td className="px-4 py-2 text-sm text-gray-900 font-mono">
{record.value}
</td>
<td className="px-4 py-2 text-sm text-gray-500">
{record.ttl !== undefined ? record.ttl : "-"}
</td>
</tr>
);
},
)}
</tbody>
</table>
</div>
</div>
)}
</div>
);
};
export default DnsMonitorView;

View File

@@ -7,6 +7,7 @@ import ServerMonitorSummaryView from "./ServerMonitorView";
import SyntheticMonitorView from "./SyntheticMonitorView";
import WebsiteMonitorSummaryView from "./WebsiteMonitorView";
import SnmpMonitorView from "./SnmpMonitorView";
import DnsMonitorView from "./DnsMonitorView";
import IncomingMonitorRequest from "Common/Types/Monitor/IncomingMonitor/IncomingMonitorRequest";
import IncomingEmailMonitorRequest from "Common/Types/Monitor/IncomingEmailMonitor/IncomingEmailMonitorRequest";
import MonitorType, {
@@ -121,6 +122,15 @@ const SummaryInfo: FunctionComponent<ComponentProps> = (
);
}
if (props.monitorType === MonitorType.DNS) {
summaryComponent = (
<DnsMonitorView
probeMonitorResponse={probeMonitorResponse}
probeName={props.probeName}
/>
);
}
return (
<div key={key} className="space-y-6">
{summaryComponent}

View File

@@ -1,47 +1,22 @@
import Loader from "../Components/Loader/Loader";
import ComponentProps from "../Pages/PageComponentProps";
import AIAgentTasksLayout from "../Pages/AIAgentTasks/Layout";
import AIAgentTaskViewLayout from "../Pages/AIAgentTasks/View/Layout";
import PageMap from "../Utils/PageMap";
import RouteMap, { RouteUtil, AIAgentTasksRoutePath } from "../Utils/RouteMap";
import Route from "Common/Types/API/Route";
import React, {
FunctionComponent,
LazyExoticComponent,
ReactElement,
Suspense,
lazy,
} from "react";
import React, { FunctionComponent, ReactElement } from "react";
import { Route as PageRoute, Routes } from "react-router-dom";
// Pages
const AIAgentTasks: LazyExoticComponent<FunctionComponent<ComponentProps>> =
lazy(() => {
return import("../Pages/AIAgentTasks/AIAgentTasks");
});
import AIAgentTasks from "../Pages/AIAgentTasks/AIAgentTasks";
const AIAgentTaskView: LazyExoticComponent<FunctionComponent<ComponentProps>> =
lazy(() => {
return import("../Pages/AIAgentTasks/View/Index");
});
import AIAgentTaskView from "../Pages/AIAgentTasks/View/Index";
const AIAgentTaskViewDelete: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/AIAgentTasks/View/Delete");
});
import AIAgentTaskViewDelete from "../Pages/AIAgentTasks/View/Delete";
const AIAgentTaskViewLogs: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/AIAgentTasks/View/Logs");
});
import AIAgentTaskViewLogs from "../Pages/AIAgentTasks/View/Logs";
const AIAgentTaskViewPullRequests: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/AIAgentTasks/View/PullRequests");
});
import AIAgentTaskViewPullRequests from "../Pages/AIAgentTasks/View/PullRequests";
const AIAgentTasksRoutes: FunctionComponent<ComponentProps> = (
props: ComponentProps,
@@ -52,23 +27,19 @@ const AIAgentTasksRoutes: FunctionComponent<ComponentProps> = (
<PageRoute
path={AIAgentTasksRoutePath[PageMap.AI_AGENT_TASKS] || ""}
element={
<Suspense fallback={Loader}>
<AIAgentTasks
{...props}
pageRoute={RouteMap[PageMap.AI_AGENT_TASKS] as Route}
/>
</Suspense>
<AIAgentTasks
{...props}
pageRoute={RouteMap[PageMap.AI_AGENT_TASKS] as Route}
/>
}
/>
<PageRoute
index
element={
<Suspense fallback={Loader}>
<AIAgentTasks
{...props}
pageRoute={RouteMap[PageMap.AI_AGENT_TASKS] as Route}
/>
</Suspense>
<AIAgentTasks
{...props}
pageRoute={RouteMap[PageMap.AI_AGENT_TASKS] as Route}
/>
}
/>
</PageRoute>
@@ -80,23 +51,19 @@ const AIAgentTasksRoutes: FunctionComponent<ComponentProps> = (
<PageRoute
index
element={
<Suspense fallback={Loader}>
<AIAgentTaskView
{...props}
pageRoute={RouteMap[PageMap.AI_AGENT_TASK_VIEW] as Route}
/>
</Suspense>
<AIAgentTaskView
{...props}
pageRoute={RouteMap[PageMap.AI_AGENT_TASK_VIEW] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.AI_AGENT_TASK_VIEW_LOGS)}
element={
<Suspense fallback={Loader}>
<AIAgentTaskViewLogs
{...props}
pageRoute={RouteMap[PageMap.AI_AGENT_TASK_VIEW_LOGS] as Route}
/>
</Suspense>
<AIAgentTaskViewLogs
{...props}
pageRoute={RouteMap[PageMap.AI_AGENT_TASK_VIEW_LOGS] as Route}
/>
}
/>
<PageRoute
@@ -104,25 +71,21 @@ const AIAgentTasksRoutes: FunctionComponent<ComponentProps> = (
PageMap.AI_AGENT_TASK_VIEW_PULL_REQUESTS,
)}
element={
<Suspense fallback={Loader}>
<AIAgentTaskViewPullRequests
{...props}
pageRoute={
RouteMap[PageMap.AI_AGENT_TASK_VIEW_PULL_REQUESTS] as Route
}
/>
</Suspense>
<AIAgentTaskViewPullRequests
{...props}
pageRoute={
RouteMap[PageMap.AI_AGENT_TASK_VIEW_PULL_REQUESTS] as Route
}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.AI_AGENT_TASK_VIEW_DELETE)}
element={
<Suspense fallback={Loader}>
<AIAgentTaskViewDelete
{...props}
pageRoute={RouteMap[PageMap.AI_AGENT_TASK_VIEW_DELETE] as Route}
/>
</Suspense>
<AIAgentTaskViewDelete
{...props}
pageRoute={RouteMap[PageMap.AI_AGENT_TASK_VIEW_DELETE] as Route}
/>
}
/>
</PageRoute>

View File

@@ -1,237 +1,88 @@
import Navigation from "Common/UI/Utils/Navigation";
import Loader from "../Components/Loader/Loader";
import Layout from "../Pages/Alerts/Layout";
import AlertViewLayout from "../Pages/Alerts/View/Layout";
import ComponentProps from "../Pages/PageComponentProps";
import PageMap from "../Utils/PageMap";
import RouteMap, { AlertsRoutePath, RouteUtil } from "../Utils/RouteMap";
import Route from "Common/Types/API/Route";
import React, {
FunctionComponent,
LazyExoticComponent,
Suspense,
lazy,
} from "react";
import React, { FunctionComponent } from "react";
import { Route as PageRoute, Routes } from "react-router-dom";
// Pages
const Alerts: LazyExoticComponent<FunctionComponent<ComponentProps>> = lazy(
() => {
return import("../Pages/Alerts/Alerts");
},
);
import Alerts from "../Pages/Alerts/Alerts";
const AlertCreate: LazyExoticComponent<FunctionComponent<ComponentProps>> =
lazy(() => {
return import("../Pages/Alerts/Create");
});
const AlertView: LazyExoticComponent<FunctionComponent<ComponentProps>> = lazy(
() => {
return import("../Pages/Alerts/View/Index");
},
);
import AlertCreate from "../Pages/Alerts/Create";
import AlertView from "../Pages/Alerts/View/Index";
const AlertViewNotificationLogs: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Alerts/View/NotificationLogs");
});
import AlertViewNotificationLogs from "../Pages/Alerts/View/NotificationLogs";
const AlertViewAILogs: LazyExoticComponent<FunctionComponent<ComponentProps>> =
lazy(() => {
return import("../Pages/Alerts/View/AILogs");
});
import AlertViewAILogs from "../Pages/Alerts/View/AILogs";
const AlertsWorkspaceConnectionSlack: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Alerts/WorkspaceConnectionSlack");
});
import AlertsWorkspaceConnectionSlack from "../Pages/Alerts/WorkspaceConnectionSlack";
const AlertsWorkspaceConnectionMicrosoftTeams: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Alerts/WorkspaceConnectionMicrosoftTeams");
});
import AlertsWorkspaceConnectionMicrosoftTeams from "../Pages/Alerts/WorkspaceConnectionMicrosoftTeams";
const AlertOnCallPolicyExecutionLogs: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Alerts/View/OnCallPolicyExecutionLogs");
});
import AlertOnCallPolicyExecutionLogs from "../Pages/Alerts/View/OnCallPolicyExecutionLogs";
const AlertViewDelete: LazyExoticComponent<FunctionComponent<ComponentProps>> =
lazy(() => {
return import("../Pages/Alerts/View/Delete");
});
const AlertViewStateTimeline: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Alerts/View/StateTimeline");
});
import AlertViewDelete from "../Pages/Alerts/View/Delete";
import AlertViewStateTimeline from "../Pages/Alerts/View/StateTimeline";
const AlertInternalNote: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Alerts/View/InternalNote");
});
import AlertInternalNote from "../Pages/Alerts/View/InternalNote";
const UnresolvedAlerts: LazyExoticComponent<FunctionComponent<ComponentProps>> =
lazy(() => {
return import("../Pages/Alerts/Unresolved");
});
const AlertViewCustomFields: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Alerts/View/CustomFields");
});
const AlertViewOwner: LazyExoticComponent<FunctionComponent<ComponentProps>> =
lazy(() => {
return import("../Pages/Alerts/View/Owners");
});
import UnresolvedAlerts from "../Pages/Alerts/Unresolved";
import AlertViewCustomFields from "../Pages/Alerts/View/CustomFields";
import AlertViewOwner from "../Pages/Alerts/View/Owners";
const AlertViewRootCause: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Alerts/View/RootCause");
});
import AlertViewRootCause from "../Pages/Alerts/View/RootCause";
const AlertViewRemediation: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Alerts/View/Remediation");
});
import AlertViewRemediation from "../Pages/Alerts/View/Remediation";
const AlertDescription: LazyExoticComponent<FunctionComponent<ComponentProps>> =
lazy(() => {
return import("../Pages/Alerts/View/Description");
});
import AlertDescription from "../Pages/Alerts/View/Description";
// Settings Pages
const AlertSettingsState: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Alerts/Settings/AlertState");
});
import AlertSettingsState from "../Pages/Alerts/Settings/AlertState";
const AlertSettingsSeverity: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Alerts/Settings/AlertSeverity");
});
import AlertSettingsSeverity from "../Pages/Alerts/Settings/AlertSeverity";
const AlertSettingsNoteTemplates: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Alerts/Settings/AlertNoteTemplates");
});
import AlertSettingsNoteTemplates from "../Pages/Alerts/Settings/AlertNoteTemplates";
const AlertSettingsNoteTemplatesView: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Alerts/Settings/AlertNoteTemplateDetail");
});
import AlertSettingsNoteTemplatesView from "../Pages/Alerts/Settings/AlertNoteTemplateDetail";
const AlertSettingsCustomFields: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Alerts/Settings/AlertCustomFields");
});
import AlertSettingsCustomFields from "../Pages/Alerts/Settings/AlertCustomFields";
const AlertSettingsGroupingRules: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Alerts/Settings/AlertGroupingRules");
});
import AlertSettingsGroupingRules from "../Pages/Alerts/Settings/AlertGroupingRules";
const AlertSettingsMore: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Alerts/Settings/AlertMoreSettings");
});
import AlertSettingsMore from "../Pages/Alerts/Settings/AlertMoreSettings";
// Episode Pages
const Episodes: LazyExoticComponent<FunctionComponent<ComponentProps>> = lazy(
() => {
return import("../Pages/Alerts/Episodes");
},
);
import Episodes from "../Pages/Alerts/Episodes";
const EpisodeCreate: LazyExoticComponent<FunctionComponent<ComponentProps>> =
lazy(() => {
return import("../Pages/Alerts/EpisodeCreate");
});
import EpisodeCreate from "../Pages/Alerts/EpisodeCreate";
const UnresolvedEpisodes: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Alerts/UnresolvedEpisodes");
});
import UnresolvedEpisodes from "../Pages/Alerts/UnresolvedEpisodes";
// Episode View Pages
const EpisodeViewLayout: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Alerts/EpisodeView/Layout");
});
import EpisodeViewLayout from "../Pages/Alerts/EpisodeView/Layout";
const EpisodeView: LazyExoticComponent<FunctionComponent<ComponentProps>> =
lazy(() => {
return import("../Pages/Alerts/EpisodeView/Index");
});
import EpisodeView from "../Pages/Alerts/EpisodeView/Index";
const EpisodeViewDescription: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Alerts/EpisodeView/Description");
});
import EpisodeViewDescription from "../Pages/Alerts/EpisodeView/Description";
const EpisodeViewRootCause: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Alerts/EpisodeView/RootCause");
});
import EpisodeViewRootCause from "../Pages/Alerts/EpisodeView/RootCause";
const EpisodeViewOwners: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Alerts/EpisodeView/Owners");
});
import EpisodeViewOwners from "../Pages/Alerts/EpisodeView/Owners";
const EpisodeViewStateTimeline: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Alerts/EpisodeView/StateTimeline");
});
import EpisodeViewStateTimeline from "../Pages/Alerts/EpisodeView/StateTimeline";
const EpisodeViewAlerts: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Alerts/EpisodeView/Alerts");
});
import EpisodeViewAlerts from "../Pages/Alerts/EpisodeView/Alerts";
const EpisodeViewInternalNote: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Alerts/EpisodeView/InternalNote");
});
import EpisodeViewInternalNote from "../Pages/Alerts/EpisodeView/InternalNote";
const EpisodeViewRemediation: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Alerts/EpisodeView/Remediation");
});
import EpisodeViewRemediation from "../Pages/Alerts/EpisodeView/Remediation";
const EpisodeViewDelete: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Alerts/EpisodeView/Delete");
});
import EpisodeViewDelete from "../Pages/Alerts/EpisodeView/Delete";
const AlertEpisodeDocs: LazyExoticComponent<FunctionComponent<ComponentProps>> =
lazy(() => {
return import("../Pages/Alerts/EpisodeDocs");
});
import AlertEpisodeDocs from "../Pages/Alerts/EpisodeDocs";
const AlertsRoutes: FunctionComponent<ComponentProps> = (
props: ComponentProps,
@@ -251,24 +102,17 @@ const AlertsRoutes: FunctionComponent<ComponentProps> = (
<PageRoute
path={AlertsRoutePath[PageMap.ALERTS] || ""}
element={
<Suspense fallback={Loader}>
<Alerts
{...props}
pageRoute={RouteMap[PageMap.ALERTS] as Route}
/>
</Suspense>
<Alerts {...props} pageRoute={RouteMap[PageMap.ALERTS] as Route} />
}
/>
<PageRoute
path={AlertsRoutePath[PageMap.UNRESOLVED_ALERTS] || ""}
element={
<Suspense fallback={Loader}>
<UnresolvedAlerts
{...props}
pageRoute={RouteMap[PageMap.UNRESOLVED_ALERTS] as Route}
/>
</Suspense>
<UnresolvedAlerts
{...props}
pageRoute={RouteMap[PageMap.UNRESOLVED_ALERTS] as Route}
/>
}
/>
@@ -277,14 +121,12 @@ const AlertsRoutes: FunctionComponent<ComponentProps> = (
AlertsRoutePath[PageMap.ALERTS_WORKSPACE_CONNECTION_SLACK] || ""
}
element={
<Suspense fallback={Loader}>
<AlertsWorkspaceConnectionSlack
{...props}
pageRoute={
RouteMap[PageMap.ALERTS_WORKSPACE_CONNECTION_SLACK] as Route
}
/>
</Suspense>
<AlertsWorkspaceConnectionSlack
{...props}
pageRoute={
RouteMap[PageMap.ALERTS_WORKSPACE_CONNECTION_SLACK] as Route
}
/>
}
/>
@@ -295,28 +137,24 @@ const AlertsRoutes: FunctionComponent<ComponentProps> = (
] || ""
}
element={
<Suspense fallback={Loader}>
<AlertsWorkspaceConnectionMicrosoftTeams
{...props}
pageRoute={
RouteMap[
PageMap.ALERTS_WORKSPACE_CONNECTION_MICROSOFT_TEAMS
] as Route
}
/>
</Suspense>
<AlertsWorkspaceConnectionMicrosoftTeams
{...props}
pageRoute={
RouteMap[
PageMap.ALERTS_WORKSPACE_CONNECTION_MICROSOFT_TEAMS
] as Route
}
/>
}
/>
<PageRoute
path={AlertsRoutePath[PageMap.ALERT_CREATE] || ""}
element={
<Suspense fallback={Loader}>
<AlertCreate
{...props}
pageRoute={RouteMap[PageMap.ALERT_CREATE] as Route}
/>
</Suspense>
<AlertCreate
{...props}
pageRoute={RouteMap[PageMap.ALERT_CREATE] as Route}
/>
}
/>
@@ -324,38 +162,32 @@ const AlertsRoutes: FunctionComponent<ComponentProps> = (
<PageRoute
path={AlertsRoutePath[PageMap.ALERTS_SETTINGS_STATE] || ""}
element={
<Suspense fallback={Loader}>
<AlertSettingsState
{...props}
pageRoute={RouteMap[PageMap.ALERTS_SETTINGS_STATE] as Route}
/>
</Suspense>
<AlertSettingsState
{...props}
pageRoute={RouteMap[PageMap.ALERTS_SETTINGS_STATE] as Route}
/>
}
/>
<PageRoute
path={AlertsRoutePath[PageMap.ALERTS_SETTINGS_SEVERITY] || ""}
element={
<Suspense fallback={Loader}>
<AlertSettingsSeverity
{...props}
pageRoute={RouteMap[PageMap.ALERTS_SETTINGS_SEVERITY] as Route}
/>
</Suspense>
<AlertSettingsSeverity
{...props}
pageRoute={RouteMap[PageMap.ALERTS_SETTINGS_SEVERITY] as Route}
/>
}
/>
<PageRoute
path={AlertsRoutePath[PageMap.ALERTS_SETTINGS_NOTE_TEMPLATES] || ""}
element={
<Suspense fallback={Loader}>
<AlertSettingsNoteTemplates
{...props}
pageRoute={
RouteMap[PageMap.ALERTS_SETTINGS_NOTE_TEMPLATES] as Route
}
/>
</Suspense>
<AlertSettingsNoteTemplates
{...props}
pageRoute={
RouteMap[PageMap.ALERTS_SETTINGS_NOTE_TEMPLATES] as Route
}
/>
}
/>
@@ -364,54 +196,46 @@ const AlertsRoutes: FunctionComponent<ComponentProps> = (
AlertsRoutePath[PageMap.ALERTS_SETTINGS_NOTE_TEMPLATES_VIEW] || ""
}
element={
<Suspense fallback={Loader}>
<AlertSettingsNoteTemplatesView
{...props}
pageRoute={
RouteMap[PageMap.ALERTS_SETTINGS_NOTE_TEMPLATES_VIEW] as Route
}
/>
</Suspense>
<AlertSettingsNoteTemplatesView
{...props}
pageRoute={
RouteMap[PageMap.ALERTS_SETTINGS_NOTE_TEMPLATES_VIEW] as Route
}
/>
}
/>
<PageRoute
path={AlertsRoutePath[PageMap.ALERTS_SETTINGS_CUSTOM_FIELDS] || ""}
element={
<Suspense fallback={Loader}>
<AlertSettingsCustomFields
{...props}
pageRoute={
RouteMap[PageMap.ALERTS_SETTINGS_CUSTOM_FIELDS] as Route
}
/>
</Suspense>
<AlertSettingsCustomFields
{...props}
pageRoute={
RouteMap[PageMap.ALERTS_SETTINGS_CUSTOM_FIELDS] as Route
}
/>
}
/>
<PageRoute
path={AlertsRoutePath[PageMap.ALERTS_SETTINGS_GROUPING_RULES] || ""}
element={
<Suspense fallback={Loader}>
<AlertSettingsGroupingRules
{...props}
pageRoute={
RouteMap[PageMap.ALERTS_SETTINGS_GROUPING_RULES] as Route
}
/>
</Suspense>
<AlertSettingsGroupingRules
{...props}
pageRoute={
RouteMap[PageMap.ALERTS_SETTINGS_GROUPING_RULES] as Route
}
/>
}
/>
<PageRoute
path={AlertsRoutePath[PageMap.ALERTS_SETTINGS_MORE] || ""}
element={
<Suspense fallback={Loader}>
<AlertSettingsMore
{...props}
pageRoute={RouteMap[PageMap.ALERTS_SETTINGS_MORE] as Route}
/>
</Suspense>
<AlertSettingsMore
{...props}
pageRoute={RouteMap[PageMap.ALERTS_SETTINGS_MORE] as Route}
/>
}
/>
@@ -419,48 +243,40 @@ const AlertsRoutes: FunctionComponent<ComponentProps> = (
<PageRoute
path={AlertsRoutePath[PageMap.ALERT_EPISODE_CREATE] || ""}
element={
<Suspense fallback={Loader}>
<EpisodeCreate
{...props}
pageRoute={RouteMap[PageMap.ALERT_EPISODE_CREATE] as Route}
/>
</Suspense>
<EpisodeCreate
{...props}
pageRoute={RouteMap[PageMap.ALERT_EPISODE_CREATE] as Route}
/>
}
/>
<PageRoute
path={AlertsRoutePath[PageMap.ALERT_EPISODES] || ""}
element={
<Suspense fallback={Loader}>
<Episodes
{...props}
pageRoute={RouteMap[PageMap.ALERT_EPISODES] as Route}
/>
</Suspense>
<Episodes
{...props}
pageRoute={RouteMap[PageMap.ALERT_EPISODES] as Route}
/>
}
/>
<PageRoute
path={AlertsRoutePath[PageMap.UNRESOLVED_ALERT_EPISODES] || ""}
element={
<Suspense fallback={Loader}>
<UnresolvedEpisodes
{...props}
pageRoute={RouteMap[PageMap.UNRESOLVED_ALERT_EPISODES] as Route}
/>
</Suspense>
<UnresolvedEpisodes
{...props}
pageRoute={RouteMap[PageMap.UNRESOLVED_ALERT_EPISODES] as Route}
/>
}
/>
<PageRoute
path={AlertsRoutePath[PageMap.ALERT_EPISODE_DOCS] || ""}
element={
<Suspense fallback={Loader}>
<AlertEpisodeDocs
{...props}
pageRoute={RouteMap[PageMap.ALERT_EPISODE_DOCS] as Route}
/>
</Suspense>
<AlertEpisodeDocs
{...props}
pageRoute={RouteMap[PageMap.ALERT_EPISODE_DOCS] as Route}
/>
}
/>
</PageRoute>
@@ -472,12 +288,10 @@ const AlertsRoutes: FunctionComponent<ComponentProps> = (
<PageRoute
index
element={
<Suspense fallback={Loader}>
<AlertView
{...props}
pageRoute={RouteMap[PageMap.ALERT_VIEW] as Route}
/>
</Suspense>
<AlertView
{...props}
pageRoute={RouteMap[PageMap.ALERT_VIEW] as Route}
/>
}
/>
<PageRoute
@@ -485,105 +299,87 @@ const AlertsRoutes: FunctionComponent<ComponentProps> = (
PageMap.ALERT_VIEW_NOTIFICATION_LOGS,
)}
element={
<Suspense fallback={Loader}>
<AlertViewNotificationLogs
{...props}
pageRoute={
RouteMap[PageMap.ALERT_VIEW_NOTIFICATION_LOGS] as Route
}
/>
</Suspense>
<AlertViewNotificationLogs
{...props}
pageRoute={
RouteMap[PageMap.ALERT_VIEW_NOTIFICATION_LOGS] as Route
}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.ALERT_VIEW_AI_LOGS)}
element={
<Suspense fallback={Loader}>
<AlertViewAILogs
{...props}
pageRoute={RouteMap[PageMap.ALERT_VIEW_AI_LOGS] as Route}
/>
</Suspense>
<AlertViewAILogs
{...props}
pageRoute={RouteMap[PageMap.ALERT_VIEW_AI_LOGS] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.ALERT_VIEW_DESCRIPTION)}
element={
<Suspense fallback={Loader}>
<AlertDescription
{...props}
pageRoute={RouteMap[PageMap.ALERT_VIEW_DESCRIPTION] as Route}
/>
</Suspense>
<AlertDescription
{...props}
pageRoute={RouteMap[PageMap.ALERT_VIEW_DESCRIPTION] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.ALERT_VIEW_ROOT_CAUSE)}
element={
<Suspense fallback={Loader}>
<AlertViewRootCause
{...props}
pageRoute={RouteMap[PageMap.ALERT_VIEW_ROOT_CAUSE] as Route}
/>
</Suspense>
<AlertViewRootCause
{...props}
pageRoute={RouteMap[PageMap.ALERT_VIEW_ROOT_CAUSE] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.ALERT_VIEW_REMEDIATION)}
element={
<Suspense fallback={Loader}>
<AlertViewRemediation
{...props}
pageRoute={RouteMap[PageMap.ALERT_VIEW_REMEDIATION] as Route}
/>
</Suspense>
<AlertViewRemediation
{...props}
pageRoute={RouteMap[PageMap.ALERT_VIEW_REMEDIATION] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.ALERT_VIEW_STATE_TIMELINE)}
element={
<Suspense fallback={Loader}>
<AlertViewStateTimeline
{...props}
pageRoute={RouteMap[PageMap.ALERT_VIEW_STATE_TIMELINE] as Route}
/>
</Suspense>
<AlertViewStateTimeline
{...props}
pageRoute={RouteMap[PageMap.ALERT_VIEW_STATE_TIMELINE] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.ALERT_VIEW_INTERNAL_NOTE)}
element={
<Suspense fallback={Loader}>
<AlertInternalNote
{...props}
pageRoute={RouteMap[PageMap.ALERT_VIEW_INTERNAL_NOTE] as Route}
/>
</Suspense>
<AlertInternalNote
{...props}
pageRoute={RouteMap[PageMap.ALERT_VIEW_INTERNAL_NOTE] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.ALERT_VIEW_CUSTOM_FIELDS)}
element={
<Suspense fallback={Loader}>
<AlertViewCustomFields
{...props}
pageRoute={RouteMap[PageMap.ALERT_VIEW_CUSTOM_FIELDS] as Route}
/>
</Suspense>
<AlertViewCustomFields
{...props}
pageRoute={RouteMap[PageMap.ALERT_VIEW_CUSTOM_FIELDS] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.ALERT_VIEW_OWNERS)}
element={
<Suspense fallback={Loader}>
<AlertViewOwner
{...props}
pageRoute={RouteMap[PageMap.ALERT_VIEW_OWNERS] as Route}
/>
</Suspense>
<AlertViewOwner
{...props}
pageRoute={RouteMap[PageMap.ALERT_VIEW_OWNERS] as Route}
/>
}
/>
<PageRoute
@@ -591,27 +387,23 @@ const AlertsRoutes: FunctionComponent<ComponentProps> = (
PageMap.ALERT_VIEW_ON_CALL_POLICY_EXECUTION_LOGS,
)}
element={
<Suspense fallback={Loader}>
<AlertOnCallPolicyExecutionLogs
{...props}
pageRoute={
RouteMap[
PageMap.ALERT_VIEW_ON_CALL_POLICY_EXECUTION_LOGS
] as Route
}
/>
</Suspense>
<AlertOnCallPolicyExecutionLogs
{...props}
pageRoute={
RouteMap[
PageMap.ALERT_VIEW_ON_CALL_POLICY_EXECUTION_LOGS
] as Route
}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.ALERT_VIEW_DELETE)}
element={
<Suspense fallback={Loader}>
<AlertViewDelete
{...props}
pageRoute={RouteMap[PageMap.ALERT_VIEW_DELETE] as Route}
/>
</Suspense>
<AlertViewDelete
{...props}
pageRoute={RouteMap[PageMap.ALERT_VIEW_DELETE] as Route}
/>
}
/>
</PageRoute>
@@ -624,12 +416,10 @@ const AlertsRoutes: FunctionComponent<ComponentProps> = (
<PageRoute
index
element={
<Suspense fallback={Loader}>
<EpisodeView
{...props}
pageRoute={RouteMap[PageMap.ALERT_EPISODE_VIEW] as Route}
/>
</Suspense>
<EpisodeView
{...props}
pageRoute={RouteMap[PageMap.ALERT_EPISODE_VIEW] as Route}
/>
}
/>
<PageRoute
@@ -637,14 +427,12 @@ const AlertsRoutes: FunctionComponent<ComponentProps> = (
PageMap.ALERT_EPISODE_VIEW_DESCRIPTION,
)}
element={
<Suspense fallback={Loader}>
<EpisodeViewDescription
{...props}
pageRoute={
RouteMap[PageMap.ALERT_EPISODE_VIEW_DESCRIPTION] as Route
}
/>
</Suspense>
<EpisodeViewDescription
{...props}
pageRoute={
RouteMap[PageMap.ALERT_EPISODE_VIEW_DESCRIPTION] as Route
}
/>
}
/>
<PageRoute
@@ -652,25 +440,21 @@ const AlertsRoutes: FunctionComponent<ComponentProps> = (
PageMap.ALERT_EPISODE_VIEW_ROOT_CAUSE,
)}
element={
<Suspense fallback={Loader}>
<EpisodeViewRootCause
{...props}
pageRoute={
RouteMap[PageMap.ALERT_EPISODE_VIEW_ROOT_CAUSE] as Route
}
/>
</Suspense>
<EpisodeViewRootCause
{...props}
pageRoute={
RouteMap[PageMap.ALERT_EPISODE_VIEW_ROOT_CAUSE] as Route
}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.ALERT_EPISODE_VIEW_OWNERS)}
element={
<Suspense fallback={Loader}>
<EpisodeViewOwners
{...props}
pageRoute={RouteMap[PageMap.ALERT_EPISODE_VIEW_OWNERS] as Route}
/>
</Suspense>
<EpisodeViewOwners
{...props}
pageRoute={RouteMap[PageMap.ALERT_EPISODE_VIEW_OWNERS] as Route}
/>
}
/>
<PageRoute
@@ -678,25 +462,21 @@ const AlertsRoutes: FunctionComponent<ComponentProps> = (
PageMap.ALERT_EPISODE_VIEW_STATE_TIMELINE,
)}
element={
<Suspense fallback={Loader}>
<EpisodeViewStateTimeline
{...props}
pageRoute={
RouteMap[PageMap.ALERT_EPISODE_VIEW_STATE_TIMELINE] as Route
}
/>
</Suspense>
<EpisodeViewStateTimeline
{...props}
pageRoute={
RouteMap[PageMap.ALERT_EPISODE_VIEW_STATE_TIMELINE] as Route
}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.ALERT_EPISODE_VIEW_ALERTS)}
element={
<Suspense fallback={Loader}>
<EpisodeViewAlerts
{...props}
pageRoute={RouteMap[PageMap.ALERT_EPISODE_VIEW_ALERTS] as Route}
/>
</Suspense>
<EpisodeViewAlerts
{...props}
pageRoute={RouteMap[PageMap.ALERT_EPISODE_VIEW_ALERTS] as Route}
/>
}
/>
<PageRoute
@@ -704,14 +484,12 @@ const AlertsRoutes: FunctionComponent<ComponentProps> = (
PageMap.ALERT_EPISODE_VIEW_INTERNAL_NOTE,
)}
element={
<Suspense fallback={Loader}>
<EpisodeViewInternalNote
{...props}
pageRoute={
RouteMap[PageMap.ALERT_EPISODE_VIEW_INTERNAL_NOTE] as Route
}
/>
</Suspense>
<EpisodeViewInternalNote
{...props}
pageRoute={
RouteMap[PageMap.ALERT_EPISODE_VIEW_INTERNAL_NOTE] as Route
}
/>
}
/>
<PageRoute
@@ -719,25 +497,21 @@ const AlertsRoutes: FunctionComponent<ComponentProps> = (
PageMap.ALERT_EPISODE_VIEW_REMEDIATION,
)}
element={
<Suspense fallback={Loader}>
<EpisodeViewRemediation
{...props}
pageRoute={
RouteMap[PageMap.ALERT_EPISODE_VIEW_REMEDIATION] as Route
}
/>
</Suspense>
<EpisodeViewRemediation
{...props}
pageRoute={
RouteMap[PageMap.ALERT_EPISODE_VIEW_REMEDIATION] as Route
}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.ALERT_EPISODE_VIEW_DELETE)}
element={
<Suspense fallback={Loader}>
<EpisodeViewDelete
{...props}
pageRoute={RouteMap[PageMap.ALERT_EPISODE_VIEW_DELETE] as Route}
/>
</Suspense>
<EpisodeViewDelete
{...props}
pageRoute={RouteMap[PageMap.ALERT_EPISODE_VIEW_DELETE] as Route}
/>
}
/>
</PageRoute>

View File

@@ -0,0 +1,27 @@
// Observability
export { default as LogsRoutes } from "./LogsRoutes";
export { default as MetricsRoutes } from "./MetricsRoutes";
export { default as TracesRoutes } from "./TracesRoutes";
export { default as ExceptionsRoutes } from "./ExceptionsRoutes";
// Incident management
export { default as IncidentsRoutes } from "./IncidentsRoutes";
export { default as AlertsRoutes } from "./AlertRoutes";
export { default as ScheduledMaintenanceEventsRoutes } from "./ScheduleMaintenanceEventsRoutes";
export { default as OnCallDutyRoutes } from "./OnCallDutyRoutes";
// Monitoring
export { default as MonitorsRoutes } from "./MonitorsRoutes";
export { default as MonitorGroupRoutes } from "./MonitorGroupRoutes";
// Platform
export { default as WorkflowRoutes } from "./WorkflowRoutes";
export { default as StatusPagesRoutes } from "./StatusPagesRoutes";
export { default as DashboardRoutes } from "./DashboardRoutes";
export { default as ServiceRoutes } from "./ServiceRoutes";
export { default as CodeRepositoryRoutes } from "./CodeRepositoryRoutes";
export { default as AIAgentTasksRoutes } from "./AIAgentTasksRoutes";
// Settings
export { default as SettingsRoutes } from "./SettingsRoutes";
export { default as UserSettingsRoutes } from "./UserSettingsRoutes";

View File

@@ -1,4 +1,3 @@
import Loader from "../Components/Loader/Loader";
import ComponentProps from "../Pages/PageComponentProps";
import CodeRepositoryViewLayout from "../Pages/CodeRepository/View/Layout";
import CodeRepositoryLayout from "../Pages/CodeRepository/Layout";
@@ -8,44 +7,19 @@ import RouteMap, {
CodeRepositoryRoutePath,
} from "../Utils/RouteMap";
import Route from "Common/Types/API/Route";
import React, {
FunctionComponent,
LazyExoticComponent,
ReactElement,
Suspense,
lazy,
} from "react";
import React, { FunctionComponent, ReactElement } from "react";
import { Route as PageRoute, Routes } from "react-router-dom";
// Pages
const CodeRepository: LazyExoticComponent<FunctionComponent<ComponentProps>> =
lazy(() => {
return import("../Pages/CodeRepository/CodeRepository");
});
import CodeRepository from "../Pages/CodeRepository/CodeRepository";
const CodeRepositoryView: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/CodeRepository/View/Index");
});
import CodeRepositoryView from "../Pages/CodeRepository/View/Index";
const CodeRepositoryViewDelete: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/CodeRepository/View/Delete");
});
import CodeRepositoryViewDelete from "../Pages/CodeRepository/View/Delete";
const CodeRepositoryViewSettings: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/CodeRepository/View/Settings");
});
import CodeRepositoryViewSettings from "../Pages/CodeRepository/View/Settings";
const CodeRepositoryViewServices: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/CodeRepository/View/Services");
});
import CodeRepositoryViewServices from "../Pages/CodeRepository/View/Services";
const CodeRepositoryRoutes: FunctionComponent<ComponentProps> = (
props: ComponentProps,
@@ -56,12 +30,10 @@ const CodeRepositoryRoutes: FunctionComponent<ComponentProps> = (
<PageRoute
path={CodeRepositoryRoutePath[PageMap.CODE_REPOSITORY] || ""}
element={
<Suspense fallback={Loader}>
<CodeRepository
{...props}
pageRoute={RouteMap[PageMap.CODE_REPOSITORY] as Route}
/>
</Suspense>
<CodeRepository
{...props}
pageRoute={RouteMap[PageMap.CODE_REPOSITORY] as Route}
/>
}
/>
</PageRoute>
@@ -73,12 +45,10 @@ const CodeRepositoryRoutes: FunctionComponent<ComponentProps> = (
<PageRoute
index
element={
<Suspense fallback={Loader}>
<CodeRepositoryView
{...props}
pageRoute={RouteMap[PageMap.CODE_REPOSITORY_VIEW] as Route}
/>
</Suspense>
<CodeRepositoryView
{...props}
pageRoute={RouteMap[PageMap.CODE_REPOSITORY_VIEW] as Route}
/>
}
/>
@@ -87,14 +57,10 @@ const CodeRepositoryRoutes: FunctionComponent<ComponentProps> = (
PageMap.CODE_REPOSITORY_VIEW_DELETE,
)}
element={
<Suspense fallback={Loader}>
<CodeRepositoryViewDelete
{...props}
pageRoute={
RouteMap[PageMap.CODE_REPOSITORY_VIEW_DELETE] as Route
}
/>
</Suspense>
<CodeRepositoryViewDelete
{...props}
pageRoute={RouteMap[PageMap.CODE_REPOSITORY_VIEW_DELETE] as Route}
/>
}
/>
@@ -103,14 +69,12 @@ const CodeRepositoryRoutes: FunctionComponent<ComponentProps> = (
PageMap.CODE_REPOSITORY_VIEW_SETTINGS,
)}
element={
<Suspense fallback={Loader}>
<CodeRepositoryViewSettings
{...props}
pageRoute={
RouteMap[PageMap.CODE_REPOSITORY_VIEW_SETTINGS] as Route
}
/>
</Suspense>
<CodeRepositoryViewSettings
{...props}
pageRoute={
RouteMap[PageMap.CODE_REPOSITORY_VIEW_SETTINGS] as Route
}
/>
}
/>
@@ -119,14 +83,12 @@ const CodeRepositoryRoutes: FunctionComponent<ComponentProps> = (
PageMap.CODE_REPOSITORY_VIEW_SERVICES,
)}
element={
<Suspense fallback={Loader}>
<CodeRepositoryViewServices
{...props}
pageRoute={
RouteMap[PageMap.CODE_REPOSITORY_VIEW_SERVICES] as Route
}
/>
</Suspense>
<CodeRepositoryViewServices
{...props}
pageRoute={
RouteMap[PageMap.CODE_REPOSITORY_VIEW_SERVICES] as Route
}
/>
}
/>
</PageRoute>

View File

@@ -1,46 +1,20 @@
import Loader from "../Components/Loader/Loader";
import ComponentProps from "../Pages/PageComponentProps";
import DashboardViewLayout from "../Pages/Dashboards/View/Layout";
import PageMap from "../Utils/PageMap";
import RouteMap, { DashboardsRoutePath, RouteUtil } from "../Utils/RouteMap";
import Route from "Common/Types/API/Route";
import React, {
FunctionComponent,
LazyExoticComponent,
ReactElement,
Suspense,
lazy,
} from "react";
import React, { FunctionComponent, ReactElement } from "react";
import { Route as PageRoute, Routes } from "react-router-dom";
const Dashboards: LazyExoticComponent<FunctionComponent<ComponentProps>> = lazy(
() => {
return import("../Pages/Dashboards/Dashboards");
},
);
import Dashboards from "../Pages/Dashboards/Dashboards";
const DashboardView: LazyExoticComponent<FunctionComponent<ComponentProps>> =
lazy(() => {
return import("../Pages/Dashboards/View/Index");
});
import DashboardView from "../Pages/Dashboards/View/Index";
const DashboardViewOverview: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Dashboards/View/Overview");
});
import DashboardViewOverview from "../Pages/Dashboards/View/Overview";
const DashboardViewDelete: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Dashboards/View/Delete");
});
import DashboardViewDelete from "../Pages/Dashboards/View/Delete";
const DashboardViewSettings: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Dashboards/View/Settings");
});
import DashboardViewSettings from "../Pages/Dashboards/View/Settings";
const DashboardsRoutes: FunctionComponent<ComponentProps> = (
props: ComponentProps,
@@ -50,12 +24,10 @@ const DashboardsRoutes: FunctionComponent<ComponentProps> = (
<PageRoute
path={DashboardsRoutePath[PageMap.DASHBOARDS] || ""}
element={
<Suspense fallback={Loader}>
<Dashboards
{...props}
pageRoute={RouteMap[PageMap.DASHBOARDS] as Route}
/>
</Suspense>
<Dashboards
{...props}
pageRoute={RouteMap[PageMap.DASHBOARDS] as Route}
/>
}
/>
@@ -66,48 +38,40 @@ const DashboardsRoutes: FunctionComponent<ComponentProps> = (
<PageRoute
index
element={
<Suspense fallback={Loader}>
<DashboardView
{...props}
pageRoute={RouteMap[PageMap.DASHBOARD_VIEW] as Route}
/>
</Suspense>
<DashboardView
{...props}
pageRoute={RouteMap[PageMap.DASHBOARD_VIEW] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.DASHBOARD_VIEW_OVERVIEW)}
element={
<Suspense fallback={Loader}>
<DashboardViewOverview
{...props}
pageRoute={RouteMap[PageMap.DASHBOARD_VIEW_OVERVIEW] as Route}
/>
</Suspense>
<DashboardViewOverview
{...props}
pageRoute={RouteMap[PageMap.DASHBOARD_VIEW_OVERVIEW] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.DASHBOARD_VIEW_DELETE)}
element={
<Suspense fallback={Loader}>
<DashboardViewDelete
{...props}
pageRoute={RouteMap[PageMap.DASHBOARD_VIEW_DELETE] as Route}
/>
</Suspense>
<DashboardViewDelete
{...props}
pageRoute={RouteMap[PageMap.DASHBOARD_VIEW_DELETE] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.DASHBOARD_VIEW_SETTINGS)}
element={
<Suspense fallback={Loader}>
<DashboardViewSettings
{...props}
pageRoute={RouteMap[PageMap.DASHBOARD_VIEW_SETTINGS] as Route}
/>
</Suspense>
<DashboardViewSettings
{...props}
pageRoute={RouteMap[PageMap.DASHBOARD_VIEW_SETTINGS] as Route}
/>
}
/>
</PageRoute>

View File

@@ -1,42 +1,20 @@
import Loader from "../Components/Loader/Loader";
import ComponentProps from "../Pages/PageComponentProps";
import ExceptionsLayout from "../Pages/Exceptions/Layout";
import ExceptionViewLayout from "../Pages/Exceptions/View/Layout";
import PageMap from "../Utils/PageMap";
import RouteMap, { ExceptionsRoutePath } from "../Utils/RouteMap";
import Route from "Common/Types/API/Route";
import React, {
FunctionComponent,
LazyExoticComponent,
ReactElement,
Suspense,
lazy,
} from "react";
import React, { FunctionComponent, ReactElement } from "react";
import { Route as PageRoute, Routes } from "react-router-dom";
// Lazy Pages
const ExceptionsUnresolved: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Exceptions/Unresolved");
});
// Pages
import ExceptionsUnresolved from "../Pages/Exceptions/Unresolved";
const ExceptionsResolved: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Exceptions/Resolved");
});
import ExceptionsResolved from "../Pages/Exceptions/Resolved";
const ExceptionsArchived: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Exceptions/Archived");
});
import ExceptionsArchived from "../Pages/Exceptions/Archived";
const ExceptionView: LazyExoticComponent<FunctionComponent<ComponentProps>> =
lazy(() => {
return import("../Pages/Exceptions/View/Index");
});
import ExceptionView from "../Pages/Exceptions/View/Index";
const ExceptionsRoutes: FunctionComponent<ComponentProps> = (
props: ComponentProps,
@@ -47,48 +25,40 @@ const ExceptionsRoutes: FunctionComponent<ComponentProps> = (
<PageRoute
index
element={
<Suspense fallback={Loader}>
<ExceptionsUnresolved
{...props}
pageRoute={RouteMap[PageMap.EXCEPTIONS] as Route}
/>
</Suspense>
<ExceptionsUnresolved
{...props}
pageRoute={RouteMap[PageMap.EXCEPTIONS] as Route}
/>
}
/>
<PageRoute
path={ExceptionsRoutePath[PageMap.EXCEPTIONS_UNRESOLVED] || ""}
element={
<Suspense fallback={Loader}>
<ExceptionsUnresolved
{...props}
pageRoute={RouteMap[PageMap.EXCEPTIONS_UNRESOLVED] as Route}
/>
</Suspense>
<ExceptionsUnresolved
{...props}
pageRoute={RouteMap[PageMap.EXCEPTIONS_UNRESOLVED] as Route}
/>
}
/>
<PageRoute
path={ExceptionsRoutePath[PageMap.EXCEPTIONS_RESOLVED] || ""}
element={
<Suspense fallback={Loader}>
<ExceptionsResolved
{...props}
pageRoute={RouteMap[PageMap.EXCEPTIONS_RESOLVED] as Route}
/>
</Suspense>
<ExceptionsResolved
{...props}
pageRoute={RouteMap[PageMap.EXCEPTIONS_RESOLVED] as Route}
/>
}
/>
<PageRoute
path={ExceptionsRoutePath[PageMap.EXCEPTIONS_ARCHIVED] || ""}
element={
<Suspense fallback={Loader}>
<ExceptionsArchived
{...props}
pageRoute={RouteMap[PageMap.EXCEPTIONS_ARCHIVED] as Route}
/>
</Suspense>
<ExceptionsArchived
{...props}
pageRoute={RouteMap[PageMap.EXCEPTIONS_ARCHIVED] as Route}
/>
}
/>
</PageRoute>
@@ -101,12 +71,10 @@ const ExceptionsRoutes: FunctionComponent<ComponentProps> = (
<PageRoute
index
element={
<Suspense fallback={Loader}>
<ExceptionView
{...props}
pageRoute={RouteMap[PageMap.EXCEPTIONS_VIEW] as Route}
/>
</Suspense>
<ExceptionView
{...props}
pageRoute={RouteMap[PageMap.EXCEPTIONS_VIEW] as Route}
/>
}
/>
</PageRoute>

File diff suppressed because it is too large Load Diff

View File

@@ -1,14 +1,11 @@
import Loader from "../Components/Loader/Loader";
import { RoutesProps } from "../Types/RoutesProps";
import PageMap from "../Utils/PageMap";
import RouteMap from "../Utils/RouteMap";
import Route from "Common/Types/API/Route";
import React, { FunctionComponent, ReactElement, Suspense, lazy } from "react";
import React, { FunctionComponent, ReactElement } from "react";
import { Route as PageRoute, Routes } from "react-router-dom";
const Init: any = lazy(() => {
return import("../Pages/Init/Init");
});
import Init from "../Pages/Init/Init";
const InitRoutes: FunctionComponent<RoutesProps> = (
props: RoutesProps,
@@ -19,28 +16,24 @@ const InitRoutes: FunctionComponent<RoutesProps> = (
<PageRoute
path={RouteMap[PageMap.INIT]?.toString() || ""}
element={
<Suspense fallback={Loader}>
<Init
{...rest}
pageRoute={RouteMap[PageMap.INIT] as Route}
projects={projects}
isLoadingProjects={isLoading}
/>
</Suspense>
<Init
{...rest}
pageRoute={RouteMap[PageMap.INIT] as Route}
projects={projects}
isLoadingProjects={isLoading}
/>
}
/>
<PageRoute
path={RouteMap[PageMap.INIT_PROJECT]?.toString() || ""}
element={
<Suspense fallback={Loader}>
<Init
{...rest}
pageRoute={RouteMap[PageMap.INIT_PROJECT] as Route}
projects={projects}
isLoadingProjects={isLoading}
/>
</Suspense>
<Init
{...rest}
pageRoute={RouteMap[PageMap.INIT_PROJECT] as Route}
projects={projects}
isLoadingProjects={isLoading}
/>
}
/>
</Routes>

View File

@@ -1,24 +1,13 @@
import Loader from "../Components/Loader/Loader";
import ComponentProps from "../Pages/PageComponentProps";
import LogsLayout from "../Pages/Logs/Layout";
import PageMap from "../Utils/PageMap";
import RouteMap from "../Utils/RouteMap";
import Route from "Common/Types/API/Route";
import React, {
FunctionComponent,
LazyExoticComponent,
ReactElement,
Suspense,
lazy,
} from "react";
import React, { FunctionComponent, ReactElement } from "react";
import { Route as PageRoute, Routes } from "react-router-dom";
// Lazy Pages
const LogsPage: LazyExoticComponent<FunctionComponent<ComponentProps>> = lazy(
() => {
return import("../Pages/Logs/Index");
},
);
// Pages
import LogsPage from "../Pages/Logs/Index";
const LogsRoutes: FunctionComponent<ComponentProps> = (
props: ComponentProps,
@@ -29,12 +18,7 @@ const LogsRoutes: FunctionComponent<ComponentProps> = (
<PageRoute
index
element={
<Suspense fallback={Loader}>
<LogsPage
{...props}
pageRoute={RouteMap[PageMap.LOGS] as Route}
/>
</Suspense>
<LogsPage {...props} pageRoute={RouteMap[PageMap.LOGS] as Route} />
}
/>
</PageRoute>

View File

@@ -1,29 +1,16 @@
import Loader from "../Components/Loader/Loader";
import ComponentProps from "../Pages/PageComponentProps";
import MetricsLayout from "../Pages/Metrics/Layout";
import MetricsViewLayout from "../Pages/Metrics/View/Layout";
import PageMap from "../Utils/PageMap";
import RouteMap, { RouteUtil, MetricsRoutePath } from "../Utils/RouteMap";
import Route from "Common/Types/API/Route";
import React, {
FunctionComponent,
LazyExoticComponent,
ReactElement,
Suspense,
lazy,
} from "react";
import React, { FunctionComponent, ReactElement } from "react";
import { Route as PageRoute, Routes } from "react-router-dom";
// Lazy Pages
const MetricsPage: LazyExoticComponent<FunctionComponent<ComponentProps>> =
lazy(() => {
return import("../Pages/Metrics/Index");
});
// Pages
import MetricsPage from "../Pages/Metrics/Index";
const MetricViewPage: LazyExoticComponent<FunctionComponent<ComponentProps>> =
lazy(() => {
return import("../Pages/Metrics/View/Index");
});
import MetricViewPage from "../Pages/Metrics/View/Index";
const MetricsRoutes: FunctionComponent<ComponentProps> = (
props: ComponentProps,
@@ -34,12 +21,10 @@ const MetricsRoutes: FunctionComponent<ComponentProps> = (
<PageRoute
index
element={
<Suspense fallback={Loader}>
<MetricsPage
{...props}
pageRoute={RouteMap[PageMap.METRICS] as Route}
/>
</Suspense>
<MetricsPage
{...props}
pageRoute={RouteMap[PageMap.METRICS] as Route}
/>
}
/>
</PageRoute>
@@ -52,24 +37,20 @@ const MetricsRoutes: FunctionComponent<ComponentProps> = (
<PageRoute
index
element={
<Suspense fallback={Loader}>
<MetricViewPage
{...props}
pageRoute={RouteMap[PageMap.METRIC_VIEW] as Route}
/>
</Suspense>
<MetricViewPage
{...props}
pageRoute={RouteMap[PageMap.METRIC_VIEW] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.METRIC_VIEW)}
element={
<Suspense fallback={Loader}>
<MetricViewPage
{...props}
pageRoute={RouteMap[PageMap.METRIC_VIEW] as Route}
/>
</Suspense>
<MetricViewPage
{...props}
pageRoute={RouteMap[PageMap.METRIC_VIEW] as Route}
/>
}
/>
</PageRoute>

View File

@@ -1,54 +1,21 @@
import Loader from "../Components/Loader/Loader";
import MonitorGroupViewLayout from "../Pages/MonitorGroup/View/Layout";
import ComponentProps from "../Pages/PageComponentProps";
import PageMap from "../Utils/PageMap";
import RouteMap, { MonitorGroupRoutePath, RouteUtil } from "../Utils/RouteMap";
import Route from "Common/Types/API/Route";
import React, {
FunctionComponent,
LazyExoticComponent,
ReactElement,
Suspense,
lazy,
} from "react";
import React, { FunctionComponent, ReactElement } from "react";
import { Route as PageRoute, Routes } from "react-router-dom";
// Pages
const MonitorGroups: LazyExoticComponent<FunctionComponent<ComponentProps>> =
lazy(() => {
return import("../Pages/MonitorGroup/MonitorGroups");
});
const MonitorGroupView: LazyExoticComponent<FunctionComponent<ComponentProps>> =
lazy(() => {
return import("../Pages/MonitorGroup/View/Index");
});
const MonitorGroupViewDelete: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/MonitorGroup/View/Delete");
});
import MonitorGroups from "../Pages/MonitorGroup/MonitorGroups";
import MonitorGroupView from "../Pages/MonitorGroup/View/Index";
import MonitorGroupViewDelete from "../Pages/MonitorGroup/View/Delete";
const MonitorGroupAlerts: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/MonitorGroup/View/Alerts");
});
import MonitorGroupAlerts from "../Pages/MonitorGroup/View/Alerts";
const MonitorGroupViewMonitors: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/MonitorGroup/View/Monitors");
});
const MonitorGroupViewIncidents: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/MonitorGroup/View/Incidents");
});
const MonitorGroupViewOwners: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/MonitorGroup/View/Owners");
});
import MonitorGroupViewMonitors from "../Pages/MonitorGroup/View/Monitors";
import MonitorGroupViewIncidents from "../Pages/MonitorGroup/View/Incidents";
import MonitorGroupViewOwners from "../Pages/MonitorGroup/View/Owners";
const MonitorGroupRoutes: FunctionComponent<ComponentProps> = (
props: ComponentProps,
@@ -58,12 +25,10 @@ const MonitorGroupRoutes: FunctionComponent<ComponentProps> = (
<PageRoute
path={MonitorGroupRoutePath[PageMap.MONITOR_GROUPS] || ""}
element={
<Suspense fallback={Loader}>
<MonitorGroups
{...props}
pageRoute={RouteMap[PageMap.MONITOR_GROUPS] as Route}
/>
</Suspense>
<MonitorGroups
{...props}
pageRoute={RouteMap[PageMap.MONITOR_GROUPS] as Route}
/>
}
/>
@@ -74,35 +39,29 @@ const MonitorGroupRoutes: FunctionComponent<ComponentProps> = (
<PageRoute
index
element={
<Suspense fallback={Loader}>
<MonitorGroupView
{...props}
pageRoute={RouteMap[PageMap.MONITOR_GROUP_VIEW] as Route}
/>
</Suspense>
<MonitorGroupView
{...props}
pageRoute={RouteMap[PageMap.MONITOR_GROUP_VIEW] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.MONITOR_GROUP_VIEW_DELETE)}
element={
<Suspense fallback={Loader}>
<MonitorGroupViewDelete
{...props}
pageRoute={RouteMap[PageMap.MONITOR_GROUP_VIEW_DELETE] as Route}
/>
</Suspense>
<MonitorGroupViewDelete
{...props}
pageRoute={RouteMap[PageMap.MONITOR_GROUP_VIEW_DELETE] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.MONITOR_GROUP_VIEW_ALERTS)}
element={
<Suspense fallback={Loader}>
<MonitorGroupAlerts
{...props}
pageRoute={RouteMap[PageMap.MONITOR_GROUP_VIEW_ALERTS] as Route}
/>
</Suspense>
<MonitorGroupAlerts
{...props}
pageRoute={RouteMap[PageMap.MONITOR_GROUP_VIEW_ALERTS] as Route}
/>
}
/>
@@ -111,14 +70,10 @@ const MonitorGroupRoutes: FunctionComponent<ComponentProps> = (
PageMap.MONITOR_GROUP_VIEW_MONITORS,
)}
element={
<Suspense fallback={Loader}>
<MonitorGroupViewMonitors
{...props}
pageRoute={
RouteMap[PageMap.MONITOR_GROUP_VIEW_MONITORS] as Route
}
/>
</Suspense>
<MonitorGroupViewMonitors
{...props}
pageRoute={RouteMap[PageMap.MONITOR_GROUP_VIEW_MONITORS] as Route}
/>
}
/>
@@ -127,26 +82,22 @@ const MonitorGroupRoutes: FunctionComponent<ComponentProps> = (
PageMap.MONITOR_GROUP_VIEW_INCIDENTS,
)}
element={
<Suspense fallback={Loader}>
<MonitorGroupViewIncidents
{...props}
pageRoute={
RouteMap[PageMap.MONITOR_GROUP_VIEW_INCIDENTS] as Route
}
/>
</Suspense>
<MonitorGroupViewIncidents
{...props}
pageRoute={
RouteMap[PageMap.MONITOR_GROUP_VIEW_INCIDENTS] as Route
}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.MONITOR_GROUP_VIEW_OWNERS)}
element={
<Suspense fallback={Loader}>
<MonitorGroupViewOwners
{...props}
pageRoute={RouteMap[PageMap.MONITOR_GROUP_VIEW_OWNERS] as Route}
/>
</Suspense>
<MonitorGroupViewOwners
{...props}
pageRoute={RouteMap[PageMap.MONITOR_GROUP_VIEW_OWNERS] as Route}
/>
}
/>
</PageRoute>

View File

@@ -1,160 +1,57 @@
import Navigation from "Common/UI/Utils/Navigation";
import Loader from "../Components/Loader/Loader";
import MonitorLayout from "../Pages/Monitor/Layout";
import MonitorViewLayout from "../Pages/Monitor/View/Layout";
import ComponentProps from "../Pages/PageComponentProps";
import PageMap from "../Utils/PageMap";
import RouteMap, { MonitorsRoutePath, RouteUtil } from "../Utils/RouteMap";
import Route from "Common/Types/API/Route";
import React, {
FunctionComponent,
LazyExoticComponent,
ReactElement,
Suspense,
lazy,
} from "react";
import React, { FunctionComponent, ReactElement } from "react";
import { Route as PageRoute, Routes } from "react-router-dom";
// Pages
const MonitorPage: LazyExoticComponent<FunctionComponent<ComponentProps>> =
lazy(() => {
return import("../Pages/Monitor/Monitors");
});
import MonitorPage from "../Pages/Monitor/Monitors";
const WorkspaceConnectionSlack: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Monitor/WorkspaceConnectionSlack");
});
import WorkspaceConnectionSlack from "../Pages/Monitor/WorkspaceConnectionSlack";
const WorkspaceConnectionTeams: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Monitor/WorkspaceConnectionMicrosoftTeams");
});
import WorkspaceConnectionTeams from "../Pages/Monitor/WorkspaceConnectionMicrosoftTeams";
const MonitorViewMetrics: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Monitor/View/Metrics");
});
import MonitorViewMetrics from "../Pages/Monitor/View/Metrics";
const MonitorprobeDisconnected: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Monitor/ProbeDisconnected");
});
import MonitorprobeDisconnected from "../Pages/Monitor/ProbeDisconnected";
const MonitorProbeDisabled: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Monitor/ProbeDisabled");
});
import MonitorProbeDisabled from "../Pages/Monitor/ProbeDisabled";
const MonitorView: LazyExoticComponent<FunctionComponent<ComponentProps>> =
lazy(() => {
return import("../Pages/Monitor/View/Index");
});
const MonitorViewDelete: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Monitor/View/Delete");
});
import MonitorView from "../Pages/Monitor/View/Index";
import MonitorViewDelete from "../Pages/Monitor/View/Delete";
const MonitorViewLogs: LazyExoticComponent<FunctionComponent<ComponentProps>> =
lazy(() => {
return import("../Pages/Monitor/View/Logs");
});
import MonitorViewLogs from "../Pages/Monitor/View/Logs";
const MonitorViewCriteria: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Monitor/View/Criteria");
});
const MonitorViewStatusTimeline: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Monitor/View/StatusTimeline");
});
const MonitorIncidents: LazyExoticComponent<FunctionComponent<ComponentProps>> =
lazy(() => {
return import("../Pages/Monitor/View/Incidents");
});
import MonitorViewCriteria from "../Pages/Monitor/View/Criteria";
import MonitorViewStatusTimeline from "../Pages/Monitor/View/StatusTimeline";
import MonitorIncidents from "../Pages/Monitor/View/Incidents";
const MonitorAlerts: LazyExoticComponent<FunctionComponent<ComponentProps>> =
lazy(() => {
return import("../Pages/Monitor/View/Alerts");
});
const MonitorInoperational: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Monitor/NotOperationalMonitors");
});
const MonitorDisabled: LazyExoticComponent<FunctionComponent<ComponentProps>> =
lazy(() => {
return import("../Pages/Monitor/DisabledMonitors");
});
const MonitorViewCustomFields: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Monitor/View/CustomFields");
});
const MonitorViewInterval: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Monitor/View/Interval");
});
import MonitorAlerts from "../Pages/Monitor/View/Alerts";
import MonitorInoperational from "../Pages/Monitor/NotOperationalMonitors";
import MonitorDisabled from "../Pages/Monitor/DisabledMonitors";
import MonitorViewCustomFields from "../Pages/Monitor/View/CustomFields";
import MonitorViewInterval from "../Pages/Monitor/View/Interval";
const MonitorViewDocumentation: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Monitor/View/Documentation");
});
import MonitorViewDocumentation from "../Pages/Monitor/View/Documentation";
const MonitorViewProbes: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Monitor/View/Probes");
});
const MonitorViewOwner: LazyExoticComponent<FunctionComponent<ComponentProps>> =
lazy(() => {
return import("../Pages/Monitor/View/Owners");
});
const MonitorViewSettings: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Monitor/View/Settings");
});
import MonitorViewProbes from "../Pages/Monitor/View/Probes";
import MonitorViewOwner from "../Pages/Monitor/View/Owners";
import MonitorViewSettings from "../Pages/Monitor/View/Settings";
const MonitorViewNotificationLogs: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Monitor/View/NotificationLogs");
});
import MonitorViewNotificationLogs from "../Pages/Monitor/View/NotificationLogs";
const MonitorCreate: LazyExoticComponent<FunctionComponent<ComponentProps>> =
lazy(() => {
return import("../Pages/Monitor/Create");
});
import MonitorCreate from "../Pages/Monitor/Create";
// Settings Pages
const MonitorSettingsStatus: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Monitor/Settings/MonitorStatus");
});
import MonitorSettingsStatus from "../Pages/Monitor/Settings/MonitorStatus";
const MonitorSettingsCustomFields: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Monitor/Settings/MonitorCustomFields");
});
import MonitorSettingsCustomFields from "../Pages/Monitor/Settings/MonitorCustomFields";
const MonitorSettingsSecrets: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Monitor/Settings/MonitorSecrets");
});
import MonitorSettingsSecrets from "../Pages/Monitor/Settings/MonitorSecrets";
const MonitorRoutes: FunctionComponent<ComponentProps> = (
props: ComponentProps,
@@ -174,61 +71,49 @@ const MonitorRoutes: FunctionComponent<ComponentProps> = (
<PageRoute
index
element={
<Suspense fallback={Loader}>
<MonitorPage
{...props}
pageRoute={RouteMap[PageMap.MONITORS] as Route}
/>
</Suspense>
<MonitorPage
{...props}
pageRoute={RouteMap[PageMap.MONITORS] as Route}
/>
}
/>
<PageRoute
path={MonitorsRoutePath[PageMap.MONITORS_DISABLED] || ""}
element={
<Suspense fallback={Loader}>
<MonitorDisabled
{...props}
pageRoute={RouteMap[PageMap.MONITORS_DISABLED] as Route}
/>
</Suspense>
<MonitorDisabled
{...props}
pageRoute={RouteMap[PageMap.MONITORS_DISABLED] as Route}
/>
}
/>
<PageRoute
path={MonitorsRoutePath[PageMap.MONITORS_PROBE_DISCONNECTED] || ""}
element={
<Suspense fallback={Loader}>
<MonitorprobeDisconnected
{...props}
pageRoute={
RouteMap[PageMap.MONITORS_PROBE_DISCONNECTED] as Route
}
/>
</Suspense>
<MonitorprobeDisconnected
{...props}
pageRoute={RouteMap[PageMap.MONITORS_PROBE_DISCONNECTED] as Route}
/>
}
/>
<PageRoute
path={MonitorsRoutePath[PageMap.MONITORS_PROBE_DISABLED] || ""}
element={
<Suspense fallback={Loader}>
<MonitorProbeDisabled
{...props}
pageRoute={RouteMap[PageMap.MONITORS_PROBE_DISABLED] as Route}
/>
</Suspense>
<MonitorProbeDisabled
{...props}
pageRoute={RouteMap[PageMap.MONITORS_PROBE_DISABLED] as Route}
/>
}
/>
<PageRoute
path={MonitorsRoutePath[PageMap.MONITORS_INOPERATIONAL] || ""}
element={
<Suspense fallback={Loader}>
<MonitorInoperational
{...props}
pageRoute={RouteMap[PageMap.MONITORS_INOPERATIONAL] as Route}
/>
</Suspense>
<MonitorInoperational
{...props}
pageRoute={RouteMap[PageMap.MONITORS_INOPERATIONAL] as Route}
/>
}
/>
@@ -237,14 +122,12 @@ const MonitorRoutes: FunctionComponent<ComponentProps> = (
MonitorsRoutePath[PageMap.MONITORS_WORKSPACE_CONNECTION_SLACK] || ""
}
element={
<Suspense fallback={Loader}>
<WorkspaceConnectionSlack
{...props}
pageRoute={
RouteMap[PageMap.MONITORS_WORKSPACE_CONNECTION_SLACK] as Route
}
/>
</Suspense>
<WorkspaceConnectionSlack
{...props}
pageRoute={
RouteMap[PageMap.MONITORS_WORKSPACE_CONNECTION_SLACK] as Route
}
/>
}
/>
@@ -255,28 +138,24 @@ const MonitorRoutes: FunctionComponent<ComponentProps> = (
] || ""
}
element={
<Suspense fallback={Loader}>
<WorkspaceConnectionTeams
{...props}
pageRoute={
RouteMap[
PageMap.MONITORS_WORKSPACE_CONNECTION_MICROSOFT_TEAMS
] as Route
}
/>
</Suspense>
<WorkspaceConnectionTeams
{...props}
pageRoute={
RouteMap[
PageMap.MONITORS_WORKSPACE_CONNECTION_MICROSOFT_TEAMS
] as Route
}
/>
}
/>
<PageRoute
path={MonitorsRoutePath[PageMap.MONITOR_CREATE] || ""}
element={
<Suspense fallback={Loader}>
<MonitorCreate
{...props}
pageRoute={RouteMap[PageMap.MONITOR_CREATE] as Route}
/>
</Suspense>
<MonitorCreate
{...props}
pageRoute={RouteMap[PageMap.MONITOR_CREATE] as Route}
/>
}
/>
@@ -284,12 +163,10 @@ const MonitorRoutes: FunctionComponent<ComponentProps> = (
<PageRoute
path={MonitorsRoutePath[PageMap.MONITORS_SETTINGS] || ""}
element={
<Suspense fallback={Loader}>
<MonitorSettingsStatus
{...props}
pageRoute={RouteMap[PageMap.MONITORS_SETTINGS] as Route}
/>
</Suspense>
<MonitorSettingsStatus
{...props}
pageRoute={RouteMap[PageMap.MONITORS_SETTINGS] as Route}
/>
}
/>
@@ -298,26 +175,22 @@ const MonitorRoutes: FunctionComponent<ComponentProps> = (
MonitorsRoutePath[PageMap.MONITORS_SETTINGS_CUSTOM_FIELDS] || ""
}
element={
<Suspense fallback={Loader}>
<MonitorSettingsCustomFields
{...props}
pageRoute={
RouteMap[PageMap.MONITORS_SETTINGS_CUSTOM_FIELDS] as Route
}
/>
</Suspense>
<MonitorSettingsCustomFields
{...props}
pageRoute={
RouteMap[PageMap.MONITORS_SETTINGS_CUSTOM_FIELDS] as Route
}
/>
}
/>
<PageRoute
path={MonitorsRoutePath[PageMap.MONITORS_SETTINGS_SECRETS] || ""}
element={
<Suspense fallback={Loader}>
<MonitorSettingsSecrets
{...props}
pageRoute={RouteMap[PageMap.MONITORS_SETTINGS_SECRETS] as Route}
/>
</Suspense>
<MonitorSettingsSecrets
{...props}
pageRoute={RouteMap[PageMap.MONITORS_SETTINGS_SECRETS] as Route}
/>
}
/>
</PageRoute>
@@ -329,83 +202,67 @@ const MonitorRoutes: FunctionComponent<ComponentProps> = (
<PageRoute
index
element={
<Suspense fallback={Loader}>
<MonitorView
{...props}
pageRoute={RouteMap[PageMap.MONITOR_VIEW] as Route}
/>
</Suspense>
<MonitorView
{...props}
pageRoute={RouteMap[PageMap.MONITOR_VIEW] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.MONITOR_VIEW_SETTINGS)}
element={
<Suspense fallback={Loader}>
<MonitorViewSettings
{...props}
pageRoute={RouteMap[PageMap.MONITOR_VIEW_SETTINGS] as Route}
/>
</Suspense>
<MonitorViewSettings
{...props}
pageRoute={RouteMap[PageMap.MONITOR_VIEW_SETTINGS] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.MONITOR_VIEW_OWNERS)}
element={
<Suspense fallback={Loader}>
<MonitorViewOwner
{...props}
pageRoute={RouteMap[PageMap.MONITOR_VIEW_OWNERS] as Route}
/>
</Suspense>
<MonitorViewOwner
{...props}
pageRoute={RouteMap[PageMap.MONITOR_VIEW_OWNERS] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.MONITOR_VIEW_CRITERIA)}
element={
<Suspense fallback={Loader}>
<MonitorViewCriteria
{...props}
pageRoute={RouteMap[PageMap.MONITOR_VIEW_CRITERIA] as Route}
/>
</Suspense>
<MonitorViewCriteria
{...props}
pageRoute={RouteMap[PageMap.MONITOR_VIEW_CRITERIA] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.MONITOR_VIEW_METRICS)}
element={
<Suspense fallback={Loader}>
<MonitorViewMetrics
{...props}
pageRoute={RouteMap[PageMap.MONITOR_VIEW_METRICS] as Route}
/>
</Suspense>
<MonitorViewMetrics
{...props}
pageRoute={RouteMap[PageMap.MONITOR_VIEW_METRICS] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.MONITOR_VIEW_INTERVAL)}
element={
<Suspense fallback={Loader}>
<MonitorViewInterval
{...props}
pageRoute={RouteMap[PageMap.MONITOR_VIEW_INTERVAL] as Route}
/>
</Suspense>
<MonitorViewInterval
{...props}
pageRoute={RouteMap[PageMap.MONITOR_VIEW_INTERVAL] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.MONITOR_VIEW_DOCUMENTATION)}
element={
<Suspense fallback={Loader}>
<MonitorViewDocumentation
{...props}
pageRoute={
RouteMap[PageMap.MONITOR_VIEW_DOCUMENTATION] as Route
}
/>
</Suspense>
<MonitorViewDocumentation
{...props}
pageRoute={RouteMap[PageMap.MONITOR_VIEW_DOCUMENTATION] as Route}
/>
}
/>
@@ -414,88 +271,72 @@ const MonitorRoutes: FunctionComponent<ComponentProps> = (
PageMap.MONITOR_VIEW_STATUS_TIMELINE,
)}
element={
<Suspense fallback={Loader}>
<MonitorViewStatusTimeline
{...props}
pageRoute={
RouteMap[PageMap.MONITOR_VIEW_STATUS_TIMELINE] as Route
}
/>
</Suspense>
<MonitorViewStatusTimeline
{...props}
pageRoute={
RouteMap[PageMap.MONITOR_VIEW_STATUS_TIMELINE] as Route
}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.MONITOR_VIEW_INCIDENTS)}
element={
<Suspense fallback={Loader}>
<MonitorIncidents
{...props}
pageRoute={RouteMap[PageMap.MONITOR_VIEW_INCIDENTS] as Route}
/>
</Suspense>
<MonitorIncidents
{...props}
pageRoute={RouteMap[PageMap.MONITOR_VIEW_INCIDENTS] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.MONITOR_VIEW_ALERTS)}
element={
<Suspense fallback={Loader}>
<MonitorAlerts
{...props}
pageRoute={RouteMap[PageMap.MONITOR_VIEW_ALERTS] as Route}
/>
</Suspense>
<MonitorAlerts
{...props}
pageRoute={RouteMap[PageMap.MONITOR_VIEW_ALERTS] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.MONITOR_VIEW_DELETE)}
element={
<Suspense fallback={Loader}>
<MonitorViewDelete
{...props}
pageRoute={RouteMap[PageMap.MONITOR_VIEW_DELETE] as Route}
/>
</Suspense>
<MonitorViewDelete
{...props}
pageRoute={RouteMap[PageMap.MONITOR_VIEW_DELETE] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.MONITOR_VIEW_CUSTOM_FIELDS)}
element={
<Suspense fallback={Loader}>
<MonitorViewCustomFields
{...props}
pageRoute={
RouteMap[PageMap.MONITOR_VIEW_CUSTOM_FIELDS] as Route
}
/>
</Suspense>
<MonitorViewCustomFields
{...props}
pageRoute={RouteMap[PageMap.MONITOR_VIEW_CUSTOM_FIELDS] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.MONITOR_VIEW_PROBES)}
element={
<Suspense fallback={Loader}>
<MonitorViewProbes
{...props}
pageRoute={RouteMap[PageMap.MONITOR_VIEW_PROBES] as Route}
/>
</Suspense>
<MonitorViewProbes
{...props}
pageRoute={RouteMap[PageMap.MONITOR_VIEW_PROBES] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.MONITOR_VIEW_LOGS)}
element={
<Suspense fallback={Loader}>
<MonitorViewLogs
{...props}
pageRoute={RouteMap[PageMap.MONITOR_VIEW_LOGS] as Route}
/>
</Suspense>
<MonitorViewLogs
{...props}
pageRoute={RouteMap[PageMap.MONITOR_VIEW_LOGS] as Route}
/>
}
/>
@@ -504,14 +345,12 @@ const MonitorRoutes: FunctionComponent<ComponentProps> = (
PageMap.MONITOR_VIEW_NOTIFICATION_LOGS,
)}
element={
<Suspense fallback={Loader}>
<MonitorViewNotificationLogs
{...props}
pageRoute={
RouteMap[PageMap.MONITOR_VIEW_NOTIFICATION_LOGS] as Route
}
/>
</Suspense>
<MonitorViewNotificationLogs
{...props}
pageRoute={
RouteMap[PageMap.MONITOR_VIEW_NOTIFICATION_LOGS] as Route
}
/>
}
/>
</PageRoute>

View File

@@ -1,4 +1,3 @@
import Loader from "../Components/Loader/Loader";
import OnCallDutyLayout from "../Pages/OnCallDuty/Layout";
import OnCallDutyPolicyViewLayout from "../Pages/OnCallDuty/OnCallDutyPolicy/Layout";
import OnCallDutyScheduleViewLayout from "../Pages/OnCallDuty/OnCallDutySchedule/Layout";
@@ -6,201 +5,67 @@ import ComponentProps from "../Pages/PageComponentProps";
import PageMap from "../Utils/PageMap";
import RouteMap, { OnCallDutyRoutePath, RouteUtil } from "../Utils/RouteMap";
import Route from "Common/Types/API/Route";
import React, {
FunctionComponent,
LazyExoticComponent,
ReactElement,
Suspense,
lazy,
} from "react";
import React, { FunctionComponent, ReactElement } from "react";
import { Route as PageRoute, Routes } from "react-router-dom";
// Polcies
const OnCallDutyPoliciesPage: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/OnCallDuty/OnCallDutyPolicies");
});
const OnCallDutyExecutionLogs: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/OnCallDuty/OnCallDutyExecutionLogs");
});
import OnCallDutyPoliciesPage from "../Pages/OnCallDuty/OnCallDutyPolicies";
import OnCallDutyExecutionLogs from "../Pages/OnCallDuty/OnCallDutyExecutionLogs";
const OnCallDutyUserOverrides: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/OnCallDuty/UserOverrides");
});
import OnCallDutyUserOverrides from "../Pages/OnCallDuty/UserOverrides";
const OnCallDutyPolicyViewUserOverrides: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/OnCallDuty/OnCallDutyPolicy/UserOverrides");
});
const OnCallDutyPolicyExecutionLogTimeline: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/OnCallDuty/OnCallDutyExecutionLogView");
});
const OnCallDutyPolicyView: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/OnCallDuty/OnCallDutyPolicy/Index");
});
const OnCallDutyPolicyViewOwners: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/OnCallDuty/OnCallDutyPolicy/Owners");
});
import OnCallDutyPolicyViewUserOverrides from "../Pages/OnCallDuty/OnCallDutyPolicy/UserOverrides";
import OnCallDutyPolicyExecutionLogTimeline from "../Pages/OnCallDuty/OnCallDutyExecutionLogView";
import OnCallDutyPolicyView from "../Pages/OnCallDuty/OnCallDutyPolicy/Index";
import OnCallDutyPolicyViewOwners from "../Pages/OnCallDuty/OnCallDutyPolicy/Owners";
const OnCallDutyPolicyViewDelete: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/OnCallDuty/OnCallDutyPolicy/Delete");
});
const OnCallDutyPolicyViewLogs: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/OnCallDuty/OnCallDutyPolicy/ExecutionLogs");
});
const OnCallDutyPolicyViewLogsView: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/OnCallDuty/OnCallDutyPolicy/ExecutionLogView");
});
import OnCallDutyPolicyViewDelete from "../Pages/OnCallDuty/OnCallDutyPolicy/Delete";
import OnCallDutyPolicyViewLogs from "../Pages/OnCallDuty/OnCallDutyPolicy/ExecutionLogs";
import OnCallDutyPolicyViewLogsView from "../Pages/OnCallDuty/OnCallDutyPolicy/ExecutionLogView";
const OnCallDutyPolicyViewNotificationLogs: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/OnCallDuty/OnCallDutyPolicy/NotificationLogs");
});
const OnCallDutyPolicyViewEscalation: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/OnCallDuty/OnCallDutyPolicy/Escalation");
});
const OnCallDutyPolicyViewCustomFields: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/OnCallDuty/OnCallDutyPolicy/CustomFields");
});
import OnCallDutyPolicyViewNotificationLogs from "../Pages/OnCallDuty/OnCallDutyPolicy/NotificationLogs";
import OnCallDutyPolicyViewEscalation from "../Pages/OnCallDuty/OnCallDutyPolicy/Escalation";
import OnCallDutyPolicyViewCustomFields from "../Pages/OnCallDuty/OnCallDutyPolicy/CustomFields";
// Schedules
const OnCallDutySchedules: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/OnCallDuty/OnCallDutySchedules");
});
const OnCallDutyScheduleView: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/OnCallDuty/OnCallDutySchedule/Index");
});
const OnCallDutyScheduleViewDelete: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/OnCallDuty/OnCallDutySchedule/Delete");
});
const OnCallDutyScheduleViewLayers: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/OnCallDuty/OnCallDutySchedule/Layers");
});
import OnCallDutySchedules from "../Pages/OnCallDuty/OnCallDutySchedules";
import OnCallDutyScheduleView from "../Pages/OnCallDuty/OnCallDutySchedule/Index";
import OnCallDutyScheduleViewDelete from "../Pages/OnCallDuty/OnCallDutySchedule/Delete";
import OnCallDutyScheduleViewLayers from "../Pages/OnCallDuty/OnCallDutySchedule/Layers";
const OnCallDutyScheduleViewNotificationLogs: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/OnCallDuty/OnCallDutySchedule/NotificationLogs");
});
const OnCallDutyScheduleViewSettings: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/OnCallDuty/OnCallDutySchedule/Settings");
});
import OnCallDutyScheduleViewNotificationLogs from "../Pages/OnCallDuty/OnCallDutySchedule/NotificationLogs";
import OnCallDutyScheduleViewSettings from "../Pages/OnCallDuty/OnCallDutySchedule/Settings";
// slack
const WorkspaceConnectionSlack: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/OnCallDuty/WorkspaceConnectionSlack");
});
import WorkspaceConnectionSlack from "../Pages/OnCallDuty/WorkspaceConnectionSlack";
// Microsoft Teams
const WorkspaceConnectionTeams: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/OnCallDuty/WorkspaceConnectionMicrosoftTeams");
});
import WorkspaceConnectionTeams from "../Pages/OnCallDuty/WorkspaceConnectionMicrosoftTeams";
// User Time Logs
const OnCallDutyUserTimeLogs: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/OnCallDuty/OnCallDutyUserTimeLogs");
});
import OnCallDutyUserTimeLogs from "../Pages/OnCallDuty/OnCallDutyUserTimeLogs";
// Settings Pages
const OnCallDutySettingsCustomFields: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/OnCallDuty/Settings/OnCallDutyPolicyCustomFields");
});
import OnCallDutySettingsCustomFields from "../Pages/OnCallDuty/Settings/OnCallDutyPolicyCustomFields";
// Incoming Call Policies
const IncomingCallPoliciesPage: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/OnCallDuty/IncomingCallPolicies");
});
import IncomingCallPoliciesPage from "../Pages/OnCallDuty/IncomingCallPolicies";
const IncomingCallPolicyViewLayout: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/OnCallDuty/IncomingCallPolicy/Layout");
});
import IncomingCallPolicyViewLayout from "../Pages/OnCallDuty/IncomingCallPolicy/Layout";
const IncomingCallPolicyView: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/OnCallDuty/IncomingCallPolicy/Index");
});
import IncomingCallPolicyView from "../Pages/OnCallDuty/IncomingCallPolicy/Index";
const IncomingCallPolicyViewEscalation: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/OnCallDuty/IncomingCallPolicy/Escalation");
});
import IncomingCallPolicyViewEscalation from "../Pages/OnCallDuty/IncomingCallPolicy/Escalation";
const IncomingCallPolicyViewLogs: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/OnCallDuty/IncomingCallPolicy/Logs");
});
import IncomingCallPolicyViewLogs from "../Pages/OnCallDuty/IncomingCallPolicy/Logs";
const IncomingCallPolicyViewLogView: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/OnCallDuty/IncomingCallPolicy/LogView");
});
import IncomingCallPolicyViewLogView from "../Pages/OnCallDuty/IncomingCallPolicy/LogView";
const IncomingCallPolicyViewDelete: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/OnCallDuty/IncomingCallPolicy/Delete");
});
import IncomingCallPolicyViewDelete from "../Pages/OnCallDuty/IncomingCallPolicy/Delete";
const IncomingCallPolicyViewSettings: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/OnCallDuty/IncomingCallPolicy/Settings");
});
import IncomingCallPolicyViewSettings from "../Pages/OnCallDuty/IncomingCallPolicy/Settings";
const IncomingCallPolicyViewDocs: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/OnCallDuty/IncomingCallPolicy/Docs");
});
import IncomingCallPolicyViewDocs from "../Pages/OnCallDuty/IncomingCallPolicy/Docs";
const OnCallDutyRoutes: FunctionComponent<ComponentProps> = (
props: ComponentProps,
@@ -211,12 +76,10 @@ const OnCallDutyRoutes: FunctionComponent<ComponentProps> = (
<PageRoute
path={OnCallDutyRoutePath[PageMap.ON_CALL_DUTY] || ""}
element={
<Suspense fallback={Loader}>
<OnCallDutyPoliciesPage
{...props}
pageRoute={RouteMap[PageMap.ON_CALL_DUTY] as Route}
/>
</Suspense>
<OnCallDutyPoliciesPage
{...props}
pageRoute={RouteMap[PageMap.ON_CALL_DUTY] as Route}
/>
}
/>
@@ -227,16 +90,14 @@ const OnCallDutyRoutes: FunctionComponent<ComponentProps> = (
] || ""
}
element={
<Suspense fallback={Loader}>
<WorkspaceConnectionSlack
{...props}
pageRoute={
RouteMap[
PageMap.ON_CALL_DUTY_WORKSPACE_CONNECTION_SLACK
] as Route
}
/>
</Suspense>
<WorkspaceConnectionSlack
{...props}
pageRoute={
RouteMap[
PageMap.ON_CALL_DUTY_WORKSPACE_CONNECTION_SLACK
] as Route
}
/>
}
/>
@@ -247,41 +108,33 @@ const OnCallDutyRoutes: FunctionComponent<ComponentProps> = (
] || ""
}
element={
<Suspense fallback={Loader}>
<WorkspaceConnectionTeams
{...props}
pageRoute={
RouteMap[
PageMap.ON_CALL_DUTY_WORKSPACE_CONNECTION_MICROSOFT_TEAMS
] as Route
}
/>
</Suspense>
<WorkspaceConnectionTeams
{...props}
pageRoute={
RouteMap[
PageMap.ON_CALL_DUTY_WORKSPACE_CONNECTION_MICROSOFT_TEAMS
] as Route
}
/>
}
/>
<PageRoute
path={OnCallDutyRoutePath[PageMap.ON_CALL_DUTY_POLICIES] || ""}
element={
<Suspense fallback={Loader}>
<OnCallDutyPoliciesPage
{...props}
pageRoute={RouteMap[PageMap.ON_CALL_DUTY_POLICIES] as Route}
/>
</Suspense>
<OnCallDutyPoliciesPage
{...props}
pageRoute={RouteMap[PageMap.ON_CALL_DUTY_POLICIES] as Route}
/>
}
/>
<PageRoute
path={OnCallDutyRoutePath[PageMap.ON_CALL_DUTY_EXECUTION_LOGS] || ""}
element={
<Suspense fallback={Loader}>
<OnCallDutyExecutionLogs
{...props}
pageRoute={
RouteMap[PageMap.ON_CALL_DUTY_EXECUTION_LOGS] as Route
}
/>
</Suspense>
<OnCallDutyExecutionLogs
{...props}
pageRoute={RouteMap[PageMap.ON_CALL_DUTY_EXECUTION_LOGS] as Route}
/>
}
/>
@@ -291,14 +144,12 @@ const OnCallDutyRoutes: FunctionComponent<ComponentProps> = (
""
}
element={
<Suspense fallback={Loader}>
<OnCallDutyUserOverrides
{...props}
pageRoute={
RouteMap[PageMap.ON_CALL_DUTY_POLICY_USER_OVERRIDES] as Route
}
/>
</Suspense>
<OnCallDutyUserOverrides
{...props}
pageRoute={
RouteMap[PageMap.ON_CALL_DUTY_POLICY_USER_OVERRIDES] as Route
}
/>
}
/>
@@ -308,42 +159,32 @@ const OnCallDutyRoutes: FunctionComponent<ComponentProps> = (
""
}
element={
<Suspense fallback={Loader}>
<OnCallDutyPolicyExecutionLogTimeline
{...props}
pageRoute={
RouteMap[
PageMap.ON_CALL_DUTY_EXECUTION_LOGS_TIMELINE
] as Route
}
/>
</Suspense>
<OnCallDutyPolicyExecutionLogTimeline
{...props}
pageRoute={
RouteMap[PageMap.ON_CALL_DUTY_EXECUTION_LOGS_TIMELINE] as Route
}
/>
}
/>
<PageRoute
path={OnCallDutyRoutePath[PageMap.ON_CALL_DUTY_SCHEDULES] || ""}
element={
<Suspense fallback={Loader}>
<OnCallDutySchedules
{...props}
pageRoute={RouteMap[PageMap.ON_CALL_DUTY_SCHEDULES] as Route}
/>
</Suspense>
<OnCallDutySchedules
{...props}
pageRoute={RouteMap[PageMap.ON_CALL_DUTY_SCHEDULES] as Route}
/>
}
/>
<PageRoute
path={OnCallDutyRoutePath[PageMap.ON_CALLDUTY_USER_TIME_LOGS] || ""}
element={
<Suspense fallback={Loader}>
<OnCallDutyUserTimeLogs
{...props}
pageRoute={
RouteMap[PageMap.ON_CALLDUTY_USER_TIME_LOGS] as Route
}
/>
</Suspense>
<OnCallDutyUserTimeLogs
{...props}
pageRoute={RouteMap[PageMap.ON_CALLDUTY_USER_TIME_LOGS] as Route}
/>
}
/>
@@ -354,14 +195,12 @@ const OnCallDutyRoutes: FunctionComponent<ComponentProps> = (
""
}
element={
<Suspense fallback={Loader}>
<OnCallDutySettingsCustomFields
{...props}
pageRoute={
RouteMap[PageMap.ON_CALL_DUTY_SETTINGS_CUSTOM_FIELDS] as Route
}
/>
</Suspense>
<OnCallDutySettingsCustomFields
{...props}
pageRoute={
RouteMap[PageMap.ON_CALL_DUTY_SETTINGS_CUSTOM_FIELDS] as Route
}
/>
}
/>
@@ -372,14 +211,12 @@ const OnCallDutyRoutes: FunctionComponent<ComponentProps> = (
""
}
element={
<Suspense fallback={Loader}>
<IncomingCallPoliciesPage
{...props}
pageRoute={
RouteMap[PageMap.ON_CALL_DUTY_INCOMING_CALL_POLICIES] as Route
}
/>
</Suspense>
<IncomingCallPoliciesPage
{...props}
pageRoute={
RouteMap[PageMap.ON_CALL_DUTY_INCOMING_CALL_POLICIES] as Route
}
/>
}
/>
</PageRoute>
@@ -391,12 +228,10 @@ const OnCallDutyRoutes: FunctionComponent<ComponentProps> = (
<PageRoute
index
element={
<Suspense fallback={Loader}>
<OnCallDutyPolicyView
{...props}
pageRoute={RouteMap[PageMap.ON_CALL_DUTY_POLICY_VIEW] as Route}
/>
</Suspense>
<OnCallDutyPolicyView
{...props}
pageRoute={RouteMap[PageMap.ON_CALL_DUTY_POLICY_VIEW] as Route}
/>
}
/>
@@ -405,14 +240,12 @@ const OnCallDutyRoutes: FunctionComponent<ComponentProps> = (
PageMap.ON_CALL_DUTY_POLICY_VIEW_DELETE,
)}
element={
<Suspense fallback={Loader}>
<OnCallDutyPolicyViewDelete
{...props}
pageRoute={
RouteMap[PageMap.ON_CALL_DUTY_POLICY_VIEW_DELETE] as Route
}
/>
</Suspense>
<OnCallDutyPolicyViewDelete
{...props}
pageRoute={
RouteMap[PageMap.ON_CALL_DUTY_POLICY_VIEW_DELETE] as Route
}
/>
}
/>
@@ -421,14 +254,12 @@ const OnCallDutyRoutes: FunctionComponent<ComponentProps> = (
PageMap.ON_CALL_DUTY_POLICY_VIEW_OWNERS,
)}
element={
<Suspense fallback={Loader}>
<OnCallDutyPolicyViewOwners
{...props}
pageRoute={
RouteMap[PageMap.ON_CALL_DUTY_POLICY_VIEW_OWNERS] as Route
}
/>
</Suspense>
<OnCallDutyPolicyViewOwners
{...props}
pageRoute={
RouteMap[PageMap.ON_CALL_DUTY_POLICY_VIEW_OWNERS] as Route
}
/>
}
/>
@@ -437,14 +268,12 @@ const OnCallDutyRoutes: FunctionComponent<ComponentProps> = (
PageMap.ON_CALL_DUTY_POLICY_VIEW_ESCALATION,
)}
element={
<Suspense fallback={Loader}>
<OnCallDutyPolicyViewEscalation
{...props}
pageRoute={
RouteMap[PageMap.ON_CALL_DUTY_POLICY_VIEW_ESCALATION] as Route
}
/>
</Suspense>
<OnCallDutyPolicyViewEscalation
{...props}
pageRoute={
RouteMap[PageMap.ON_CALL_DUTY_POLICY_VIEW_ESCALATION] as Route
}
/>
}
/>
@@ -453,16 +282,14 @@ const OnCallDutyRoutes: FunctionComponent<ComponentProps> = (
PageMap.ON_CALL_DUTY_POLICY_VIEW_USER_OVERRIDES,
)}
element={
<Suspense fallback={Loader}>
<OnCallDutyPolicyViewUserOverrides
{...props}
pageRoute={
RouteMap[
PageMap.ON_CALL_DUTY_POLICY_VIEW_USER_OVERRIDES
] as Route
}
/>
</Suspense>
<OnCallDutyPolicyViewUserOverrides
{...props}
pageRoute={
RouteMap[
PageMap.ON_CALL_DUTY_POLICY_VIEW_USER_OVERRIDES
] as Route
}
/>
}
/>
@@ -471,16 +298,14 @@ const OnCallDutyRoutes: FunctionComponent<ComponentProps> = (
PageMap.ON_CALL_DUTY_POLICY_VIEW_CUSTOM_FIELDS,
)}
element={
<Suspense fallback={Loader}>
<OnCallDutyPolicyViewCustomFields
{...props}
pageRoute={
RouteMap[
PageMap.ON_CALL_DUTY_POLICY_VIEW_CUSTOM_FIELDS
] as Route
}
/>
</Suspense>
<OnCallDutyPolicyViewCustomFields
{...props}
pageRoute={
RouteMap[
PageMap.ON_CALL_DUTY_POLICY_VIEW_CUSTOM_FIELDS
] as Route
}
/>
}
/>
@@ -489,16 +314,14 @@ const OnCallDutyRoutes: FunctionComponent<ComponentProps> = (
PageMap.ON_CALL_DUTY_POLICY_VIEW_EXECUTION_LOGS,
)}
element={
<Suspense fallback={Loader}>
<OnCallDutyPolicyViewLogs
{...props}
pageRoute={
RouteMap[
PageMap.ON_CALL_DUTY_POLICY_VIEW_EXECUTION_LOGS
] as Route
}
/>
</Suspense>
<OnCallDutyPolicyViewLogs
{...props}
pageRoute={
RouteMap[
PageMap.ON_CALL_DUTY_POLICY_VIEW_EXECUTION_LOGS
] as Route
}
/>
}
/>
@@ -508,16 +331,14 @@ const OnCallDutyRoutes: FunctionComponent<ComponentProps> = (
2,
)}
element={
<Suspense fallback={Loader}>
<OnCallDutyPolicyViewLogsView
{...props}
pageRoute={
RouteMap[
PageMap.ON_CALL_DUTY_POLICY_VIEW_EXECUTION_LOG_VIEW
] as Route
}
/>
</Suspense>
<OnCallDutyPolicyViewLogsView
{...props}
pageRoute={
RouteMap[
PageMap.ON_CALL_DUTY_POLICY_VIEW_EXECUTION_LOG_VIEW
] as Route
}
/>
}
/>
@@ -526,16 +347,14 @@ const OnCallDutyRoutes: FunctionComponent<ComponentProps> = (
PageMap.ON_CALL_DUTY_POLICY_VIEW_NOTIFICATION_LOGS,
)}
element={
<Suspense fallback={Loader}>
<OnCallDutyPolicyViewNotificationLogs
{...props}
pageRoute={
RouteMap[
PageMap.ON_CALL_DUTY_POLICY_VIEW_NOTIFICATION_LOGS
] as Route
}
/>
</Suspense>
<OnCallDutyPolicyViewNotificationLogs
{...props}
pageRoute={
RouteMap[
PageMap.ON_CALL_DUTY_POLICY_VIEW_NOTIFICATION_LOGS
] as Route
}
/>
}
/>
</PageRoute>
@@ -546,14 +365,10 @@ const OnCallDutyRoutes: FunctionComponent<ComponentProps> = (
<PageRoute
index
element={
<Suspense fallback={Loader}>
<OnCallDutyScheduleView
{...props}
pageRoute={
RouteMap[PageMap.ON_CALL_DUTY_SCHEDULE_VIEW] as Route
}
/>
</Suspense>
<OnCallDutyScheduleView
{...props}
pageRoute={RouteMap[PageMap.ON_CALL_DUTY_SCHEDULE_VIEW] as Route}
/>
}
/>
@@ -562,14 +377,12 @@ const OnCallDutyRoutes: FunctionComponent<ComponentProps> = (
PageMap.ON_CALL_DUTY_SCHEDULE_VIEW_DELETE,
)}
element={
<Suspense fallback={Loader}>
<OnCallDutyScheduleViewDelete
{...props}
pageRoute={
RouteMap[PageMap.ON_CALL_DUTY_SCHEDULE_VIEW_DELETE] as Route
}
/>
</Suspense>
<OnCallDutyScheduleViewDelete
{...props}
pageRoute={
RouteMap[PageMap.ON_CALL_DUTY_SCHEDULE_VIEW_DELETE] as Route
}
/>
}
/>
@@ -578,14 +391,12 @@ const OnCallDutyRoutes: FunctionComponent<ComponentProps> = (
PageMap.ON_CALL_DUTY_SCHEDULE_VIEW_LAYERS,
)}
element={
<Suspense fallback={Loader}>
<OnCallDutyScheduleViewLayers
{...props}
pageRoute={
RouteMap[PageMap.ON_CALL_DUTY_SCHEDULE_VIEW_LAYERS] as Route
}
/>
</Suspense>
<OnCallDutyScheduleViewLayers
{...props}
pageRoute={
RouteMap[PageMap.ON_CALL_DUTY_SCHEDULE_VIEW_LAYERS] as Route
}
/>
}
/>
@@ -594,16 +405,14 @@ const OnCallDutyRoutes: FunctionComponent<ComponentProps> = (
PageMap.ON_CALL_DUTY_SCHEDULE_VIEW_NOTIFICATION_LOGS,
)}
element={
<Suspense fallback={Loader}>
<OnCallDutyScheduleViewNotificationLogs
{...props}
pageRoute={
RouteMap[
PageMap.ON_CALL_DUTY_SCHEDULE_VIEW_NOTIFICATION_LOGS
] as Route
}
/>
</Suspense>
<OnCallDutyScheduleViewNotificationLogs
{...props}
pageRoute={
RouteMap[
PageMap.ON_CALL_DUTY_SCHEDULE_VIEW_NOTIFICATION_LOGS
] as Route
}
/>
}
/>
@@ -612,14 +421,12 @@ const OnCallDutyRoutes: FunctionComponent<ComponentProps> = (
PageMap.ON_CALL_DUTY_SCHEDULE_VIEW_SETTINGS,
)}
element={
<Suspense fallback={Loader}>
<OnCallDutyScheduleViewSettings
{...props}
pageRoute={
RouteMap[PageMap.ON_CALL_DUTY_SCHEDULE_VIEW_SETTINGS] as Route
}
/>
</Suspense>
<OnCallDutyScheduleViewSettings
{...props}
pageRoute={
RouteMap[PageMap.ON_CALL_DUTY_SCHEDULE_VIEW_SETTINGS] as Route
}
/>
}
/>
</PageRoute>
@@ -630,25 +437,19 @@ const OnCallDutyRoutes: FunctionComponent<ComponentProps> = (
OnCallDutyRoutePath[PageMap.ON_CALL_DUTY_INCOMING_CALL_POLICY_VIEW] ||
""
}
element={
<Suspense fallback={Loader}>
<IncomingCallPolicyViewLayout {...props} />
</Suspense>
}
element={<IncomingCallPolicyViewLayout {...props} />}
>
<PageRoute
index
element={
<Suspense fallback={Loader}>
<IncomingCallPolicyView
{...props}
pageRoute={
RouteMap[
PageMap.ON_CALL_DUTY_INCOMING_CALL_POLICY_VIEW
] as Route
}
/>
</Suspense>
<IncomingCallPolicyView
{...props}
pageRoute={
RouteMap[
PageMap.ON_CALL_DUTY_INCOMING_CALL_POLICY_VIEW
] as Route
}
/>
}
/>
@@ -657,16 +458,14 @@ const OnCallDutyRoutes: FunctionComponent<ComponentProps> = (
PageMap.ON_CALL_DUTY_INCOMING_CALL_POLICY_VIEW_ESCALATION,
)}
element={
<Suspense fallback={Loader}>
<IncomingCallPolicyViewEscalation
{...props}
pageRoute={
RouteMap[
PageMap.ON_CALL_DUTY_INCOMING_CALL_POLICY_VIEW_ESCALATION
] as Route
}
/>
</Suspense>
<IncomingCallPolicyViewEscalation
{...props}
pageRoute={
RouteMap[
PageMap.ON_CALL_DUTY_INCOMING_CALL_POLICY_VIEW_ESCALATION
] as Route
}
/>
}
/>
@@ -675,16 +474,14 @@ const OnCallDutyRoutes: FunctionComponent<ComponentProps> = (
PageMap.ON_CALL_DUTY_INCOMING_CALL_POLICY_VIEW_LOGS,
)}
element={
<Suspense fallback={Loader}>
<IncomingCallPolicyViewLogs
{...props}
pageRoute={
RouteMap[
PageMap.ON_CALL_DUTY_INCOMING_CALL_POLICY_VIEW_LOGS
] as Route
}
/>
</Suspense>
<IncomingCallPolicyViewLogs
{...props}
pageRoute={
RouteMap[
PageMap.ON_CALL_DUTY_INCOMING_CALL_POLICY_VIEW_LOGS
] as Route
}
/>
}
/>
@@ -694,16 +491,14 @@ const OnCallDutyRoutes: FunctionComponent<ComponentProps> = (
2,
)}
element={
<Suspense fallback={Loader}>
<IncomingCallPolicyViewLogView
{...props}
pageRoute={
RouteMap[
PageMap.ON_CALL_DUTY_INCOMING_CALL_POLICY_VIEW_LOG_VIEW
] as Route
}
/>
</Suspense>
<IncomingCallPolicyViewLogView
{...props}
pageRoute={
RouteMap[
PageMap.ON_CALL_DUTY_INCOMING_CALL_POLICY_VIEW_LOG_VIEW
] as Route
}
/>
}
/>
@@ -712,16 +507,14 @@ const OnCallDutyRoutes: FunctionComponent<ComponentProps> = (
PageMap.ON_CALL_DUTY_INCOMING_CALL_POLICY_VIEW_SETTINGS,
)}
element={
<Suspense fallback={Loader}>
<IncomingCallPolicyViewSettings
{...props}
pageRoute={
RouteMap[
PageMap.ON_CALL_DUTY_INCOMING_CALL_POLICY_VIEW_SETTINGS
] as Route
}
/>
</Suspense>
<IncomingCallPolicyViewSettings
{...props}
pageRoute={
RouteMap[
PageMap.ON_CALL_DUTY_INCOMING_CALL_POLICY_VIEW_SETTINGS
] as Route
}
/>
}
/>
@@ -730,16 +523,14 @@ const OnCallDutyRoutes: FunctionComponent<ComponentProps> = (
PageMap.ON_CALL_DUTY_INCOMING_CALL_POLICY_VIEW_DELETE,
)}
element={
<Suspense fallback={Loader}>
<IncomingCallPolicyViewDelete
{...props}
pageRoute={
RouteMap[
PageMap.ON_CALL_DUTY_INCOMING_CALL_POLICY_VIEW_DELETE
] as Route
}
/>
</Suspense>
<IncomingCallPolicyViewDelete
{...props}
pageRoute={
RouteMap[
PageMap.ON_CALL_DUTY_INCOMING_CALL_POLICY_VIEW_DELETE
] as Route
}
/>
}
/>
@@ -748,16 +539,14 @@ const OnCallDutyRoutes: FunctionComponent<ComponentProps> = (
PageMap.ON_CALL_DUTY_INCOMING_CALL_POLICY_VIEW_DOCS,
)}
element={
<Suspense fallback={Loader}>
<IncomingCallPolicyViewDocs
{...props}
pageRoute={
RouteMap[
PageMap.ON_CALL_DUTY_INCOMING_CALL_POLICY_VIEW_DOCS
] as Route
}
/>
</Suspense>
<IncomingCallPolicyViewDocs
{...props}
pageRoute={
RouteMap[
PageMap.ON_CALL_DUTY_INCOMING_CALL_POLICY_VIEW_DOCS
] as Route
}
/>
}
/>
</PageRoute>

View File

@@ -1,5 +1,4 @@
import Navigation from "Common/UI/Utils/Navigation";
import Loader from "../Components/Loader/Loader";
import ComponentProps from "../Pages/PageComponentProps";
import ScheduledMaintenancesLaoyut from "../Pages/ScheduledMaintenanceEvents/Layout";
import ScheduledMaintenanceViewLayout from "../Pages/ScheduledMaintenanceEvents/View/Layout";
@@ -9,167 +8,51 @@ import RouteMap, {
ScheduledMaintenanceEventsRoutePath,
} from "../Utils/RouteMap";
import Route from "Common/Types/API/Route";
import React, {
FunctionComponent,
LazyExoticComponent,
ReactElement,
Suspense,
lazy,
} from "react";
import React, { FunctionComponent, ReactElement } from "react";
import { Route as PageRoute, Routes } from "react-router-dom";
// Pages
const ScheduledMaintenanceEvents: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import(
"../Pages/ScheduledMaintenanceEvents/ScheduledMaintenanceEvents"
);
});
const ScheduledMaintenanceEventView: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/ScheduledMaintenanceEvents/View/Index");
});
const ScheduledMaintenanceEventViewDelete: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/ScheduledMaintenanceEvents/View/Delete");
});
import ScheduledMaintenanceEvents from "../Pages/ScheduledMaintenanceEvents/ScheduledMaintenanceEvents";
import ScheduledMaintenanceEventView from "../Pages/ScheduledMaintenanceEvents/View/Index";
import ScheduledMaintenanceEventViewDelete from "../Pages/ScheduledMaintenanceEvents/View/Delete";
const ScheduledMaintenanceEventsWorkspaceConnectionMicrosoftTeams: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import(
"../Pages/ScheduledMaintenanceEvents/WorkspaceConnectionMicrosoftTeams"
);
});
import ScheduledMaintenanceEventsWorkspaceConnectionMicrosoftTeams from "../Pages/ScheduledMaintenanceEvents/WorkspaceConnectionMicrosoftTeams";
const ScheduledMaintenanceEventViewOwner: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/ScheduledMaintenanceEvents/View/Owners");
});
import ScheduledMaintenanceEventViewOwner from "../Pages/ScheduledMaintenanceEvents/View/Owners";
const ScheduledMaintenanceEventsWorkspaceConnectionSlack: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/ScheduledMaintenanceEvents/WorkspaceConnectionSlack");
});
import ScheduledMaintenanceEventsWorkspaceConnectionSlack from "../Pages/ScheduledMaintenanceEvents/WorkspaceConnectionSlack";
const ScheduledMaintenanceEventsViewSettings: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/ScheduledMaintenanceEvents/View/Settings");
});
import ScheduledMaintenanceEventsViewSettings from "../Pages/ScheduledMaintenanceEvents/View/Settings";
const ScheduledMaintenanceEventViewStateTimeline: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/ScheduledMaintenanceEvents/View/StateTimeline");
});
const ScheduledMaintenanceEventInternalNote: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/ScheduledMaintenanceEvents/View/InternalNote");
});
const ScheduledMaintenanceEventPublicNote: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/ScheduledMaintenanceEvents/View/PublicNote");
});
const OngoingScheduledMaintenanceEvents: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/ScheduledMaintenanceEvents/Ongoing");
});
const ScheduledMaintenanceEventsViewCustomFields: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/ScheduledMaintenanceEvents/View/CustomFields");
});
import ScheduledMaintenanceEventViewStateTimeline from "../Pages/ScheduledMaintenanceEvents/View/StateTimeline";
import ScheduledMaintenanceEventInternalNote from "../Pages/ScheduledMaintenanceEvents/View/InternalNote";
import ScheduledMaintenanceEventPublicNote from "../Pages/ScheduledMaintenanceEvents/View/PublicNote";
import OngoingScheduledMaintenanceEvents from "../Pages/ScheduledMaintenanceEvents/Ongoing";
import ScheduledMaintenanceEventsViewCustomFields from "../Pages/ScheduledMaintenanceEvents/View/CustomFields";
const ScheduledMaintenanceEventViewNotificationLogs: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/ScheduledMaintenanceEvents/View/NotificationLogs");
});
import ScheduledMaintenanceEventViewNotificationLogs from "../Pages/ScheduledMaintenanceEvents/View/NotificationLogs";
const ScheduledMaintenanceEventViewAILogs: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/ScheduledMaintenanceEvents/View/AILogs");
});
import ScheduledMaintenanceEventViewAILogs from "../Pages/ScheduledMaintenanceEvents/View/AILogs";
const ScheduledMaintenanceEventViewDescription: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/ScheduledMaintenanceEvents/View/Description");
});
import ScheduledMaintenanceEventViewDescription from "../Pages/ScheduledMaintenanceEvents/View/Description";
const ScheduledMaintenanceEventCreate: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/ScheduledMaintenanceEvents/Create");
});
import ScheduledMaintenanceEventCreate from "../Pages/ScheduledMaintenanceEvents/Create";
// Settings Pages
const ScheduledMaintenanceSettingsState: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import(
"../Pages/ScheduledMaintenanceEvents/Settings/ScheduledMaintenanceState"
);
});
import ScheduledMaintenanceSettingsState from "../Pages/ScheduledMaintenanceEvents/Settings/ScheduledMaintenanceState";
const ScheduledMaintenanceSettingsTemplates: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import(
"../Pages/ScheduledMaintenanceEvents/Settings/ScheduledMaintenanceTemplates"
);
});
import ScheduledMaintenanceSettingsTemplates from "../Pages/ScheduledMaintenanceEvents/Settings/ScheduledMaintenanceTemplates";
const ScheduledMaintenanceSettingsTemplatesView: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import(
"../Pages/ScheduledMaintenanceEvents/Settings/ScheduledMaintenanceTemplateView"
);
});
import ScheduledMaintenanceSettingsTemplatesView from "../Pages/ScheduledMaintenanceEvents/Settings/ScheduledMaintenanceTemplateView";
const ScheduledMaintenanceSettingsNoteTemplates: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import(
"../Pages/ScheduledMaintenanceEvents/Settings/ScheduledMaintenanceNoteTemplates"
);
});
import ScheduledMaintenanceSettingsNoteTemplates from "../Pages/ScheduledMaintenanceEvents/Settings/ScheduledMaintenanceNoteTemplates";
const ScheduledMaintenanceSettingsNoteTemplatesView: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import(
"../Pages/ScheduledMaintenanceEvents/Settings/ScheduledMaintenanceNoteTemplateView"
);
});
import ScheduledMaintenanceSettingsNoteTemplatesView from "../Pages/ScheduledMaintenanceEvents/Settings/ScheduledMaintenanceNoteTemplateView";
const ScheduledMaintenanceSettingsCustomFields: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import(
"../Pages/ScheduledMaintenanceEvents/Settings/ScheduledMaintenanceCusomFields"
);
});
import ScheduledMaintenanceSettingsCustomFields from "../Pages/ScheduledMaintenanceEvents/Settings/ScheduledMaintenanceCusomFields";
const ScheduledMaintenanceSettingsMore: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import(
"../Pages/ScheduledMaintenanceEvents/Settings/ScheduledMaintenanceMoreSettings"
);
});
import ScheduledMaintenanceSettingsMore from "../Pages/ScheduledMaintenanceEvents/Settings/ScheduledMaintenanceMoreSettings";
const ScheduledMaintenanceEventsRoutes: FunctionComponent<ComponentProps> = (
props: ComponentProps,
@@ -195,14 +78,12 @@ const ScheduledMaintenanceEventsRoutes: FunctionComponent<ComponentProps> = (
<PageRoute
index
element={
<Suspense fallback={Loader}>
<ScheduledMaintenanceEvents
{...props}
pageRoute={
RouteMap[PageMap.SCHEDULED_MAINTENANCE_EVENTS] as Route
}
/>
</Suspense>
<ScheduledMaintenanceEvents
{...props}
pageRoute={
RouteMap[PageMap.SCHEDULED_MAINTENANCE_EVENTS] as Route
}
/>
}
/>
<PageRoute
@@ -212,16 +93,12 @@ const ScheduledMaintenanceEventsRoutes: FunctionComponent<ComponentProps> = (
] || ""
}
element={
<Suspense fallback={Loader}>
<OngoingScheduledMaintenanceEvents
{...props}
pageRoute={
RouteMap[
PageMap.ONGOING_SCHEDULED_MAINTENANCE_EVENTS
] as Route
}
/>
</Suspense>
<OngoingScheduledMaintenanceEvents
{...props}
pageRoute={
RouteMap[PageMap.ONGOING_SCHEDULED_MAINTENANCE_EVENTS] as Route
}
/>
}
/>
@@ -232,17 +109,15 @@ const ScheduledMaintenanceEventsRoutes: FunctionComponent<ComponentProps> = (
] || ""
}
element={
<Suspense fallback={Loader}>
<ScheduledMaintenanceEventsWorkspaceConnectionSlack
{...props}
pageRoute={
RouteMap[
PageMap
.SCHEDULED_MAINTENANCE_EVENTS_WORKSPACE_CONNECTION_SLACK
] as Route
}
/>
</Suspense>
<ScheduledMaintenanceEventsWorkspaceConnectionSlack
{...props}
pageRoute={
RouteMap[
PageMap
.SCHEDULED_MAINTENANCE_EVENTS_WORKSPACE_CONNECTION_SLACK
] as Route
}
/>
}
/>
@@ -254,17 +129,15 @@ const ScheduledMaintenanceEventsRoutes: FunctionComponent<ComponentProps> = (
] || ""
}
element={
<Suspense fallback={Loader}>
<ScheduledMaintenanceEventsWorkspaceConnectionMicrosoftTeams
{...props}
pageRoute={
RouteMap[
PageMap
.SCHEDULED_MAINTENANCE_EVENTS_WORKSPACE_CONNECTION_MICROSOFT_TEAMS
] as Route
}
/>
</Suspense>
<ScheduledMaintenanceEventsWorkspaceConnectionMicrosoftTeams
{...props}
pageRoute={
RouteMap[
PageMap
.SCHEDULED_MAINTENANCE_EVENTS_WORKSPACE_CONNECTION_MICROSOFT_TEAMS
] as Route
}
/>
}
/>
@@ -275,14 +148,12 @@ const ScheduledMaintenanceEventsRoutes: FunctionComponent<ComponentProps> = (
] || ""
}
element={
<Suspense fallback={Loader}>
<ScheduledMaintenanceEventCreate
{...props}
pageRoute={
RouteMap[PageMap.SCHEDULED_MAINTENANCE_EVENT_CREATE] as Route
}
/>
</Suspense>
<ScheduledMaintenanceEventCreate
{...props}
pageRoute={
RouteMap[PageMap.SCHEDULED_MAINTENANCE_EVENT_CREATE] as Route
}
/>
}
/>
@@ -294,16 +165,14 @@ const ScheduledMaintenanceEventsRoutes: FunctionComponent<ComponentProps> = (
] || ""
}
element={
<Suspense fallback={Loader}>
<ScheduledMaintenanceSettingsState
{...props}
pageRoute={
RouteMap[
PageMap.SCHEDULED_MAINTENANCE_EVENTS_SETTINGS_STATE
] as Route
}
/>
</Suspense>
<ScheduledMaintenanceSettingsState
{...props}
pageRoute={
RouteMap[
PageMap.SCHEDULED_MAINTENANCE_EVENTS_SETTINGS_STATE
] as Route
}
/>
}
/>
@@ -314,16 +183,14 @@ const ScheduledMaintenanceEventsRoutes: FunctionComponent<ComponentProps> = (
] || ""
}
element={
<Suspense fallback={Loader}>
<ScheduledMaintenanceSettingsTemplates
{...props}
pageRoute={
RouteMap[
PageMap.SCHEDULED_MAINTENANCE_EVENTS_SETTINGS_TEMPLATES
] as Route
}
/>
</Suspense>
<ScheduledMaintenanceSettingsTemplates
{...props}
pageRoute={
RouteMap[
PageMap.SCHEDULED_MAINTENANCE_EVENTS_SETTINGS_TEMPLATES
] as Route
}
/>
}
/>
@@ -334,16 +201,14 @@ const ScheduledMaintenanceEventsRoutes: FunctionComponent<ComponentProps> = (
] || ""
}
element={
<Suspense fallback={Loader}>
<ScheduledMaintenanceSettingsTemplatesView
{...props}
pageRoute={
RouteMap[
PageMap.SCHEDULED_MAINTENANCE_EVENTS_SETTINGS_TEMPLATES_VIEW
] as Route
}
/>
</Suspense>
<ScheduledMaintenanceSettingsTemplatesView
{...props}
pageRoute={
RouteMap[
PageMap.SCHEDULED_MAINTENANCE_EVENTS_SETTINGS_TEMPLATES_VIEW
] as Route
}
/>
}
/>
@@ -354,16 +219,14 @@ const ScheduledMaintenanceEventsRoutes: FunctionComponent<ComponentProps> = (
] || ""
}
element={
<Suspense fallback={Loader}>
<ScheduledMaintenanceSettingsNoteTemplates
{...props}
pageRoute={
RouteMap[
PageMap.SCHEDULED_MAINTENANCE_EVENTS_SETTINGS_NOTE_TEMPLATES
] as Route
}
/>
</Suspense>
<ScheduledMaintenanceSettingsNoteTemplates
{...props}
pageRoute={
RouteMap[
PageMap.SCHEDULED_MAINTENANCE_EVENTS_SETTINGS_NOTE_TEMPLATES
] as Route
}
/>
}
/>
@@ -374,17 +237,15 @@ const ScheduledMaintenanceEventsRoutes: FunctionComponent<ComponentProps> = (
] || ""
}
element={
<Suspense fallback={Loader}>
<ScheduledMaintenanceSettingsNoteTemplatesView
{...props}
pageRoute={
RouteMap[
PageMap
.SCHEDULED_MAINTENANCE_EVENTS_SETTINGS_NOTE_TEMPLATES_VIEW
] as Route
}
/>
</Suspense>
<ScheduledMaintenanceSettingsNoteTemplatesView
{...props}
pageRoute={
RouteMap[
PageMap
.SCHEDULED_MAINTENANCE_EVENTS_SETTINGS_NOTE_TEMPLATES_VIEW
] as Route
}
/>
}
/>
@@ -395,16 +256,14 @@ const ScheduledMaintenanceEventsRoutes: FunctionComponent<ComponentProps> = (
] || ""
}
element={
<Suspense fallback={Loader}>
<ScheduledMaintenanceSettingsCustomFields
{...props}
pageRoute={
RouteMap[
PageMap.SCHEDULED_MAINTENANCE_EVENTS_SETTINGS_CUSTOM_FIELDS
] as Route
}
/>
</Suspense>
<ScheduledMaintenanceSettingsCustomFields
{...props}
pageRoute={
RouteMap[
PageMap.SCHEDULED_MAINTENANCE_EVENTS_SETTINGS_CUSTOM_FIELDS
] as Route
}
/>
}
/>
@@ -415,16 +274,14 @@ const ScheduledMaintenanceEventsRoutes: FunctionComponent<ComponentProps> = (
] || ""
}
element={
<Suspense fallback={Loader}>
<ScheduledMaintenanceSettingsMore
{...props}
pageRoute={
RouteMap[
PageMap.SCHEDULED_MAINTENANCE_EVENTS_SETTINGS_MORE
] as Route
}
/>
</Suspense>
<ScheduledMaintenanceSettingsMore
{...props}
pageRoute={
RouteMap[
PageMap.SCHEDULED_MAINTENANCE_EVENTS_SETTINGS_MORE
] as Route
}
/>
}
/>
</PageRoute>
@@ -440,14 +297,10 @@ const ScheduledMaintenanceEventsRoutes: FunctionComponent<ComponentProps> = (
<PageRoute
index
element={
<Suspense fallback={Loader}>
<ScheduledMaintenanceEventView
{...props}
pageRoute={
RouteMap[PageMap.SCHEDULED_MAINTENANCE_VIEW] as Route
}
/>
</Suspense>
<ScheduledMaintenanceEventView
{...props}
pageRoute={RouteMap[PageMap.SCHEDULED_MAINTENANCE_VIEW] as Route}
/>
}
/>
<PageRoute
@@ -455,16 +308,14 @@ const ScheduledMaintenanceEventsRoutes: FunctionComponent<ComponentProps> = (
PageMap.SCHEDULED_MAINTENANCE_VIEW_CUSTOM_FIELDS,
)}
element={
<Suspense fallback={Loader}>
<ScheduledMaintenanceEventsViewCustomFields
{...props}
pageRoute={
RouteMap[
PageMap.SCHEDULED_MAINTENANCE_VIEW_CUSTOM_FIELDS
] as Route
}
/>
</Suspense>
<ScheduledMaintenanceEventsViewCustomFields
{...props}
pageRoute={
RouteMap[
PageMap.SCHEDULED_MAINTENANCE_VIEW_CUSTOM_FIELDS
] as Route
}
/>
}
/>
<PageRoute
@@ -472,16 +323,14 @@ const ScheduledMaintenanceEventsRoutes: FunctionComponent<ComponentProps> = (
PageMap.SCHEDULED_MAINTENANCE_VIEW_NOTIFICATION_LOGS,
)}
element={
<Suspense fallback={Loader}>
<ScheduledMaintenanceEventViewNotificationLogs
{...props}
pageRoute={
RouteMap[
PageMap.SCHEDULED_MAINTENANCE_VIEW_NOTIFICATION_LOGS
] as Route
}
/>
</Suspense>
<ScheduledMaintenanceEventViewNotificationLogs
{...props}
pageRoute={
RouteMap[
PageMap.SCHEDULED_MAINTENANCE_VIEW_NOTIFICATION_LOGS
] as Route
}
/>
}
/>
<PageRoute
@@ -489,14 +338,12 @@ const ScheduledMaintenanceEventsRoutes: FunctionComponent<ComponentProps> = (
PageMap.SCHEDULED_MAINTENANCE_VIEW_AI_LOGS,
)}
element={
<Suspense fallback={Loader}>
<ScheduledMaintenanceEventViewAILogs
{...props}
pageRoute={
RouteMap[PageMap.SCHEDULED_MAINTENANCE_VIEW_AI_LOGS] as Route
}
/>
</Suspense>
<ScheduledMaintenanceEventViewAILogs
{...props}
pageRoute={
RouteMap[PageMap.SCHEDULED_MAINTENANCE_VIEW_AI_LOGS] as Route
}
/>
}
/>
<PageRoute
@@ -504,14 +351,12 @@ const ScheduledMaintenanceEventsRoutes: FunctionComponent<ComponentProps> = (
PageMap.SCHEDULED_MAINTENANCE_VIEW_SETTINGS,
)}
element={
<Suspense fallback={Loader}>
<ScheduledMaintenanceEventsViewSettings
{...props}
pageRoute={
RouteMap[PageMap.SCHEDULED_MAINTENANCE_VIEW_SETTINGS] as Route
}
/>
</Suspense>
<ScheduledMaintenanceEventsViewSettings
{...props}
pageRoute={
RouteMap[PageMap.SCHEDULED_MAINTENANCE_VIEW_SETTINGS] as Route
}
/>
}
/>
<PageRoute
@@ -519,14 +364,12 @@ const ScheduledMaintenanceEventsRoutes: FunctionComponent<ComponentProps> = (
PageMap.SCHEDULED_MAINTENANCE_VIEW_DELETE,
)}
element={
<Suspense fallback={Loader}>
<ScheduledMaintenanceEventViewDelete
{...props}
pageRoute={
RouteMap[PageMap.SCHEDULED_MAINTENANCE_VIEW_DELETE] as Route
}
/>
</Suspense>
<ScheduledMaintenanceEventViewDelete
{...props}
pageRoute={
RouteMap[PageMap.SCHEDULED_MAINTENANCE_VIEW_DELETE] as Route
}
/>
}
/>
@@ -535,16 +378,14 @@ const ScheduledMaintenanceEventsRoutes: FunctionComponent<ComponentProps> = (
PageMap.SCHEDULED_MAINTENANCE_VIEW_DESCRIPTION,
)}
element={
<Suspense fallback={Loader}>
<ScheduledMaintenanceEventViewDescription
{...props}
pageRoute={
RouteMap[
PageMap.SCHEDULED_MAINTENANCE_VIEW_DESCRIPTION
] as Route
}
/>
</Suspense>
<ScheduledMaintenanceEventViewDescription
{...props}
pageRoute={
RouteMap[
PageMap.SCHEDULED_MAINTENANCE_VIEW_DESCRIPTION
] as Route
}
/>
}
/>
@@ -553,14 +394,12 @@ const ScheduledMaintenanceEventsRoutes: FunctionComponent<ComponentProps> = (
PageMap.SCHEDULED_MAINTENANCE_VIEW_OWNERS,
)}
element={
<Suspense fallback={Loader}>
<ScheduledMaintenanceEventViewOwner
{...props}
pageRoute={
RouteMap[PageMap.SCHEDULED_MAINTENANCE_VIEW_OWNERS] as Route
}
/>
</Suspense>
<ScheduledMaintenanceEventViewOwner
{...props}
pageRoute={
RouteMap[PageMap.SCHEDULED_MAINTENANCE_VIEW_OWNERS] as Route
}
/>
}
/>
@@ -569,16 +408,14 @@ const ScheduledMaintenanceEventsRoutes: FunctionComponent<ComponentProps> = (
PageMap.SCHEDULED_MAINTENANCE_VIEW_STATE_TIMELINE,
)}
element={
<Suspense fallback={Loader}>
<ScheduledMaintenanceEventViewStateTimeline
{...props}
pageRoute={
RouteMap[
PageMap.SCHEDULED_MAINTENANCE_VIEW_STATE_TIMELINE
] as Route
}
/>
</Suspense>
<ScheduledMaintenanceEventViewStateTimeline
{...props}
pageRoute={
RouteMap[
PageMap.SCHEDULED_MAINTENANCE_VIEW_STATE_TIMELINE
] as Route
}
/>
}
/>
@@ -587,14 +424,12 @@ const ScheduledMaintenanceEventsRoutes: FunctionComponent<ComponentProps> = (
PageMap.SCHEDULED_MAINTENANCE_INTERNAL_NOTE,
)}
element={
<Suspense fallback={Loader}>
<ScheduledMaintenanceEventInternalNote
{...props}
pageRoute={
RouteMap[PageMap.SCHEDULED_MAINTENANCE_INTERNAL_NOTE] as Route
}
/>
</Suspense>
<ScheduledMaintenanceEventInternalNote
{...props}
pageRoute={
RouteMap[PageMap.SCHEDULED_MAINTENANCE_INTERNAL_NOTE] as Route
}
/>
}
/>
@@ -603,14 +438,12 @@ const ScheduledMaintenanceEventsRoutes: FunctionComponent<ComponentProps> = (
PageMap.SCHEDULED_MAINTENANCE_PUBLIC_NOTE,
)}
element={
<Suspense fallback={Loader}>
<ScheduledMaintenanceEventPublicNote
{...props}
pageRoute={
RouteMap[PageMap.SCHEDULED_MAINTENANCE_PUBLIC_NOTE] as Route
}
/>
</Suspense>
<ScheduledMaintenanceEventPublicNote
{...props}
pageRoute={
RouteMap[PageMap.SCHEDULED_MAINTENANCE_PUBLIC_NOTE] as Route
}
/>
}
/>
</PageRoute>

View File

@@ -1,99 +1,38 @@
import Loader from "../Components/Loader/Loader";
import ComponentProps from "../Pages/PageComponentProps";
import StatusPageViewLayout from "../Pages/Service/View/Layout";
import ServiceLayout from "../Pages/Service/Layout";
import PageMap from "../Utils/PageMap";
import RouteMap, { RouteUtil, ServiceRoutePath } from "../Utils/RouteMap";
import Route from "Common/Types/API/Route";
import React, {
FunctionComponent,
LazyExoticComponent,
ReactElement,
Suspense,
lazy,
} from "react";
import React, { FunctionComponent, ReactElement } from "react";
import { Route as PageRoute, Routes } from "react-router-dom";
// Pages
const Services: LazyExoticComponent<FunctionComponent<ComponentProps>> = lazy(
() => {
return import("../Pages/Service/Services");
},
);
const ServiceDependencyGraph: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Service/DependencyGraph");
});
const ServiceView: LazyExoticComponent<FunctionComponent<ComponentProps>> =
lazy(() => {
return import("../Pages/Service/View/Index");
});
import Services from "../Pages/Service/Services";
import ServiceDependencyGraph from "../Pages/Service/DependencyGraph";
import ServiceView from "../Pages/Service/View/Index";
const ServiceViewMonitors: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Service/View/Monitors");
});
import ServiceViewMonitors from "../Pages/Service/View/Monitors";
const ServiceViewIncidents: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Service/View/Incidents");
});
import ServiceViewIncidents from "../Pages/Service/View/Incidents";
const ServiceViewAlerts: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Service/View/Alerts");
});
import ServiceViewAlerts from "../Pages/Service/View/Alerts";
const ServiceViewLogs: LazyExoticComponent<FunctionComponent<ComponentProps>> =
lazy(() => {
return import("../Pages/Service/View/Logs");
});
import ServiceViewLogs from "../Pages/Service/View/Logs";
const ServiceViewTraces: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Service/View/Traces");
});
import ServiceViewTraces from "../Pages/Service/View/Traces";
const ServiceViewMetrics: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Service/View/Metrics");
});
import ServiceViewMetrics from "../Pages/Service/View/Metrics";
const ServiceViewDelete: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Service/View/Delete");
});
import ServiceViewDelete from "../Pages/Service/View/Delete";
const ServiceViewSettings: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Service/View/Settings");
});
import ServiceViewSettings from "../Pages/Service/View/Settings";
const ServiceViewOwners: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Service/View/Owners");
});
import ServiceViewOwners from "../Pages/Service/View/Owners";
const ServiceViewDependencies: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Service/View/Dependencies");
});
import ServiceViewDependencies from "../Pages/Service/View/Dependencies";
const ServiceViewCodeRepositories: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Service/View/CodeRepositories");
});
import ServiceViewCodeRepositories from "../Pages/Service/View/CodeRepositories";
const ServiceRoutes: FunctionComponent<ComponentProps> = (
props: ComponentProps,
@@ -104,23 +43,19 @@ const ServiceRoutes: FunctionComponent<ComponentProps> = (
<PageRoute
path={ServiceRoutePath[PageMap.SERVICES] || ""}
element={
<Suspense fallback={Loader}>
<Services
{...props}
pageRoute={RouteMap[PageMap.SERVICES] as Route}
/>
</Suspense>
<Services
{...props}
pageRoute={RouteMap[PageMap.SERVICES] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.SERVICE_DEPENDENCY_GRAPH)}
element={
<Suspense fallback={Loader}>
<ServiceDependencyGraph
{...props}
pageRoute={RouteMap[PageMap.SERVICE_DEPENDENCY_GRAPH] as Route}
/>
</Suspense>
<ServiceDependencyGraph
{...props}
pageRoute={RouteMap[PageMap.SERVICE_DEPENDENCY_GRAPH] as Route}
/>
}
/>
</PageRoute>
@@ -132,132 +67,110 @@ const ServiceRoutes: FunctionComponent<ComponentProps> = (
<PageRoute
index
element={
<Suspense fallback={Loader}>
<ServiceView
{...props}
pageRoute={RouteMap[PageMap.SERVICE_VIEW] as Route}
/>
</Suspense>
<ServiceView
{...props}
pageRoute={RouteMap[PageMap.SERVICE_VIEW] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.SERVICE_VIEW_DELETE)}
element={
<Suspense fallback={Loader}>
<ServiceViewDelete
{...props}
pageRoute={RouteMap[PageMap.SERVICE_VIEW_DELETE] as Route}
/>
</Suspense>
<ServiceViewDelete
{...props}
pageRoute={RouteMap[PageMap.SERVICE_VIEW_DELETE] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.SERVICE_VIEW_SETTINGS)}
element={
<Suspense fallback={Loader}>
<ServiceViewSettings
{...props}
pageRoute={RouteMap[PageMap.SERVICE_VIEW_SETTINGS] as Route}
/>
</Suspense>
<ServiceViewSettings
{...props}
pageRoute={RouteMap[PageMap.SERVICE_VIEW_SETTINGS] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.SERVICE_VIEW_MONITORS)}
element={
<Suspense fallback={Loader}>
<ServiceViewMonitors
{...props}
pageRoute={RouteMap[PageMap.SERVICE_VIEW_MONITORS] as Route}
/>
</Suspense>
<ServiceViewMonitors
{...props}
pageRoute={RouteMap[PageMap.SERVICE_VIEW_MONITORS] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.SERVICE_VIEW_ALERTS)}
element={
<Suspense fallback={Loader}>
<ServiceViewAlerts
{...props}
pageRoute={RouteMap[PageMap.SERVICE_VIEW_ALERTS] as Route}
/>
</Suspense>
<ServiceViewAlerts
{...props}
pageRoute={RouteMap[PageMap.SERVICE_VIEW_ALERTS] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.SERVICE_VIEW_INCIDENTS)}
element={
<Suspense fallback={Loader}>
<ServiceViewIncidents
{...props}
pageRoute={RouteMap[PageMap.SERVICE_VIEW_INCIDENTS] as Route}
/>
</Suspense>
<ServiceViewIncidents
{...props}
pageRoute={RouteMap[PageMap.SERVICE_VIEW_INCIDENTS] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.SERVICE_VIEW_LOGS)}
element={
<Suspense fallback={Loader}>
<ServiceViewLogs
{...props}
pageRoute={RouteMap[PageMap.SERVICE_VIEW_LOGS] as Route}
/>
</Suspense>
<ServiceViewLogs
{...props}
pageRoute={RouteMap[PageMap.SERVICE_VIEW_LOGS] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.SERVICE_VIEW_TRACES)}
element={
<Suspense fallback={Loader}>
<ServiceViewTraces
{...props}
pageRoute={RouteMap[PageMap.SERVICE_VIEW_TRACES] as Route}
/>
</Suspense>
<ServiceViewTraces
{...props}
pageRoute={RouteMap[PageMap.SERVICE_VIEW_TRACES] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.SERVICE_VIEW_METRICS)}
element={
<Suspense fallback={Loader}>
<ServiceViewMetrics
{...props}
pageRoute={RouteMap[PageMap.SERVICE_VIEW_METRICS] as Route}
/>
</Suspense>
<ServiceViewMetrics
{...props}
pageRoute={RouteMap[PageMap.SERVICE_VIEW_METRICS] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.SERVICE_VIEW_OWNERS)}
element={
<Suspense fallback={Loader}>
<ServiceViewOwners
{...props}
pageRoute={RouteMap[PageMap.SERVICE_VIEW_OWNERS] as Route}
/>
</Suspense>
<ServiceViewOwners
{...props}
pageRoute={RouteMap[PageMap.SERVICE_VIEW_OWNERS] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.SERVICE_VIEW_DEPENDENCIES)}
element={
<Suspense fallback={Loader}>
<ServiceViewDependencies
{...props}
pageRoute={RouteMap[PageMap.SERVICE_VIEW_DEPENDENCIES] as Route}
/>
</Suspense>
<ServiceViewDependencies
{...props}
pageRoute={RouteMap[PageMap.SERVICE_VIEW_DEPENDENCIES] as Route}
/>
}
/>
@@ -266,14 +179,12 @@ const ServiceRoutes: FunctionComponent<ComponentProps> = (
PageMap.SERVICE_VIEW_CODE_REPOSITORIES,
)}
element={
<Suspense fallback={Loader}>
<ServiceViewCodeRepositories
{...props}
pageRoute={
RouteMap[PageMap.SERVICE_VIEW_CODE_REPOSITORIES] as Route
}
/>
</Suspense>
<ServiceViewCodeRepositories
{...props}
pageRoute={
RouteMap[PageMap.SERVICE_VIEW_CODE_REPOSITORIES] as Route
}
/>
}
/>
</PageRoute>

View File

@@ -1,171 +1,63 @@
import Loader from "../Components/Loader/Loader";
import ComponentProps from "../Pages/PageComponentProps";
import SettingsLayout from "../Pages/Settings/Layout";
import PageMap from "../Utils/PageMap";
import RouteMap, { RouteUtil, SettingsRoutePath } from "../Utils/RouteMap";
import Route from "Common/Types/API/Route";
import React, {
FunctionComponent,
LazyExoticComponent,
ReactElement,
Suspense,
lazy,
} from "react";
import React, { FunctionComponent, ReactElement } from "react";
import { Route as PageRoute, Routes } from "react-router-dom";
// Pages
const ProjectSettings: LazyExoticComponent<FunctionComponent<ComponentProps>> =
lazy(() => {
return import("../Pages/Settings/ProjectSettings");
});
const SettingsApiKeys: LazyExoticComponent<FunctionComponent<ComponentProps>> =
lazy(() => {
return import("../Pages/Settings/APIKeys");
});
import ProjectSettings from "../Pages/Settings/ProjectSettings";
import SettingsApiKeys from "../Pages/Settings/APIKeys";
const SettingsUsers: LazyExoticComponent<FunctionComponent<ComponentProps>> =
lazy(() => {
return import("../Pages/Settings/Users");
});
import SettingsUsers from "../Pages/Settings/Users";
const SettingsUserView: LazyExoticComponent<FunctionComponent<ComponentProps>> =
lazy(() => {
return import("../Pages/Settings/UserView");
});
import SettingsUserView from "../Pages/Settings/UserView";
const SettingsApiKeyView: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Settings/APIKeyView");
});
import SettingsApiKeyView from "../Pages/Settings/APIKeyView";
const SettingsIngestionKeys: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Settings/TelemetryIngestionKeys");
});
import SettingsIngestionKeys from "../Pages/Settings/TelemetryIngestionKeys";
const SettingsIngestionKeyView: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Settings/TelemetryIngestionKeyView");
});
import SettingsIngestionKeyView from "../Pages/Settings/TelemetryIngestionKeyView";
const SettingLabels: LazyExoticComponent<FunctionComponent<ComponentProps>> =
lazy(() => {
return import("../Pages/Settings/Labels");
});
import SettingLabels from "../Pages/Settings/Labels";
const SettingProbes: LazyExoticComponent<FunctionComponent<ComponentProps>> =
lazy(() => {
return import("../Pages/Settings/Probes");
});
import SettingProbes from "../Pages/Settings/Probes";
const SettingAIAgents: LazyExoticComponent<FunctionComponent<ComponentProps>> =
lazy(() => {
return import("../Pages/Settings/AIAgents");
});
import SettingAIAgents from "../Pages/Settings/AIAgents";
const SettingsAIAgentView: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Settings/AIAgentView");
});
import SettingsAIAgentView from "../Pages/Settings/AIAgentView";
const SettingLlmProviders: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Settings/LlmProviders");
});
import SettingLlmProviders from "../Pages/Settings/LlmProviders";
const SettingsLlmProviderView: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Settings/LlmProviderView");
});
import SettingsLlmProviderView from "../Pages/Settings/LlmProviderView";
const SettingsAIBilling: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Settings/AIBillingSettings");
});
import SettingsAIBilling from "../Pages/Settings/AIBillingSettings";
const SettingFeatureFlags: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Settings/FeatureFlags");
});
const SettingsTeams: LazyExoticComponent<FunctionComponent<ComponentProps>> =
lazy(() => {
return import("../Pages/Settings/Teams");
});
const SettingsTeamView: LazyExoticComponent<FunctionComponent<ComponentProps>> =
lazy(() => {
return import("../Pages/Settings/TeamView");
});
import SettingFeatureFlags from "../Pages/Settings/FeatureFlags";
import SettingsTeams from "../Pages/Settings/Teams";
import SettingsTeamView from "../Pages/Settings/TeamView";
const SettingsProbeView: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Settings/ProbeView");
});
import SettingsProbeView from "../Pages/Settings/ProbeView";
const SettingsDomains: LazyExoticComponent<FunctionComponent<ComponentProps>> =
lazy(() => {
return import("../Pages/Settings/Domains");
});
import SettingsDomains from "../Pages/Settings/Domains";
const SettingsBilling: LazyExoticComponent<FunctionComponent<ComponentProps>> =
lazy(() => {
return import("../Pages/Settings/Billing");
});
const SettingsSSO: LazyExoticComponent<FunctionComponent<ComponentProps>> =
lazy(() => {
return import("../Pages/Settings/SSO");
});
import SettingsBilling from "../Pages/Settings/Billing";
import SettingsSSO from "../Pages/Settings/SSO";
const SettingsSCIM: LazyExoticComponent<FunctionComponent<ComponentProps>> =
lazy(() => {
return import("../Pages/Settings/SCIM");
});
import SettingsSCIM from "../Pages/Settings/SCIM";
const SettingsNotificationLogs: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Settings/NotificationLogs");
});
const SettingsAILogs: LazyExoticComponent<FunctionComponent<ComponentProps>> =
lazy(() => {
return import("../Pages/Settings/AILogs");
});
const SettingsNotifications: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Settings/NotificationSettings");
});
const SettingsInvoices: LazyExoticComponent<FunctionComponent<ComponentProps>> =
lazy(() => {
return import("../Pages/Settings/Invoices");
});
import SettingsNotificationLogs from "../Pages/Settings/NotificationLogs";
import SettingsAILogs from "../Pages/Settings/AILogs";
import SettingsNotifications from "../Pages/Settings/NotificationSettings";
import SettingsInvoices from "../Pages/Settings/Invoices";
const SettingsMicrosoftTeamsIntegration: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Settings/MicrosoftTeamsIntegration");
});
import SettingsMicrosoftTeamsIntegration from "../Pages/Settings/MicrosoftTeamsIntegration";
const SettingsUsageHistory: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Settings/UsageHistory");
});
import SettingsUsageHistory from "../Pages/Settings/UsageHistory";
const SettingsSlackIntegration: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Settings/SlackIntegration");
});
import SettingsSlackIntegration from "../Pages/Settings/SlackIntegration";
const SettingsRoutes: FunctionComponent<ComponentProps> = (
props: ComponentProps,
@@ -179,60 +71,48 @@ const SettingsRoutes: FunctionComponent<ComponentProps> = (
<PageRoute
index
element={
<Suspense fallback={Loader}>
<ProjectSettings
{...props}
pageRoute={RouteMap[PageMap.SETTINGS] as Route}
/>
</Suspense>
<ProjectSettings
{...props}
pageRoute={RouteMap[PageMap.SETTINGS] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.SETTINGS_NOTIFICATION_LOGS)}
element={
<Suspense fallback={Loader}>
<SettingsNotificationLogs
{...props}
pageRoute={
RouteMap[PageMap.SETTINGS_NOTIFICATION_LOGS] as Route
}
/>
</Suspense>
<SettingsNotificationLogs
{...props}
pageRoute={RouteMap[PageMap.SETTINGS_NOTIFICATION_LOGS] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.SETTINGS_AI_LOGS)}
element={
<Suspense fallback={Loader}>
<SettingsAILogs
{...props}
pageRoute={RouteMap[PageMap.SETTINGS_AI_LOGS] as Route}
/>
</Suspense>
<SettingsAILogs
{...props}
pageRoute={RouteMap[PageMap.SETTINGS_AI_LOGS] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.SETTINGS_USAGE_HISTORY)}
element={
<Suspense fallback={Loader}>
<SettingsUsageHistory
{...props}
pageRoute={RouteMap[PageMap.SETTINGS_USAGE_HISTORY] as Route}
/>
</Suspense>
<SettingsUsageHistory
{...props}
pageRoute={RouteMap[PageMap.SETTINGS_USAGE_HISTORY] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.SETTINGS_FEATURE_FLAGS)}
element={
<Suspense fallback={Loader}>
<SettingFeatureFlags
{...props}
pageRoute={RouteMap[PageMap.SETTINGS_FEATURE_FLAGS] as Route}
/>
</Suspense>
<SettingFeatureFlags
{...props}
pageRoute={RouteMap[PageMap.SETTINGS_FEATURE_FLAGS] as Route}
/>
}
/>
@@ -241,28 +121,22 @@ const SettingsRoutes: FunctionComponent<ComponentProps> = (
PageMap.SETTINGS_NOTIFICATION_SETTINGS,
)}
element={
<Suspense fallback={Loader}>
<SettingsNotifications
{...props}
pageRoute={
RouteMap[PageMap.SETTINGS_NOTIFICATION_SETTINGS] as Route
}
/>
</Suspense>
<SettingsNotifications
{...props}
pageRoute={
RouteMap[PageMap.SETTINGS_NOTIFICATION_SETTINGS] as Route
}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.SETTINGS_SLACK_INTEGRATION)}
element={
<Suspense fallback={Loader}>
<SettingsSlackIntegration
{...props}
pageRoute={
RouteMap[PageMap.SETTINGS_SLACK_INTEGRATION] as Route
}
/>
</Suspense>
<SettingsSlackIntegration
{...props}
pageRoute={RouteMap[PageMap.SETTINGS_SLACK_INTEGRATION] as Route}
/>
}
/>
@@ -271,76 +145,62 @@ const SettingsRoutes: FunctionComponent<ComponentProps> = (
PageMap.SETTINGS_MICROSOFT_TEAMS_INTEGRATION,
)}
element={
<Suspense fallback={Loader}>
<SettingsMicrosoftTeamsIntegration
{...props}
pageRoute={
RouteMap[
PageMap.SETTINGS_MICROSOFT_TEAMS_INTEGRATION
] as Route
}
/>
</Suspense>
<SettingsMicrosoftTeamsIntegration
{...props}
pageRoute={
RouteMap[PageMap.SETTINGS_MICROSOFT_TEAMS_INTEGRATION] as Route
}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.SETTINGS_SSO)}
element={
<Suspense fallback={Loader}>
<SettingsSSO
{...props}
pageRoute={RouteMap[PageMap.SETTINGS_SSO] as Route}
/>
</Suspense>
<SettingsSSO
{...props}
pageRoute={RouteMap[PageMap.SETTINGS_SSO] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.SETTINGS_SCIM)}
element={
<Suspense fallback={Loader}>
<SettingsSCIM
{...props}
pageRoute={RouteMap[PageMap.SETTINGS_SCIM] as Route}
/>
</Suspense>
<SettingsSCIM
{...props}
pageRoute={RouteMap[PageMap.SETTINGS_SCIM] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.SETTINGS_DOMAINS)}
element={
<Suspense fallback={Loader}>
<SettingsDomains
{...props}
pageRoute={RouteMap[PageMap.SETTINGS_DOMAINS] as Route}
/>
</Suspense>
<SettingsDomains
{...props}
pageRoute={RouteMap[PageMap.SETTINGS_DOMAINS] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.SETTINGS_APIKEYS)}
element={
<Suspense fallback={Loader}>
<SettingsApiKeys
{...props}
pageRoute={RouteMap[PageMap.SETTINGS_APIKEYS] as Route}
/>
</Suspense>
<SettingsApiKeys
{...props}
pageRoute={RouteMap[PageMap.SETTINGS_APIKEYS] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.SETTINGS_APIKEY_VIEW, 2)}
element={
<Suspense fallback={Loader}>
<SettingsApiKeyView
{...props}
pageRoute={RouteMap[PageMap.SETTINGS_APIKEY_VIEW] as Route}
/>
</Suspense>
<SettingsApiKeyView
{...props}
pageRoute={RouteMap[PageMap.SETTINGS_APIKEY_VIEW] as Route}
/>
}
/>
@@ -349,14 +209,12 @@ const SettingsRoutes: FunctionComponent<ComponentProps> = (
PageMap.SETTINGS_TELEMETRY_INGESTION_KEYS,
)}
element={
<Suspense fallback={Loader}>
<SettingsIngestionKeys
{...props}
pageRoute={
RouteMap[PageMap.SETTINGS_TELEMETRY_INGESTION_KEYS] as Route
}
/>
</Suspense>
<SettingsIngestionKeys
{...props}
pageRoute={
RouteMap[PageMap.SETTINGS_TELEMETRY_INGESTION_KEYS] as Route
}
/>
}
/>
@@ -366,160 +224,132 @@ const SettingsRoutes: FunctionComponent<ComponentProps> = (
2,
)}
element={
<Suspense fallback={Loader}>
<SettingsIngestionKeyView
{...props}
pageRoute={
RouteMap[
PageMap.SETTINGS_TELEMETRY_INGESTION_KEY_VIEW
] as Route
}
/>
</Suspense>
<SettingsIngestionKeyView
{...props}
pageRoute={
RouteMap[PageMap.SETTINGS_TELEMETRY_INGESTION_KEY_VIEW] as Route
}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.SETTINGS_BILLING)}
element={
<Suspense fallback={Loader}>
<SettingsBilling
{...props}
pageRoute={RouteMap[PageMap.SETTINGS_BILLING] as Route}
/>
</Suspense>
<SettingsBilling
{...props}
pageRoute={RouteMap[PageMap.SETTINGS_BILLING] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.SETTINGS_BILLING_INVOICES)}
element={
<Suspense fallback={Loader}>
<SettingsInvoices
{...props}
pageRoute={RouteMap[PageMap.SETTINGS_BILLING_INVOICES] as Route}
/>
</Suspense>
<SettingsInvoices
{...props}
pageRoute={RouteMap[PageMap.SETTINGS_BILLING_INVOICES] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.SETTINGS_LABELS)}
element={
<Suspense fallback={Loader}>
<SettingLabels
{...props}
pageRoute={RouteMap[PageMap.SETTINGS_LABELS] as Route}
/>
</Suspense>
<SettingLabels
{...props}
pageRoute={RouteMap[PageMap.SETTINGS_LABELS] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.SETTINGS_PROBES)}
element={
<Suspense fallback={Loader}>
<SettingProbes
{...props}
pageRoute={RouteMap[PageMap.SETTINGS_PROBES] as Route}
/>
</Suspense>
<SettingProbes
{...props}
pageRoute={RouteMap[PageMap.SETTINGS_PROBES] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.SETTINGS_AI_AGENTS)}
element={
<Suspense fallback={Loader}>
<SettingAIAgents
{...props}
pageRoute={RouteMap[PageMap.SETTINGS_AI_AGENTS] as Route}
/>
</Suspense>
<SettingAIAgents
{...props}
pageRoute={RouteMap[PageMap.SETTINGS_AI_AGENTS] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.SETTINGS_AI_AGENT_VIEW, 2)}
element={
<Suspense fallback={Loader}>
<SettingsAIAgentView
{...props}
pageRoute={RouteMap[PageMap.SETTINGS_AI_AGENT_VIEW] as Route}
/>
</Suspense>
<SettingsAIAgentView
{...props}
pageRoute={RouteMap[PageMap.SETTINGS_AI_AGENT_VIEW] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.SETTINGS_LLM_PROVIDERS)}
element={
<Suspense fallback={Loader}>
<SettingLlmProviders
{...props}
pageRoute={RouteMap[PageMap.SETTINGS_LLM_PROVIDERS] as Route}
/>
</Suspense>
<SettingLlmProviders
{...props}
pageRoute={RouteMap[PageMap.SETTINGS_LLM_PROVIDERS] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.SETTINGS_TEAMS)}
element={
<Suspense fallback={Loader}>
<SettingsTeams
{...props}
pageRoute={RouteMap[PageMap.SETTINGS_TEAMS] as Route}
/>
</Suspense>
<SettingsTeams
{...props}
pageRoute={RouteMap[PageMap.SETTINGS_TEAMS] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.SETTINGS_USERS)}
element={
<Suspense fallback={Loader}>
<SettingsUsers
{...props}
pageRoute={RouteMap[PageMap.SETTINGS_USERS] as Route}
/>
</Suspense>
<SettingsUsers
{...props}
pageRoute={RouteMap[PageMap.SETTINGS_USERS] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.SETTINGS_TEAM_VIEW, 2)}
element={
<Suspense fallback={Loader}>
<SettingsTeamView
{...props}
pageRoute={RouteMap[PageMap.SETTINGS_TEAM_VIEW] as Route}
/>
</Suspense>
<SettingsTeamView
{...props}
pageRoute={RouteMap[PageMap.SETTINGS_TEAM_VIEW] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.SETTINGS_USER_VIEW, 2)}
element={
<Suspense fallback={Loader}>
<SettingsUserView
{...props}
pageRoute={RouteMap[PageMap.SETTINGS_USER_VIEW] as Route}
/>
</Suspense>
<SettingsUserView
{...props}
pageRoute={RouteMap[PageMap.SETTINGS_USER_VIEW] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.SETTINGS_PROBE_VIEW, 2)}
element={
<Suspense fallback={Loader}>
<SettingsProbeView
{...props}
pageRoute={RouteMap[PageMap.SETTINGS_PROBE_VIEW] as Route}
/>
</Suspense>
<SettingsProbeView
{...props}
pageRoute={RouteMap[PageMap.SETTINGS_PROBE_VIEW] as Route}
/>
}
/>
@@ -529,26 +359,20 @@ const SettingsRoutes: FunctionComponent<ComponentProps> = (
2,
)}
element={
<Suspense fallback={Loader}>
<SettingsLlmProviderView
{...props}
pageRoute={
RouteMap[PageMap.SETTINGS_LLM_PROVIDER_VIEW] as Route
}
/>
</Suspense>
<SettingsLlmProviderView
{...props}
pageRoute={RouteMap[PageMap.SETTINGS_LLM_PROVIDER_VIEW] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.SETTINGS_AI_BILLING)}
element={
<Suspense fallback={Loader}>
<SettingsAIBilling
{...props}
pageRoute={RouteMap[PageMap.SETTINGS_AI_BILLING] as Route}
/>
</Suspense>
<SettingsAIBilling
{...props}
pageRoute={RouteMap[PageMap.SETTINGS_AI_BILLING] as Route}
/>
}
/>
</PageRoute>

File diff suppressed because it is too large Load Diff

View File

@@ -1,30 +1,16 @@
import Loader from "../Components/Loader/Loader";
import ComponentProps from "../Pages/PageComponentProps";
import TracesLayout from "../Pages/Traces/Layout";
import TracesViewLayout from "../Pages/Traces/View/Layout";
import PageMap from "../Utils/PageMap";
import RouteMap, { RouteUtil, TracesRoutePath } from "../Utils/RouteMap";
import Route from "Common/Types/API/Route";
import React, {
FunctionComponent,
LazyExoticComponent,
ReactElement,
Suspense,
lazy,
} from "react";
import React, { FunctionComponent, ReactElement } from "react";
import { Route as PageRoute, Routes } from "react-router-dom";
// Lazy Pages
const TracesPage: LazyExoticComponent<FunctionComponent<ComponentProps>> = lazy(
() => {
return import("../Pages/Traces/Index");
},
);
// Pages
import TracesPage from "../Pages/Traces/Index";
const TraceViewPage: LazyExoticComponent<FunctionComponent<ComponentProps>> =
lazy(() => {
return import("../Pages/Traces/View/Index");
});
import TraceViewPage from "../Pages/Traces/View/Index";
const TracesRoutes: FunctionComponent<ComponentProps> = (
props: ComponentProps,
@@ -35,12 +21,10 @@ const TracesRoutes: FunctionComponent<ComponentProps> = (
<PageRoute
index
element={
<Suspense fallback={Loader}>
<TracesPage
{...props}
pageRoute={RouteMap[PageMap.TRACES] as Route}
/>
</Suspense>
<TracesPage
{...props}
pageRoute={RouteMap[PageMap.TRACES] as Route}
/>
}
/>
</PageRoute>
@@ -53,24 +37,20 @@ const TracesRoutes: FunctionComponent<ComponentProps> = (
<PageRoute
index
element={
<Suspense fallback={Loader}>
<TraceViewPage
{...props}
pageRoute={RouteMap[PageMap.TRACE_VIEW] as Route}
/>
</Suspense>
<TraceViewPage
{...props}
pageRoute={RouteMap[PageMap.TRACE_VIEW] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.TRACE_VIEW)}
element={
<Suspense fallback={Loader}>
<TraceViewPage
{...props}
pageRoute={RouteMap[PageMap.TRACE_VIEW] as Route}
/>
</Suspense>
<TraceViewPage
{...props}
pageRoute={RouteMap[PageMap.TRACE_VIEW] as Route}
/>
}
/>
</PageRoute>

View File

@@ -1,86 +1,31 @@
import Loader from "../Components/Loader/Loader";
import ComponentProps from "../Pages/PageComponentProps";
import UserSettingsLayout from "../Pages/UserSettings/Layout";
import PageMap from "../Utils/PageMap";
import RouteMap, { UserSettingsRoutePath } from "../Utils/RouteMap";
import Route from "Common/Types/API/Route";
import React, {
FunctionComponent,
LazyExoticComponent,
ReactElement,
Suspense,
lazy,
} from "react";
import React, { FunctionComponent, ReactElement } from "react";
import { Route as PageRoute, Routes } from "react-router-dom";
// Pages
const UserSettingsNotificationMethods: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/UserSettings/NotificationMethods");
});
const UserSettingsCustomFields: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/UserSettings/CustomFields");
});
const UserSettingsIncidentNotificationRules: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/UserSettings/IncidentOnCallRules");
});
import UserSettingsNotificationMethods from "../Pages/UserSettings/NotificationMethods";
import UserSettingsCustomFields from "../Pages/UserSettings/CustomFields";
import UserSettingsIncidentNotificationRules from "../Pages/UserSettings/IncidentOnCallRules";
const UserSettingsMicrosoftTeamsIntegration: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/UserSettings/MicrosoftTeamsIntegration");
});
import UserSettingsMicrosoftTeamsIntegration from "../Pages/UserSettings/MicrosoftTeamsIntegration";
const UserSettingsAlertNotificationRules: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/UserSettings/AlertOnCallRules");
});
import UserSettingsAlertNotificationRules from "../Pages/UserSettings/AlertOnCallRules";
const UserSettingsAlertEpisodeNotificationRules: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/UserSettings/EpisodeOnCallRules");
});
import UserSettingsAlertEpisodeNotificationRules from "../Pages/UserSettings/EpisodeOnCallRules";
const UserSettingsIncidentEpisodeNotificationRules: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/UserSettings/IncidentEpisodeOnCallRules");
});
import UserSettingsIncidentEpisodeNotificationRules from "../Pages/UserSettings/IncidentEpisodeOnCallRules";
const UserSettingsNotificationLogs: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/UserSettings/OnCallLogs");
});
const UserSettingsNotificationLogsTimeline: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/UserSettings/OnCallLogsTimeline");
});
const UserSettingsNotiifcationSetting: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/UserSettings/NotificationSettings");
});
import UserSettingsNotificationLogs from "../Pages/UserSettings/OnCallLogs";
import UserSettingsNotificationLogsTimeline from "../Pages/UserSettings/OnCallLogsTimeline";
import UserSettingsNotiifcationSetting from "../Pages/UserSettings/NotificationSettings";
const UserSettingsSlackIntegration: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/UserSettings/SlackIntegration");
});
import UserSettingsSlackIntegration from "../Pages/UserSettings/SlackIntegration";
const UserSettingsIncomingCallPhoneNumbers: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/UserSettings/IncomingCallPhoneNumbers");
});
import UserSettingsIncomingCallPhoneNumbers from "../Pages/UserSettings/IncomingCallPhoneNumbers";
const UserSettingsRoutes: FunctionComponent<ComponentProps> = (
props: ComponentProps,
@@ -91,12 +36,10 @@ const UserSettingsRoutes: FunctionComponent<ComponentProps> = (
<PageRoute
path={UserSettingsRoutePath[PageMap.USER_SETTINGS] || ""}
element={
<Suspense fallback={Loader}>
<UserSettingsNotificationMethods
{...props}
pageRoute={RouteMap[PageMap.USER_SETTINGS] as Route}
/>
</Suspense>
<UserSettingsNotificationMethods
{...props}
pageRoute={RouteMap[PageMap.USER_SETTINGS] as Route}
/>
}
/>
<PageRoute
@@ -104,27 +47,19 @@ const UserSettingsRoutes: FunctionComponent<ComponentProps> = (
UserSettingsRoutePath[PageMap.USER_SETTINGS_CUSTOM_FIELDS] || ""
}
element={
<Suspense fallback={Loader}>
<UserSettingsCustomFields
{...props}
pageRoute={
RouteMap[PageMap.USER_SETTINGS_CUSTOM_FIELDS] as Route
}
/>
</Suspense>
<UserSettingsCustomFields
{...props}
pageRoute={RouteMap[PageMap.USER_SETTINGS_CUSTOM_FIELDS] as Route}
/>
}
/>
<PageRoute
path={UserSettingsRoutePath[PageMap.USER_SETTINGS_ON_CALL_LOGS] || ""}
element={
<Suspense fallback={Loader}>
<UserSettingsNotificationLogs
{...props}
pageRoute={
RouteMap[PageMap.USER_SETTINGS_ON_CALL_LOGS] as Route
}
/>
</Suspense>
<UserSettingsNotificationLogs
{...props}
pageRoute={RouteMap[PageMap.USER_SETTINGS_ON_CALL_LOGS] as Route}
/>
}
/>
@@ -135,14 +70,12 @@ const UserSettingsRoutes: FunctionComponent<ComponentProps> = (
] || ""
}
element={
<Suspense fallback={Loader}>
<UserSettingsNotificationLogsTimeline
{...props}
pageRoute={
RouteMap[PageMap.USER_SETTINGS_ON_CALL_LOGS_TIMELINE] as Route
}
/>
</Suspense>
<UserSettingsNotificationLogsTimeline
{...props}
pageRoute={
RouteMap[PageMap.USER_SETTINGS_ON_CALL_LOGS_TIMELINE] as Route
}
/>
}
/>
@@ -153,14 +86,12 @@ const UserSettingsRoutes: FunctionComponent<ComponentProps> = (
] || ""
}
element={
<Suspense fallback={Loader}>
<UserSettingsNotiifcationSetting
{...props}
pageRoute={
RouteMap[PageMap.USER_SETTINGS_NOTIFICATION_SETTINGS] as Route
}
/>
</Suspense>
<UserSettingsNotiifcationSetting
{...props}
pageRoute={
RouteMap[PageMap.USER_SETTINGS_NOTIFICATION_SETTINGS] as Route
}
/>
}
/>
@@ -170,14 +101,12 @@ const UserSettingsRoutes: FunctionComponent<ComponentProps> = (
""
}
element={
<Suspense fallback={Loader}>
<UserSettingsNotificationMethods
{...props}
pageRoute={
RouteMap[PageMap.USER_SETTINGS_NOTIFICATION_METHODS] as Route
}
/>
</Suspense>
<UserSettingsNotificationMethods
{...props}
pageRoute={
RouteMap[PageMap.USER_SETTINGS_NOTIFICATION_METHODS] as Route
}
/>
}
/>
@@ -188,16 +117,12 @@ const UserSettingsRoutes: FunctionComponent<ComponentProps> = (
] || ""
}
element={
<Suspense fallback={Loader}>
<UserSettingsIncidentNotificationRules
{...props}
pageRoute={
RouteMap[
PageMap.USER_SETTINGS_INCIDENT_ON_CALL_RULES
] as Route
}
/>
</Suspense>
<UserSettingsIncidentNotificationRules
{...props}
pageRoute={
RouteMap[PageMap.USER_SETTINGS_INCIDENT_ON_CALL_RULES] as Route
}
/>
}
/>
@@ -206,14 +131,12 @@ const UserSettingsRoutes: FunctionComponent<ComponentProps> = (
UserSettingsRoutePath[PageMap.USER_SETTINGS_SLACK_INTEGRATION] || ""
}
element={
<Suspense fallback={Loader}>
<UserSettingsSlackIntegration
{...props}
pageRoute={
RouteMap[PageMap.USER_SETTINGS_SLACK_INTEGRATION] as Route
}
/>
</Suspense>
<UserSettingsSlackIntegration
{...props}
pageRoute={
RouteMap[PageMap.USER_SETTINGS_SLACK_INTEGRATION] as Route
}
/>
}
/>
@@ -224,16 +147,14 @@ const UserSettingsRoutes: FunctionComponent<ComponentProps> = (
] || ""
}
element={
<Suspense fallback={Loader}>
<UserSettingsMicrosoftTeamsIntegration
{...props}
pageRoute={
RouteMap[
PageMap.USER_SETTINGS_MICROSOFT_TEAMS_INTEGRATION
] as Route
}
/>
</Suspense>
<UserSettingsMicrosoftTeamsIntegration
{...props}
pageRoute={
RouteMap[
PageMap.USER_SETTINGS_MICROSOFT_TEAMS_INTEGRATION
] as Route
}
/>
}
/>
@@ -243,14 +164,12 @@ const UserSettingsRoutes: FunctionComponent<ComponentProps> = (
""
}
element={
<Suspense fallback={Loader}>
<UserSettingsAlertNotificationRules
{...props}
pageRoute={
RouteMap[PageMap.USER_SETTINGS_ALERT_ON_CALL_RULES] as Route
}
/>
</Suspense>
<UserSettingsAlertNotificationRules
{...props}
pageRoute={
RouteMap[PageMap.USER_SETTINGS_ALERT_ON_CALL_RULES] as Route
}
/>
}
/>
@@ -261,16 +180,14 @@ const UserSettingsRoutes: FunctionComponent<ComponentProps> = (
] || ""
}
element={
<Suspense fallback={Loader}>
<UserSettingsAlertEpisodeNotificationRules
{...props}
pageRoute={
RouteMap[
PageMap.USER_SETTINGS_ALERT_EPISODE_ON_CALL_RULES
] as Route
}
/>
</Suspense>
<UserSettingsAlertEpisodeNotificationRules
{...props}
pageRoute={
RouteMap[
PageMap.USER_SETTINGS_ALERT_EPISODE_ON_CALL_RULES
] as Route
}
/>
}
/>
@@ -281,16 +198,14 @@ const UserSettingsRoutes: FunctionComponent<ComponentProps> = (
] || ""
}
element={
<Suspense fallback={Loader}>
<UserSettingsIncidentEpisodeNotificationRules
{...props}
pageRoute={
RouteMap[
PageMap.USER_SETTINGS_INCIDENT_EPISODE_ON_CALL_RULES
] as Route
}
/>
</Suspense>
<UserSettingsIncidentEpisodeNotificationRules
{...props}
pageRoute={
RouteMap[
PageMap.USER_SETTINGS_INCIDENT_EPISODE_ON_CALL_RULES
] as Route
}
/>
}
/>
@@ -301,16 +216,14 @@ const UserSettingsRoutes: FunctionComponent<ComponentProps> = (
] || ""
}
element={
<Suspense fallback={Loader}>
<UserSettingsIncomingCallPhoneNumbers
{...props}
pageRoute={
RouteMap[
PageMap.USER_SETTINGS_INCOMING_CALL_PHONE_NUMBERS
] as Route
}
/>
</Suspense>
<UserSettingsIncomingCallPhoneNumbers
{...props}
pageRoute={
RouteMap[
PageMap.USER_SETTINGS_INCOMING_CALL_PHONE_NUMBERS
] as Route
}
/>
}
/>
</PageRoute>

View File

@@ -1,59 +1,22 @@
import Loader from "../Components/Loader/Loader";
import ComponentProps from "../Pages/PageComponentProps";
import WorkflowsLayout from "../Pages/Workflow/Layout";
import WorkflowViewLayout from "../Pages/Workflow/View/Layout";
import PageMap from "../Utils/PageMap";
import RouteMap, { RouteUtil, WorkflowRoutePath } from "../Utils/RouteMap";
import Route from "Common/Types/API/Route";
import React, {
FunctionComponent,
LazyExoticComponent,
ReactElement,
Suspense,
lazy,
} from "react";
import React, { FunctionComponent, ReactElement } from "react";
import { Route as PageRoute, Routes } from "react-router-dom";
// Lazy Pages
const Workflows: LazyExoticComponent<FunctionComponent<ComponentProps>> = lazy(
() => {
return import("../Pages/Workflow/Workflows");
},
);
const WorkflowsVariables: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Workflow/Variable");
});
const WorkflowsLogs: LazyExoticComponent<FunctionComponent<ComponentProps>> =
lazy(() => {
return import("../Pages/Workflow/Logs");
});
const WorkflowLogs: LazyExoticComponent<FunctionComponent<ComponentProps>> =
lazy(() => {
return import("../Pages/Workflow/View/Logs");
});
const WorkflowDelete: LazyExoticComponent<FunctionComponent<ComponentProps>> =
lazy(() => {
return import("../Pages/Workflow/View/Delete");
});
const WorkflowBuilder: LazyExoticComponent<FunctionComponent<ComponentProps>> =
lazy(() => {
return import("../Pages/Workflow/View/Builder");
});
const WorkflowOverview: LazyExoticComponent<FunctionComponent<ComponentProps>> =
lazy(() => {
return import("../Pages/Workflow/View/Index");
});
const WorkflowVariables: LazyExoticComponent<
FunctionComponent<ComponentProps>
> = lazy(() => {
return import("../Pages/Workflow/View/Variable");
});
const WorkflowSettings: LazyExoticComponent<FunctionComponent<ComponentProps>> =
lazy(() => {
return import("../Pages/Workflow/View/Settings");
});
// Pages
import Workflows from "../Pages/Workflow/Workflows";
import WorkflowsVariables from "../Pages/Workflow/Variable";
import WorkflowsLogs from "../Pages/Workflow/Logs";
import WorkflowLogs from "../Pages/Workflow/View/Logs";
import WorkflowDelete from "../Pages/Workflow/View/Delete";
import WorkflowBuilder from "../Pages/Workflow/View/Builder";
import WorkflowOverview from "../Pages/Workflow/View/Index";
import WorkflowVariables from "../Pages/Workflow/View/Variable";
import WorkflowSettings from "../Pages/Workflow/View/Settings";
const WorkflowRoutes: FunctionComponent<ComponentProps> = (
props: ComponentProps,
@@ -64,36 +27,30 @@ const WorkflowRoutes: FunctionComponent<ComponentProps> = (
<PageRoute
index
element={
<Suspense fallback={Loader}>
<Workflows
{...props}
pageRoute={RouteMap[PageMap.WORKFLOWS] as Route}
/>
</Suspense>
<Workflows
{...props}
pageRoute={RouteMap[PageMap.WORKFLOWS] as Route}
/>
}
/>
<PageRoute
path={WorkflowRoutePath[PageMap.WORKFLOWS_VARIABLES] || ""}
element={
<Suspense fallback={Loader}>
<WorkflowsVariables
{...props}
pageRoute={RouteMap[PageMap.WORKFLOWS_VARIABLES] as Route}
/>
</Suspense>
<WorkflowsVariables
{...props}
pageRoute={RouteMap[PageMap.WORKFLOWS_VARIABLES] as Route}
/>
}
/>
<PageRoute
path={WorkflowRoutePath[PageMap.WORKFLOWS_LOGS] || ""}
element={
<Suspense fallback={Loader}>
<WorkflowsLogs
{...props}
pageRoute={RouteMap[PageMap.WORKFLOWS_LOGS] as Route}
/>
</Suspense>
<WorkflowsLogs
{...props}
pageRoute={RouteMap[PageMap.WORKFLOWS_LOGS] as Route}
/>
}
/>
</PageRoute>
@@ -105,71 +62,59 @@ const WorkflowRoutes: FunctionComponent<ComponentProps> = (
<PageRoute
index
element={
<Suspense fallback={Loader}>
<WorkflowOverview
{...props}
pageRoute={RouteMap[PageMap.WORKFLOW_VIEW] as Route}
/>
</Suspense>
<WorkflowOverview
{...props}
pageRoute={RouteMap[PageMap.WORKFLOW_VIEW] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.WORKFLOW_VIEW_SETTINGS)}
element={
<Suspense fallback={Loader}>
<WorkflowSettings
{...props}
pageRoute={RouteMap[PageMap.WORKFLOW_VIEW_SETTINGS] as Route}
/>
</Suspense>
<WorkflowSettings
{...props}
pageRoute={RouteMap[PageMap.WORKFLOW_VIEW_SETTINGS] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.WORKFLOW_VARIABLES)}
element={
<Suspense fallback={Loader}>
<WorkflowVariables
{...props}
pageRoute={RouteMap[PageMap.WORKFLOW_VARIABLES] as Route}
/>
</Suspense>
<WorkflowVariables
{...props}
pageRoute={RouteMap[PageMap.WORKFLOW_VARIABLES] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.WORKFLOW_BUILDER)}
element={
<Suspense fallback={Loader}>
<WorkflowBuilder
{...props}
pageRoute={RouteMap[PageMap.WORKFLOW_BUILDER] as Route}
/>
</Suspense>
<WorkflowBuilder
{...props}
pageRoute={RouteMap[PageMap.WORKFLOW_BUILDER] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.WORKFLOW_LOGS)}
element={
<Suspense fallback={Loader}>
<WorkflowLogs
{...props}
pageRoute={RouteMap[PageMap.WORKFLOW_LOGS] as Route}
/>
</Suspense>
<WorkflowLogs
{...props}
pageRoute={RouteMap[PageMap.WORKFLOW_LOGS] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.WORKFLOW_DELETE)}
element={
<Suspense fallback={Loader}>
<WorkflowDelete
{...props}
pageRoute={RouteMap[PageMap.WORKFLOW_DELETE] as Route}
/>
</Suspense>
<WorkflowDelete
{...props}
pageRoute={RouteMap[PageMap.WORKFLOW_DELETE] as Route}
/>
}
/>
</PageRoute>

View File

@@ -283,6 +283,18 @@ export default class CriteriaFilterUtil {
});
}
if (monitorType === MonitorType.DNS) {
options = options.filter((i: DropdownOption) => {
return (
i.value === CheckOn.DnsIsOnline ||
i.value === CheckOn.DnsResponseTime ||
i.value === CheckOn.DnsRecordValue ||
i.value === CheckOn.DnssecIsValid ||
i.value === CheckOn.DnsRecordExists
);
});
}
return options;
}
@@ -498,6 +510,40 @@ export default class CriteriaFilterUtil {
});
}
if (
checkOn === CheckOn.DnsIsOnline ||
checkOn === CheckOn.DnssecIsValid ||
checkOn === CheckOn.DnsRecordExists
) {
options = options.filter((i: DropdownOption) => {
return i.value === FilterType.True || i.value === FilterType.False;
});
}
if (checkOn === CheckOn.DnsResponseTime) {
options = options.filter((i: DropdownOption) => {
return (
i.value === FilterType.GreaterThan ||
i.value === FilterType.LessThan ||
i.value === FilterType.LessThanOrEqualTo ||
i.value === FilterType.GreaterThanOrEqualTo
);
});
}
if (checkOn === CheckOn.DnsRecordValue) {
options = options.filter((i: DropdownOption) => {
return (
i.value === FilterType.Contains ||
i.value === FilterType.NotContains ||
i.value === FilterType.EqualTo ||
i.value === FilterType.NotEqualTo ||
i.value === FilterType.StartsWith ||
i.value === FilterType.EndsWith
);
});
}
if (checkOn === CheckOn.SnmpResponseTime) {
options = options.filter((i: DropdownOption) => {
return (
@@ -655,6 +701,14 @@ export default class CriteriaFilterUtil {
return "1";
}
if (checkOn === CheckOn.DnsResponseTime) {
return "5000";
}
if (checkOn === CheckOn.DnsRecordValue) {
return "192.168.1.1";
}
return "";
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -613,9 +613,9 @@
}
},
"node_modules/qs": {
"version": "6.14.1",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz",
"integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==",
"version": "6.14.2",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz",
"integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==",
"license": "BSD-3-Clause",
"dependencies": {
"side-channel": "^1.1.0"

View File

@@ -128,9 +128,13 @@ export default class BlogPostUtil {
}
public static async getBlogPost(fileName: string): Promise<BlogPost | null> {
const blogPost: BlogPost | null = await this.getBlogPostFromFile(fileName);
return blogPost;
try {
const blogPost: BlogPost | null =
await this.getBlogPostFromFile(fileName);
return blogPost;
} catch {
return null;
}
}
public static async getTags(): Promise<string[]> {

6
MCP/package-lock.json generated
View File

@@ -4328,9 +4328,9 @@
"license": "MIT"
},
"node_modules/qs": {
"version": "6.14.1",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz",
"integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==",
"version": "6.14.2",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz",
"integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==",
"license": "BSD-3-Clause",
"dependencies": {
"side-channel": "^1.1.0"

View File

@@ -1 +1,2 @@
import "./global.css";
export { default } from "./src/App";

View File

@@ -6,18 +6,20 @@
"orientation": "portrait",
"icon": "./assets/icon.png",
"scheme": "oneuptime",
"userInterfaceStyle": "dark",
"userInterfaceStyle": "automatic",
"newArchEnabled": true,
"splash": {
"image": "./assets/splash-icon.png",
"resizeMode": "contain",
"backgroundColor": "#0D1117"
"backgroundColor": "#FFFFFF"
},
"ios": {
"supportsTablet": true,
"bundleIdentifier": "com.oneuptime.oncall",
"infoPlist": {
"UIBackgroundModes": ["remote-notification"],
"UIBackgroundModes": [
"remote-notification"
],
"NSFaceIDUsageDescription": "Authenticate to access OneUptime On-Call"
}
},
@@ -36,7 +38,8 @@
"color": "#58A6FF"
}
],
"expo-local-authentication"
"expo-local-authentication",
"expo-font"
],
"web": {
"favicon": "./assets/favicon.png"

View File

@@ -0,0 +1,9 @@
module.exports = function (api) {
api.cache(true);
return {
presets: [
["babel-preset-expo", { jsxImportSource: "nativewind" }],
"nativewind/babel",
],
};
};

89
MobileApp/global.css Normal file
View File

@@ -0,0 +1,89 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
:root {
--color-bg-primary: #FFFFFF;
--color-bg-secondary: #F8F8FA;
--color-bg-tertiary: #F0F0F2;
--color-bg-elevated: #FFFFFF;
--color-card-accent: rgba(0, 0, 0, 0.04);
--color-bg-glass: rgba(255, 255, 255, 0.85);
--color-icon-bg: rgba(0, 0, 0, 0.05);
--color-border-default: #E5E5EA;
--color-border-subtle: #F0F0F2;
--color-text-primary: #111111;
--color-text-secondary: #6B6B6B;
--color-text-tertiary: #9A9A9A;
--color-text-inverse: #FFFFFF;
--color-severity-critical: #CF222E;
--color-severity-critical-bg: #CF222E1A;
--color-severity-major: #BC4C00;
--color-severity-major-bg: #BC4C001A;
--color-severity-minor: #9A6700;
--color-severity-minor-bg: #9A67001A;
--color-severity-warning: #BF8700;
--color-severity-warning-bg: #BF87001A;
--color-severity-info: #0969DA;
--color-severity-info-bg: #0969DA1A;
--color-state-created: #CF222E;
--color-state-acknowledged: #9A6700;
--color-state-resolved: #1A7F37;
--color-state-investigating: #BC4C00;
--color-state-muted: #8C959F;
--color-oncall-active: #1A7F37;
--color-oncall-active-bg: #1A7F371A;
--color-oncall-inactive: #8C959F;
--color-oncall-inactive-bg: #8C959F1A;
--color-action-primary: #1A1A1A;
--color-action-primary-pressed: #333333;
--color-action-destructive: #CF222E;
--color-action-destructive-pressed: #A40E26;
--color-status-success: #1A7F37;
--color-status-success-bg: #1A7F371A;
--color-status-error: #CF222E;
--color-status-error-bg: #CF222E1A;
}
.dark {
--color-bg-primary: #000000;
--color-bg-secondary: #0A0A0A;
--color-bg-tertiary: #161616;
--color-bg-elevated: #0F0F0F;
--color-card-accent: rgba(255, 255, 255, 0.07);
--color-bg-glass: rgba(255, 255, 255, 0.05);
--color-icon-bg: rgba(255, 255, 255, 0.07);
--color-border-default: #1C1C1E;
--color-border-subtle: #141414;
--color-text-primary: #F0F0F0;
--color-text-secondary: #8E8E93;
--color-text-tertiary: #636366;
--color-text-inverse: #000000;
--color-severity-critical: #F85149;
--color-severity-critical-bg: #F8514926;
--color-severity-major: #F0883E;
--color-severity-major-bg: #F0883E26;
--color-severity-minor: #D29922;
--color-severity-minor-bg: #D2992226;
--color-severity-warning: #E3B341;
--color-severity-warning-bg: #E3B34126;
--color-severity-info: #58A6FF;
--color-severity-info-bg: #58A6FF26;
--color-state-created: #F85149;
--color-state-acknowledged: #D29922;
--color-state-resolved: #3FB950;
--color-state-investigating: #F0883E;
--color-state-muted: #636366;
--color-oncall-active: #3FB950;
--color-oncall-active-bg: #3FB95026;
--color-oncall-inactive: #636366;
--color-oncall-inactive-bg: #63636626;
--color-action-primary: #FFFFFF;
--color-action-primary-pressed: #D4D4D4;
--color-action-destructive: #F85149;
--color-action-destructive-pressed: #DA3633;
--color-status-success: #3FB950;
--color-status-success-bg: #3FB95026;
--color-status-error: #F85149;
--color-status-error-bg: #F8514926;
}

View File

@@ -0,0 +1,6 @@
const { getDefaultConfig } = require("expo/metro-config");
const { withNativeWind } = require("nativewind/metro");
const config = getDefaultConfig(__dirname);
module.exports = withNativeWind(config, { input: "./global.css" });

3
MobileApp/nativewind-env.d.ts vendored Normal file
View File

@@ -0,0 +1,3 @@
/// <reference types="nativewind/types" />
// NOTE: This file should not be edited and should be committed with your source code. It is generated by NativeWind.

File diff suppressed because it is too large Load Diff

View File

@@ -10,6 +10,7 @@
"compile": "tsc --noEmit"
},
"dependencies": {
"@expo/vector-icons": "^15.0.3",
"@react-native-async-storage/async-storage": "^2.2.0",
"@react-native-community/netinfo": "11.4.1",
"@react-navigation/bottom-tabs": "^7.12.0",
@@ -22,23 +23,29 @@
"expo": "~54.0.33",
"expo-constants": "~18.0.0",
"expo-device": "~8.0.10",
"expo-font": "~14.0.11",
"expo-haptics": "~15.0.8",
"expo-linear-gradient": "~15.0.8",
"expo-linking": "~8.0.11",
"expo-local-authentication": "~17.0.8",
"expo-notifications": "~0.32.0",
"expo-splash-screen": "^31.0.13",
"expo-status-bar": "~3.0.9",
"expo-system-ui": "~6.0.9",
"nativewind": "^4.2.1",
"postcss": "^8.5.6",
"react": "19.1.0",
"react-dom": "19.1.0",
"react-native": "0.81.5",
"react-native-keychain": "^10.0.0",
"react-native-safe-area-context": "^5.6.2",
"react-native-screens": "~4.16.0",
"react-native-web": "^0.21.0"
"react-native-svg": "15.12.1",
"react-native-web": "^0.21.0",
"tailwindcss": "^3.4.19"
},
"devDependencies": {
"@types/react": "~19.1.0",
"babel-preset-expo": "^54.0.10",
"typescript": "~5.9.2"
},
"private": true

View File

@@ -1,5 +1,6 @@
import React from "react";
import { View, StyleSheet, ViewStyle } from "react-native";
import { View } from "react-native";
import { SafeAreaProvider } from "react-native-safe-area-context";
import { StatusBar } from "expo-status-bar";
import { QueryClient } from "@tanstack/react-query";
import { PersistQueryClientProvider } from "@tanstack/react-query-persist-client";
@@ -30,10 +31,8 @@ function AppContent(): React.JSX.Element {
return (
<View
style={[
styles.container,
{ backgroundColor: theme.colors.backgroundPrimary },
]}
className="flex-1 bg-bg-primary"
style={{ flex: 1, backgroundColor: theme.colors.backgroundPrimary }}
>
<StatusBar style={theme.isDark ? "light" : "dark"} />
<RootNavigator />
@@ -44,23 +43,19 @@ function AppContent(): React.JSX.Element {
export default function App(): React.JSX.Element {
return (
<PersistQueryClientProvider
client={queryClient}
persistOptions={{ persister: asyncStoragePersister }}
>
<ThemeProvider>
<AuthProvider>
<ProjectProvider>
<AppContent />
</ProjectProvider>
</AuthProvider>
</ThemeProvider>
</PersistQueryClientProvider>
<SafeAreaProvider>
<PersistQueryClientProvider
client={queryClient}
persistOptions={{ persister: asyncStoragePersister }}
>
<ThemeProvider>
<AuthProvider>
<ProjectProvider>
<AppContent />
</ProjectProvider>
</AuthProvider>
</ThemeProvider>
</PersistQueryClientProvider>
</SafeAreaProvider>
);
}
const styles: { container: ViewStyle } = StyleSheet.create({
container: {
flex: 1,
},
});

View File

@@ -6,6 +6,7 @@ import type {
AlertState,
StateTimelineItem,
NoteItem,
FeedItem,
} from "./types";
export async function fetchAlertEpisodes(
@@ -43,6 +44,41 @@ export async function fetchAlertEpisodes(
return response.data;
}
export async function fetchAllAlertEpisodes(
options: { skip?: number; limit?: number; unresolvedOnly?: boolean } = {},
): Promise<ListResponse<AlertEpisodeItem>> {
const { skip = 0, limit = 100, unresolvedOnly = false } = options;
const query: Record<string, unknown> = {};
if (unresolvedOnly) {
query.currentAlertState = { isResolvedState: false };
}
const response: AxiosResponse = await apiClient.post(
`/api/alert-episode/get-list?skip=${skip}&limit=${limit}`,
{
query,
select: {
_id: true,
title: true,
episodeNumber: true,
episodeNumberWithPrefix: true,
description: true,
createdAt: true,
alertCount: true,
currentAlertState: { _id: true, name: true, color: true },
alertSeverity: { _id: true, name: true, color: true },
projectId: true,
},
sort: { createdAt: "DESC" },
},
{
headers: { "is-multi-tenant-query": "true" },
},
);
return response.data;
}
export async function fetchAlertEpisodeById(
projectId: string,
episodeId: string,
@@ -118,6 +154,31 @@ export async function fetchAlertEpisodeStateTimeline(
return response.data.data;
}
export async function fetchAlertEpisodeFeed(
projectId: string,
episodeId: string,
): Promise<FeedItem[]> {
const response: AxiosResponse = await apiClient.post(
"/api/alert-episode-feed/get-list?skip=0&limit=50",
{
query: { alertEpisodeId: episodeId },
select: {
_id: true,
feedInfoInMarkdown: true,
moreInformationInMarkdown: true,
displayColor: true,
postedAt: true,
createdAt: true,
},
sort: { postedAt: "DESC" },
},
{
headers: { tenantid: projectId },
},
);
return response.data.data;
}
export async function changeAlertEpisodeState(
projectId: string,
episodeId: string,

View File

@@ -5,6 +5,7 @@ import type {
AlertItem,
AlertState,
StateTimelineItem,
FeedItem,
} from "./types";
export async function fetchAlerts(
@@ -42,6 +43,41 @@ export async function fetchAlerts(
return response.data;
}
export async function fetchAllAlerts(
options: { skip?: number; limit?: number; unresolvedOnly?: boolean } = {},
): Promise<ListResponse<AlertItem>> {
const { skip = 0, limit = 100, unresolvedOnly = false } = options;
const query: Record<string, unknown> = {};
if (unresolvedOnly) {
query.currentAlertState = { isResolvedState: false };
}
const response: AxiosResponse = await apiClient.post(
`/api/alert/get-list?skip=${skip}&limit=${limit}`,
{
query,
select: {
_id: true,
title: true,
alertNumber: true,
alertNumberWithPrefix: true,
description: true,
createdAt: true,
currentAlertState: { _id: true, name: true, color: true },
alertSeverity: { _id: true, name: true, color: true },
monitor: { _id: true, name: true },
projectId: true,
},
sort: { createdAt: "DESC" },
},
{
headers: { "is-multi-tenant-query": "true" },
},
);
return response.data;
}
export async function fetchAlertById(
projectId: string,
alertId: string,
@@ -117,6 +153,31 @@ export async function fetchAlertStateTimeline(
return response.data.data;
}
export async function fetchAlertFeed(
projectId: string,
alertId: string,
): Promise<FeedItem[]> {
const response: AxiosResponse = await apiClient.post(
"/api/alert-feed/get-list?skip=0&limit=50",
{
query: { alertId },
select: {
_id: true,
feedInfoInMarkdown: true,
moreInformationInMarkdown: true,
displayColor: true,
postedAt: true,
createdAt: true,
},
sort: { postedAt: "DESC" },
},
{
headers: { tenantid: projectId },
},
);
return response.data.data;
}
export async function changeAlertState(
projectId: string,
alertId: string,

View File

@@ -13,6 +13,42 @@ import {
type StoredTokens,
} from "../storage/keychain";
/**
* Recursively normalizes OneUptime API serialized types in response data.
* Converts { _type: "ObjectID", value: "uuid" } → "uuid"
* Converts { _type: "DateTime", value: "iso-string" } → "iso-string"
*/
function normalizeResponseData(data: unknown): unknown {
if (data === null || data === undefined) {
return data;
}
if (Array.isArray(data)) {
return data.map(normalizeResponseData);
}
if (typeof data === "object") {
const obj: Record<string, unknown> = data as Record<string, unknown>;
// Check for serialized OneUptime types
if (
typeof obj["_type"] === "string" &&
typeof obj["value"] === "string" &&
(obj["_type"] === "ObjectID" || obj["_type"] === "DateTime")
) {
return obj["value"];
}
const normalized: Record<string, unknown> = {};
for (const key in obj) {
normalized[key] = normalizeResponseData(obj[key]);
}
return normalized;
}
return data;
}
let isRefreshing: boolean = false;
let refreshSubscribers: Array<(token: string) => void> = [];
let onAuthFailure: (() => void) | null = null;
@@ -55,9 +91,10 @@ apiClient.interceptors.request.use(
},
);
// Response interceptor: handle 401 with token refresh queue
// Response interceptor: normalize OneUptime serialized types then handle 401
apiClient.interceptors.response.use(
(response: AxiosResponse) => {
response.data = normalizeResponseData(response.data);
return response;
},
async (error: AxiosError) => {
@@ -97,6 +134,9 @@ apiClient.interceptors.response.use(
{
refreshToken: tokens.refreshToken,
},
{
timeout: 10000,
},
);
const { accessToken, refreshToken, refreshTokenExpiresAt } =

View File

@@ -6,6 +6,7 @@ import type {
IncidentState,
StateTimelineItem,
NoteItem,
FeedItem,
} from "./types";
export async function fetchIncidentEpisodes(
@@ -44,6 +45,42 @@ export async function fetchIncidentEpisodes(
return response.data;
}
export async function fetchAllIncidentEpisodes(
options: { skip?: number; limit?: number; unresolvedOnly?: boolean } = {},
): Promise<ListResponse<IncidentEpisodeItem>> {
const { skip = 0, limit = 100, unresolvedOnly = false } = options;
const query: Record<string, unknown> = {};
if (unresolvedOnly) {
query.currentIncidentState = { isResolvedState: false };
}
const response: AxiosResponse = await apiClient.post(
`/api/incident-episode/get-list?skip=${skip}&limit=${limit}`,
{
query,
select: {
_id: true,
title: true,
episodeNumber: true,
episodeNumberWithPrefix: true,
description: true,
createdAt: true,
declaredAt: true,
incidentCount: true,
currentIncidentState: { _id: true, name: true, color: true },
incidentSeverity: { _id: true, name: true, color: true },
projectId: true,
},
sort: { createdAt: "DESC" },
},
{
headers: { "is-multi-tenant-query": "true" },
},
);
return response.data;
}
export async function fetchIncidentEpisodeById(
projectId: string,
episodeId: string,
@@ -120,6 +157,31 @@ export async function fetchIncidentEpisodeStateTimeline(
return response.data.data;
}
export async function fetchIncidentEpisodeFeed(
projectId: string,
episodeId: string,
): Promise<FeedItem[]> {
const response: AxiosResponse = await apiClient.post(
"/api/incident-episode-feed/get-list?skip=0&limit=50",
{
query: { incidentEpisodeId: episodeId },
select: {
_id: true,
feedInfoInMarkdown: true,
moreInformationInMarkdown: true,
displayColor: true,
postedAt: true,
createdAt: true,
},
sort: { postedAt: "DESC" },
},
{
headers: { tenantid: projectId },
},
);
return response.data.data;
}
export async function changeIncidentEpisodeState(
projectId: string,
episodeId: string,

View File

@@ -5,6 +5,7 @@ import type {
IncidentItem,
IncidentState,
StateTimelineItem,
FeedItem,
} from "./types";
export async function fetchIncidents(
@@ -43,6 +44,42 @@ export async function fetchIncidents(
return response.data;
}
export async function fetchAllIncidents(
options: { skip?: number; limit?: number; unresolvedOnly?: boolean } = {},
): Promise<ListResponse<IncidentItem>> {
const { skip = 0, limit = 100, unresolvedOnly = false } = options;
const query: Record<string, unknown> = {};
if (unresolvedOnly) {
query.currentIncidentState = { isResolvedState: false };
}
const response: AxiosResponse = await apiClient.post(
`/api/incident/get-list?skip=${skip}&limit=${limit}`,
{
query,
select: {
_id: true,
title: true,
incidentNumber: true,
incidentNumberWithPrefix: true,
description: true,
declaredAt: true,
createdAt: true,
currentIncidentState: { _id: true, name: true, color: true },
incidentSeverity: { _id: true, name: true, color: true },
monitors: { _id: true, name: true },
projectId: true,
},
sort: { createdAt: "DESC" },
},
{
headers: { "is-multi-tenant-query": "true" },
},
);
return response.data;
}
export async function fetchIncidentById(
projectId: string,
incidentId: string,
@@ -119,6 +156,31 @@ export async function fetchIncidentStateTimeline(
return response.data.data;
}
export async function fetchIncidentFeed(
projectId: string,
incidentId: string,
): Promise<FeedItem[]> {
const response: AxiosResponse = await apiClient.post(
"/api/incident-feed/get-list?skip=0&limit=50",
{
query: { incidentId },
select: {
_id: true,
feedInfoInMarkdown: true,
moreInformationInMarkdown: true,
displayColor: true,
postedAt: true,
createdAt: true,
},
sort: { postedAt: "DESC" },
},
{
headers: { tenantid: projectId },
},
);
return response.data.data;
}
export async function changeIncidentState(
projectId: string,
incidentId: string,

View File

@@ -12,7 +12,7 @@ export async function fetchProjects(): Promise<ListResponse<ProjectItem>> {
},
{
headers: {
// Multi-tenant request — no tenantid needed, user token determines access
"is-multi-tenant-query": "true",
},
},
);

View File

@@ -39,6 +39,7 @@ export interface IncidentItem {
currentIncidentState: NamedEntityWithColor;
incidentSeverity: NamedEntityWithColor;
monitors: NamedEntity[];
projectId?: string;
}
export interface AlertItem {
@@ -51,6 +52,7 @@ export interface AlertItem {
currentAlertState: NamedEntityWithColor;
alertSeverity: NamedEntityWithColor;
monitor: NamedEntity | null;
projectId?: string;
}
export interface IncidentState {
@@ -91,6 +93,7 @@ export interface IncidentEpisodeItem {
incidentCount: number;
currentIncidentState: NamedEntityWithColor;
incidentSeverity: NamedEntityWithColor;
projectId?: string;
}
export interface AlertEpisodeItem {
@@ -103,6 +106,7 @@ export interface AlertEpisodeItem {
alertCount: number;
currentAlertState: NamedEntityWithColor;
alertSeverity: NamedEntityWithColor;
projectId?: string;
}
export interface NoteItem {
@@ -111,3 +115,23 @@ export interface NoteItem {
createdAt: string;
createdByUser: { _id: string; name: string } | null;
}
export interface FeedItem {
_id: string;
feedInfoInMarkdown: string;
moreInformationInMarkdown?: string;
displayColor: ColorField;
postedAt?: string;
createdAt: string;
}
export interface WithProject<T> {
item: T;
projectId: string;
projectName: string;
}
export type ProjectIncidentItem = WithProject<IncidentItem>;
export type ProjectAlertItem = WithProject<AlertItem>;
export type ProjectIncidentEpisodeItem = WithProject<IncidentEpisodeItem>;
export type ProjectAlertEpisodeItem = WithProject<AlertEpisodeItem>;

View File

@@ -5,12 +5,12 @@ import {
TextInput,
TouchableOpacity,
Modal,
ActivityIndicator,
StyleSheet,
KeyboardAvoidingView,
Platform,
} from "react-native";
import { Ionicons } from "@expo/vector-icons";
import { useTheme } from "../theme";
import GradientButton from "./GradientButton";
interface AddNoteModalProps {
visible: boolean;
@@ -49,37 +49,63 @@ export default function AddNoteModal({
onRequestClose={handleClose}
>
<KeyboardAvoidingView
style={styles.overlay}
className="flex-1 justify-end"
style={{ backgroundColor: "rgba(0,0,0,0.5)" }}
behavior={Platform.OS === "ios" ? "padding" : "height"}
>
<View
style={[
styles.container,
{
backgroundColor: theme.colors.backgroundSecondary,
borderColor: theme.colors.borderDefault,
},
]}
className="rounded-t-[28px] p-5 pb-9"
style={{
backgroundColor: theme.isDark
? theme.colors.backgroundElevated
: theme.colors.backgroundPrimary,
borderWidth: 1,
borderBottomWidth: 0,
borderColor: theme.colors.borderGlass,
shadowColor: "#000",
shadowOpacity: 0.2,
shadowOffset: { width: 0, height: -8 },
shadowRadius: 24,
elevation: 16,
}}
>
<Text
style={[
theme.typography.titleMedium,
{ color: theme.colors.textPrimary, marginBottom: 16 },
]}
>
Add Note
</Text>
{/* Drag Handle */}
<View className="items-center pt-1 pb-5">
<View
className="w-10 h-1.5 rounded-full"
style={{ backgroundColor: theme.colors.borderDefault }}
/>
</View>
<View className="flex-row items-center mb-5">
<View
className="w-9 h-9 rounded-lg items-center justify-center mr-3"
style={{
backgroundColor: theme.colors.iconBackground,
}}
>
<Ionicons
name="chatbubble-outline"
size={18}
color={theme.colors.textPrimary}
/>
</View>
<Text
className="text-title-md text-text-primary"
style={{ letterSpacing: -0.3 }}
>
Add Note
</Text>
</View>
<TextInput
style={[
styles.input,
{
backgroundColor: theme.colors.backgroundTertiary,
color: theme.colors.textPrimary,
borderColor: theme.colors.borderSubtle,
},
]}
placeholder="Add a note..."
className="min-h-[120px] rounded-xl p-4 text-[15px] text-text-primary"
style={{
backgroundColor: theme.colors.backgroundSecondary,
borderWidth: 1,
borderColor: theme.colors.borderGlass,
}}
placeholder="Write a note..."
placeholderTextColor={theme.colors.textTertiary}
value={noteText}
onChangeText={setNoteText}
@@ -88,105 +114,34 @@ export default function AddNoteModal({
editable={!isSubmitting}
/>
<View style={styles.buttonRow}>
<View className="flex-row gap-3 mt-5">
<TouchableOpacity
style={[
styles.button,
{
backgroundColor: theme.colors.backgroundTertiary,
borderColor: theme.colors.borderSubtle,
borderWidth: 1,
},
]}
className="flex-1 py-3.5 rounded-xl items-center justify-center min-h-[48px]"
style={{
backgroundColor: theme.colors.backgroundGlass,
borderWidth: 1,
borderColor: theme.colors.borderGlass,
}}
onPress={handleClose}
disabled={isSubmitting}
activeOpacity={0.7}
>
<Text
style={[
styles.buttonText,
{ color: theme.colors.textSecondary },
]}
>
<Text className="text-[15px] font-bold text-text-secondary">
Cancel
</Text>
</TouchableOpacity>
<TouchableOpacity
style={[
styles.button,
{
backgroundColor:
noteText.trim() && !isSubmitting
? theme.colors.actionPrimary
: theme.colors.backgroundTertiary,
},
]}
onPress={handleSubmit}
disabled={!noteText.trim() || isSubmitting}
>
{isSubmitting ? (
<ActivityIndicator
size="small"
color={theme.colors.textInverse}
/>
) : (
<Text
style={[
styles.buttonText,
{
color: noteText.trim()
? theme.colors.textInverse
: theme.colors.textTertiary,
},
]}
>
Submit
</Text>
)}
</TouchableOpacity>
<View className="flex-1">
<GradientButton
label="Submit"
onPress={handleSubmit}
loading={isSubmitting}
disabled={!noteText.trim() || isSubmitting}
/>
</View>
</View>
</View>
</KeyboardAvoidingView>
</Modal>
);
}
const styles: ReturnType<typeof StyleSheet.create> = StyleSheet.create({
overlay: {
flex: 1,
backgroundColor: "rgba(0,0,0,0.6)",
justifyContent: "flex-end",
},
container: {
borderTopLeftRadius: 20,
borderTopRightRadius: 20,
borderWidth: 1,
borderBottomWidth: 0,
padding: 20,
paddingBottom: 36,
},
input: {
minHeight: 120,
borderRadius: 10,
borderWidth: 1,
padding: 12,
fontSize: 15,
},
buttonRow: {
flexDirection: "row",
gap: 12,
marginTop: 16,
},
button: {
flex: 1,
paddingVertical: 14,
borderRadius: 12,
alignItems: "center",
justifyContent: "center",
minHeight: 48,
},
buttonText: {
fontSize: 15,
fontWeight: "700",
},
});

View File

@@ -1,18 +1,26 @@
import React from "react";
import { View, Text, StyleSheet, TouchableOpacity } from "react-native";
import { View, Text, TouchableOpacity } from "react-native";
import { Ionicons } from "@expo/vector-icons";
import { LinearGradient } from "expo-linear-gradient";
import { useTheme } from "../theme";
import { rgbToHex } from "../utils/color";
import { formatRelativeTime } from "../utils/date";
import ProjectBadge from "./ProjectBadge";
import GlassCard from "./GlassCard";
import type { AlertItem } from "../api/types";
interface AlertCardProps {
alert: AlertItem;
onPress: () => void;
projectName?: string;
muted?: boolean;
}
export default function AlertCard({
alert,
onPress,
projectName,
muted,
}: AlertCardProps): React.JSX.Element {
const { theme } = useTheme();
@@ -28,122 +36,102 @@ export default function AlertCard({
return (
<TouchableOpacity
style={[
styles.card,
{
backgroundColor: theme.colors.backgroundSecondary,
borderColor: theme.colors.borderSubtle,
},
]}
className="mb-3"
style={{
opacity: muted ? 0.55 : 1,
}}
onPress={onPress}
activeOpacity={0.7}
accessibilityRole="button"
accessibilityLabel={`Alert ${alert.alertNumberWithPrefix || alert.alertNumber}, ${alert.title}. State: ${alert.currentAlertState?.name ?? "unknown"}. Severity: ${alert.alertSeverity?.name ?? "unknown"}.`}
>
<View style={styles.topRow}>
<Text style={[styles.number, { color: theme.colors.textTertiary }]}>
{alert.alertNumberWithPrefix || `#${alert.alertNumber}`}
</Text>
<Text style={[styles.time, { color: theme.colors.textTertiary }]}>
{timeString}
</Text>
</View>
<GlassCard opaque>
<View className="flex-row">
<LinearGradient
colors={[stateColor, stateColor + "40"]}
start={{ x: 0.5, y: 0 }}
end={{ x: 0.5, y: 1 }}
style={{ width: 3 }}
/>
<View className="flex-1 p-4">
{projectName ? (
<View className="mb-2">
<ProjectBadge name={projectName} />
</View>
) : null}
<View className="flex-row justify-between items-center mb-2">
<View
className="px-2.5 py-0.5 rounded-full"
style={{ backgroundColor: theme.colors.backgroundTertiary }}
>
<Text className="text-[12px] font-semibold text-text-tertiary">
{alert.alertNumberWithPrefix || `#${alert.alertNumber}`}
</Text>
</View>
<Text className="text-[12px] text-text-tertiary">
{timeString}
</Text>
</View>
<Text
style={[
theme.typography.bodyLarge,
{ color: theme.colors.textPrimary, fontWeight: "600" },
]}
numberOfLines={2}
>
{alert.title}
</Text>
<View style={styles.badgeRow}>
{alert.currentAlertState ? (
<View
style={[
styles.badge,
{ backgroundColor: theme.colors.backgroundTertiary },
]}
>
<View style={[styles.dot, { backgroundColor: stateColor }]} />
<Text
style={[styles.badgeText, { color: theme.colors.textPrimary }]}
className="text-body-lg text-text-primary font-semibold mt-0.5"
numberOfLines={2}
>
{alert.currentAlertState.name}
{alert.title}
</Text>
</View>
) : null}
{alert.alertSeverity ? (
<View
style={[styles.badge, { backgroundColor: severityColor + "26" }]}
>
<Text style={[styles.badgeText, { color: severityColor }]}>
{alert.alertSeverity.name}
</Text>
</View>
) : null}
</View>
<View className="flex-row flex-wrap gap-2 mt-3">
{alert.currentAlertState ? (
<View
className="flex-row items-center px-2.5 py-1 rounded-full"
style={{
backgroundColor: theme.colors.backgroundTertiary,
}}
>
<View
className="w-2 h-2 rounded-full mr-1.5"
style={{ backgroundColor: stateColor }}
/>
<Text className="text-[12px] font-semibold text-text-primary">
{alert.currentAlertState.name}
</Text>
</View>
) : null}
{alert.monitor ? (
<Text
style={[styles.monitor, { color: theme.colors.textSecondary }]}
numberOfLines={1}
>
{alert.monitor.name}
</Text>
) : null}
{alert.alertSeverity ? (
<View
className="flex-row items-center px-2.5 py-1 rounded-full"
style={{ backgroundColor: severityColor + "15" }}
>
<Text
className="text-[12px] font-semibold"
style={{ color: severityColor }}
>
{alert.alertSeverity.name}
</Text>
</View>
) : null}
</View>
{alert.monitor ? (
<View className="flex-row items-center mt-3">
<Ionicons
name="desktop-outline"
size={12}
color={theme.colors.textTertiary}
style={{ marginRight: 5 }}
/>
<Text
className="text-[12px] text-text-secondary flex-1"
numberOfLines={1}
>
{alert.monitor.name}
</Text>
</View>
) : null}
</View>
</View>
</GlassCard>
</TouchableOpacity>
);
}
const styles: ReturnType<typeof StyleSheet.create> = StyleSheet.create({
card: {
padding: 16,
borderRadius: 12,
borderWidth: 1,
marginBottom: 12,
},
topRow: {
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
marginBottom: 6,
},
number: {
fontSize: 13,
fontWeight: "600",
},
time: {
fontSize: 12,
},
badgeRow: {
flexDirection: "row",
flexWrap: "wrap",
gap: 8,
marginTop: 10,
},
badge: {
flexDirection: "row",
alignItems: "center",
paddingHorizontal: 8,
paddingVertical: 4,
borderRadius: 6,
},
dot: {
width: 8,
height: 8,
borderRadius: 4,
marginRight: 6,
},
badgeText: {
fontSize: 12,
fontWeight: "600",
},
monitor: {
fontSize: 12,
marginTop: 8,
},
});

View File

@@ -1,6 +1,8 @@
import React from "react";
import { View, Text, StyleSheet } from "react-native";
import { View, Text } from "react-native";
import { Ionicons } from "@expo/vector-icons";
import { useTheme } from "../theme";
import GradientButton from "./GradientButton";
type EmptyIcon = "incidents" | "alerts" | "episodes" | "notes" | "default";
@@ -8,178 +10,75 @@ interface EmptyStateProps {
title: string;
subtitle?: string;
icon?: EmptyIcon;
actionLabel?: string;
onAction?: () => void;
}
function EmptyIcon({
icon,
color,
}: {
icon: EmptyIcon;
color: string;
}): React.JSX.Element {
/*
* Simple geometric SVG-style icons using View primitives
* Monochrome, clean, professional — not cartoon/playful
*/
if (icon === "incidents") {
return (
<View style={styles.iconContainer}>
<View style={[styles.iconShield, { borderColor: color }]}>
<View style={[styles.iconCheckmark, { backgroundColor: color }]} />
</View>
</View>
);
}
if (icon === "alerts") {
return (
<View style={styles.iconContainer}>
<View style={[styles.iconBell, { borderColor: color }]}>
<View style={[styles.iconBellClapper, { backgroundColor: color }]} />
</View>
</View>
);
}
if (icon === "episodes") {
return (
<View style={styles.iconContainer}>
<View style={[styles.iconStack, { borderColor: color }]} />
<View
style={[styles.iconStackBack, { borderColor: color, opacity: 0.4 }]}
/>
</View>
);
}
// Default: simple circle with line through it
return (
<View style={styles.iconContainer}>
<View style={[styles.iconCircle, { borderColor: color }]}>
<View style={[styles.iconLine, { backgroundColor: color }]} />
</View>
</View>
);
}
const iconMap: Record<EmptyIcon, keyof typeof Ionicons.glyphMap> = {
incidents: "warning-outline",
alerts: "notifications-outline",
episodes: "layers-outline",
notes: "document-text-outline",
default: "remove-circle-outline",
};
export default function EmptyState({
title,
subtitle,
icon = "default",
actionLabel,
onAction,
}: EmptyStateProps): React.JSX.Element {
const { theme } = useTheme();
return (
<View style={styles.container}>
<EmptyIcon icon={icon} color={theme.colors.textTertiary} />
<View className="flex-1 items-center justify-center px-10 py-28">
{/* Outer gradient glow ring */}
<View className="w-28 h-28 rounded-full items-center justify-center overflow-hidden">
<View
style={{
position: "absolute",
top: 0,
left: 0,
right: 0,
bottom: 0,
backgroundColor: theme.colors.surfaceGlow,
borderRadius: 56,
}}
/>
{/* Inner icon container */}
<View
className="w-20 h-20 rounded-full items-center justify-center"
style={{
backgroundColor: theme.colors.backgroundTertiary,
}}
>
<Ionicons
name={iconMap[icon]}
size={36}
color={theme.colors.textSecondary}
/>
</View>
</View>
<Text
style={[
theme.typography.titleSmall,
{
color: theme.colors.textPrimary,
textAlign: "center",
marginTop: 20,
},
]}
className="text-title-md text-text-primary text-center mt-7"
style={{ letterSpacing: -0.3 }}
>
{title}
</Text>
{subtitle ? (
<Text
style={[
theme.typography.bodyMedium,
{
color: theme.colors.textSecondary,
textAlign: "center",
marginTop: theme.spacing.sm,
lineHeight: 20,
},
]}
>
<Text className="text-body-md text-text-secondary text-center mt-2.5 leading-6 max-w-[280px]">
{subtitle}
</Text>
) : null}
{actionLabel && onAction ? (
<View className="mt-6 w-[200px]">
<GradientButton label={actionLabel} onPress={onAction} />
</View>
) : null}
</View>
);
}
const styles: ReturnType<typeof StyleSheet.create> = StyleSheet.create({
container: {
flex: 1,
alignItems: "center",
justifyContent: "center",
paddingHorizontal: 40,
paddingVertical: 60,
},
iconContainer: {
width: 64,
height: 64,
alignItems: "center",
justifyContent: "center",
},
// Shield icon (incidents)
iconShield: {
width: 44,
height: 52,
borderWidth: 2,
borderRadius: 6,
borderBottomLeftRadius: 22,
borderBottomRightRadius: 22,
alignItems: "center",
justifyContent: "center",
},
iconCheckmark: {
width: 16,
height: 3,
borderRadius: 1.5,
transform: [{ rotate: "-45deg" }],
},
// Bell icon (alerts)
iconBell: {
width: 36,
height: 36,
borderWidth: 2,
borderRadius: 18,
borderBottomLeftRadius: 4,
borderBottomRightRadius: 4,
alignItems: "center",
justifyContent: "flex-end",
paddingBottom: 4,
},
iconBellClapper: {
width: 8,
height: 8,
borderRadius: 4,
},
// Stack icon (episodes)
iconStack: {
width: 40,
height: 32,
borderWidth: 2,
borderRadius: 8,
position: "absolute",
top: 12,
},
iconStackBack: {
width: 32,
height: 28,
borderWidth: 2,
borderRadius: 6,
position: "absolute",
top: 6,
},
// Default circle icon
iconCircle: {
width: 48,
height: 48,
borderWidth: 2,
borderRadius: 24,
alignItems: "center",
justifyContent: "center",
},
iconLine: {
width: 20,
height: 2,
borderRadius: 1,
},
});

View File

@@ -1,8 +1,11 @@
import React from "react";
import { View, Text, StyleSheet, TouchableOpacity } from "react-native";
import { View, Text, TouchableOpacity } from "react-native";
import { LinearGradient } from "expo-linear-gradient";
import { useTheme } from "../theme";
import { rgbToHex } from "../utils/color";
import { formatRelativeTime } from "../utils/date";
import ProjectBadge from "./ProjectBadge";
import GlassCard from "./GlassCard";
import type {
IncidentEpisodeItem,
AlertEpisodeItem,
@@ -14,17 +17,21 @@ type EpisodeCardProps =
episode: IncidentEpisodeItem;
type: "incident";
onPress: () => void;
projectName?: string;
muted?: boolean;
}
| {
episode: AlertEpisodeItem;
type: "alert";
onPress: () => void;
projectName?: string;
muted?: boolean;
};
export default function EpisodeCard(
props: EpisodeCardProps,
): React.JSX.Element {
const { episode, type, onPress } = props;
const { episode, type, onPress, projectName, muted } = props;
const { theme } = useTheme();
const state: NamedEntityWithColor =
@@ -56,120 +63,96 @@ export default function EpisodeCard(
return (
<TouchableOpacity
style={[
styles.card,
{
backgroundColor: theme.colors.backgroundSecondary,
borderColor: theme.colors.borderSubtle,
},
]}
className="mb-3"
style={{
opacity: muted ? 0.55 : 1,
}}
onPress={onPress}
activeOpacity={0.7}
>
<View style={styles.topRow}>
<Text style={[styles.number, { color: theme.colors.textTertiary }]}>
{episode.episodeNumberWithPrefix || `#${episode.episodeNumber}`}
</Text>
<Text style={[styles.time, { color: theme.colors.textTertiary }]}>
{timeString}
</Text>
</View>
<GlassCard opaque>
<View className="flex-row">
<LinearGradient
colors={[stateColor, stateColor + "40"]}
start={{ x: 0.5, y: 0 }}
end={{ x: 0.5, y: 1 }}
style={{ width: 3 }}
/>
<View className="flex-1 p-4">
{projectName ? (
<View className="mb-2">
<ProjectBadge name={projectName} />
</View>
) : null}
<View className="flex-row justify-between items-center mb-2">
<View
className="px-2.5 py-0.5 rounded-full"
style={{ backgroundColor: theme.colors.backgroundTertiary }}
>
<Text className="text-[12px] font-semibold text-text-tertiary">
{episode.episodeNumberWithPrefix ||
`#${episode.episodeNumber}`}
</Text>
</View>
<Text className="text-[12px] text-text-tertiary">
{timeString}
</Text>
</View>
<Text
style={[
theme.typography.bodyLarge,
{ color: theme.colors.textPrimary, fontWeight: "600" },
]}
numberOfLines={2}
>
{episode.title}
</Text>
<View style={styles.badgeRow}>
{state ? (
<View
style={[
styles.badge,
{ backgroundColor: theme.colors.backgroundTertiary },
]}
>
<View style={[styles.dot, { backgroundColor: stateColor }]} />
<Text
style={[styles.badgeText, { color: theme.colors.textPrimary }]}
className="text-body-lg text-text-primary font-semibold mt-0.5"
numberOfLines={2}
>
{state.name}
{episode.title}
</Text>
</View>
) : null}
{severity ? (
<View
style={[styles.badge, { backgroundColor: severityColor + "26" }]}
>
<Text style={[styles.badgeText, { color: severityColor }]}>
{severity.name}
</Text>
</View>
) : null}
</View>
<View className="flex-row flex-wrap gap-2 mt-3">
{state ? (
<View
className="flex-row items-center px-2.5 py-1 rounded-full"
style={{
backgroundColor: theme.colors.backgroundTertiary,
}}
>
<View
className="w-2 h-2 rounded-full mr-1.5"
style={{ backgroundColor: stateColor }}
/>
<Text className="text-[12px] font-semibold text-text-primary">
{state.name}
</Text>
</View>
) : null}
{childCount > 0 ? (
<Text
style={[styles.childCount, { color: theme.colors.textSecondary }]}
>
{childCount} {type === "incident" ? "incident" : "alert"}
{childCount !== 1 ? "s" : ""}
</Text>
) : null}
{severity ? (
<View
className="flex-row items-center px-2.5 py-1 rounded-full"
style={{ backgroundColor: severityColor + "15" }}
>
<Text
className="text-[12px] font-semibold"
style={{ color: severityColor }}
>
{severity.name}
</Text>
</View>
) : null}
{childCount > 0 ? (
<View
className="flex-row items-center px-2.5 py-1 rounded-full"
style={{ backgroundColor: theme.colors.backgroundTertiary }}
>
<Text className="text-[12px] font-semibold text-text-secondary">
{childCount} {type === "incident" ? "incident" : "alert"}
{childCount !== 1 ? "s" : ""}
</Text>
</View>
) : null}
</View>
</View>
</View>
</GlassCard>
</TouchableOpacity>
);
}
const styles: ReturnType<typeof StyleSheet.create> = StyleSheet.create({
card: {
padding: 16,
borderRadius: 12,
borderWidth: 1,
marginBottom: 12,
},
topRow: {
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
marginBottom: 6,
},
number: {
fontSize: 13,
fontWeight: "600",
},
time: {
fontSize: 12,
},
badgeRow: {
flexDirection: "row",
flexWrap: "wrap",
gap: 8,
marginTop: 10,
},
badge: {
flexDirection: "row",
alignItems: "center",
paddingHorizontal: 8,
paddingVertical: 4,
borderRadius: 6,
},
dot: {
width: 8,
height: 8,
borderRadius: 4,
marginRight: 6,
},
badgeText: {
fontSize: 12,
fontWeight: "600",
},
childCount: {
fontSize: 12,
marginTop: 8,
},
});

View File

@@ -0,0 +1,89 @@
import React from "react";
import { View, Text } from "react-native";
import { useTheme } from "../theme";
import { rgbToHex } from "../utils/color";
import { formatDateTime } from "../utils/date";
import type { FeedItem } from "../api/types";
interface FeedTimelineProps {
feed: FeedItem[];
}
function stripMarkdown(md: string): string {
return md
.replace(/\*\*(.*?)\*\*/g, "$1")
.replace(/\*(.*?)\*/g, "$1")
.replace(/`(.*?)`/g, "$1")
.replace(/#{1,6}\s/g, "")
.replace(/\[([^\]]+)\]\([^)]+\)/g, "$1")
.replace(/\n{2,}/g, "\n")
.trim();
}
export default function FeedTimeline({
feed,
}: FeedTimelineProps): React.JSX.Element {
const { theme } = useTheme();
return (
<View className="ml-1">
{feed.map((entry: FeedItem, index: number) => {
const entryColor: string = entry.displayColor
? rgbToHex(entry.displayColor)
: theme.colors.textTertiary;
const isLast: boolean = index === feed.length - 1;
const timeString: string = formatDateTime(
entry.postedAt || entry.createdAt,
);
const mainText: string = stripMarkdown(entry.feedInfoInMarkdown);
const moreText: string | undefined = entry.moreInformationInMarkdown
? stripMarkdown(entry.moreInformationInMarkdown)
: undefined;
return (
<View key={entry._id} className="flex-row">
{/* Timeline connector */}
<View className="items-center mr-3.5">
<View
className="w-3 h-3 rounded-full mt-0.5"
style={{
backgroundColor: entryColor,
shadowColor: entryColor,
shadowOpacity: 0.3,
shadowOffset: { width: 0, height: 1 },
shadowRadius: 4,
elevation: 2,
}}
/>
{!isLast ? (
<View
className="w-0.5 flex-1 my-1.5"
style={{
backgroundColor: theme.colors.borderDefault,
}}
/>
) : null}
</View>
{/* Content */}
<View className="flex-1 pb-5">
<Text className="text-body-md text-text-primary leading-5">
{mainText}
</Text>
{moreText ? (
<Text
className="text-body-sm text-text-secondary mt-1.5 leading-5"
numberOfLines={3}
>
{moreText}
</Text>
) : null}
<Text className="text-body-sm text-text-tertiary mt-1.5">
{timeString}
</Text>
</View>
</View>
);
})}
</View>
);
}

View File

@@ -0,0 +1,40 @@
import React from "react";
import { View, ViewStyle } from "react-native";
import { useTheme } from "../theme";
interface GlassCardProps {
children: React.ReactNode;
style?: ViewStyle;
opaque?: boolean;
}
export default function GlassCard({
children,
style,
opaque = false,
}: GlassCardProps): React.JSX.Element {
const { theme } = useTheme();
return (
<View
className="rounded-2xl overflow-hidden"
style={[
{
backgroundColor: opaque
? theme.colors.backgroundElevated
: theme.colors.backgroundGlass,
borderWidth: 1,
borderColor: theme.colors.borderGlass,
shadowColor: "#000000",
shadowOpacity: theme.isDark ? 0.3 : 0.08,
shadowOffset: { width: 0, height: 2 },
shadowRadius: 12,
elevation: 3,
},
style,
]}
>
{children}
</View>
);
}

View File

@@ -0,0 +1,127 @@
import React from "react";
import {
TouchableOpacity,
Text,
ActivityIndicator,
View,
ViewStyle,
} from "react-native";
import { LinearGradient } from "expo-linear-gradient";
import { Ionicons } from "@expo/vector-icons";
import { useTheme } from "../theme";
interface GradientButtonProps {
label: string;
onPress: () => void;
loading?: boolean;
disabled?: boolean;
icon?: keyof typeof Ionicons.glyphMap;
variant?: "primary" | "secondary";
style?: ViewStyle;
}
export default function GradientButton({
label,
onPress,
loading = false,
disabled = false,
icon,
variant = "primary",
style,
}: GradientButtonProps): React.JSX.Element {
const { theme } = useTheme();
if (variant === "secondary") {
return (
<TouchableOpacity
className="h-[52px] rounded-xl items-center justify-center overflow-hidden"
style={[
{
backgroundColor: theme.colors.backgroundGlass,
borderWidth: 1,
borderColor: theme.colors.borderGlass,
opacity: disabled || loading ? 0.6 : 1,
},
style,
]}
onPress={onPress}
disabled={disabled || loading}
activeOpacity={0.7}
>
<View className="flex-row items-center">
{loading ? (
<ActivityIndicator color={theme.colors.textPrimary} />
) : (
<>
{icon ? (
<Ionicons
name={icon}
size={18}
color={theme.colors.textPrimary}
style={{ marginRight: 8 }}
/>
) : null}
<Text
className="text-[16px] font-bold"
style={{ color: theme.colors.textPrimary }}
>
{label}
</Text>
</>
)}
</View>
</TouchableOpacity>
);
}
return (
<TouchableOpacity
className="h-[52px] rounded-xl overflow-hidden"
style={[
{
opacity: disabled || loading ? 0.6 : 1,
shadowColor: theme.colors.accentGradientStart,
shadowOpacity: theme.isDark ? 0.15 : 0.2,
shadowOffset: { width: 0, height: 4 },
shadowRadius: 12,
elevation: 4,
},
style,
]}
onPress={onPress}
disabled={disabled || loading}
activeOpacity={0.85}
>
<LinearGradient
colors={[
theme.colors.accentGradientStart,
theme.colors.accentGradientEnd,
]}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
className="flex-1 items-center justify-center flex-row"
>
{loading ? (
<ActivityIndicator color={theme.colors.textInverse} />
) : (
<>
{icon ? (
<Ionicons
name={icon}
size={18}
color={theme.colors.textInverse}
style={{ marginRight: 8 }}
/>
) : null}
<Text
className="text-[16px] font-bold"
style={{ color: theme.colors.textInverse }}
>
{label}
</Text>
</>
)}
</LinearGradient>
</TouchableOpacity>
);
}

View File

@@ -0,0 +1,32 @@
import React from "react";
import { LinearGradient } from "expo-linear-gradient";
import { useTheme } from "../theme";
interface GradientHeaderProps {
height?: number;
children?: React.ReactNode;
}
export default function GradientHeader({
height = 320,
children,
}: GradientHeaderProps): React.JSX.Element {
const { theme } = useTheme();
return (
<LinearGradient
colors={[theme.colors.gradientStart, theme.colors.gradientEnd]}
start={{ x: 0.5, y: 0 }}
end={{ x: 0.5, y: 1 }}
style={{
position: "absolute",
top: 0,
left: 0,
right: 0,
height,
}}
>
{children}
</LinearGradient>
);
}

View File

@@ -1,18 +1,26 @@
import React from "react";
import { View, Text, StyleSheet, TouchableOpacity } from "react-native";
import { View, Text, TouchableOpacity } from "react-native";
import { Ionicons } from "@expo/vector-icons";
import { LinearGradient } from "expo-linear-gradient";
import { useTheme } from "../theme";
import { rgbToHex } from "../utils/color";
import { formatRelativeTime } from "../utils/date";
import ProjectBadge from "./ProjectBadge";
import GlassCard from "./GlassCard";
import type { IncidentItem, NamedEntity } from "../api/types";
interface IncidentCardProps {
incident: IncidentItem;
onPress: () => void;
projectName?: string;
muted?: boolean;
}
export default function IncidentCard({
incident,
onPress,
projectName,
muted,
}: IncidentCardProps): React.JSX.Element {
const { theme } = useTheme();
@@ -31,126 +39,107 @@ export default function IncidentCard({
return (
<TouchableOpacity
style={[
styles.card,
{
backgroundColor: theme.colors.backgroundSecondary,
borderColor: theme.colors.borderSubtle,
},
]}
className="mb-3"
style={{
opacity: muted ? 0.55 : 1,
}}
onPress={onPress}
activeOpacity={0.7}
accessibilityRole="button"
accessibilityLabel={`Incident ${incident.incidentNumberWithPrefix || incident.incidentNumber}, ${incident.title}. State: ${incident.currentIncidentState?.name ?? "unknown"}. Severity: ${incident.incidentSeverity?.name ?? "unknown"}.`}
>
<View style={styles.topRow}>
<Text style={[styles.number, { color: theme.colors.textTertiary }]}>
{incident.incidentNumberWithPrefix || `#${incident.incidentNumber}`}
</Text>
<Text style={[styles.time, { color: theme.colors.textTertiary }]}>
{timeString}
</Text>
</View>
<GlassCard opaque>
<View className="flex-row">
<LinearGradient
colors={[stateColor, stateColor + "40"]}
start={{ x: 0.5, y: 0 }}
end={{ x: 0.5, y: 1 }}
style={{ width: 3 }}
/>
<View className="flex-1 p-4">
{projectName ? (
<View className="mb-2">
<ProjectBadge name={projectName} />
</View>
) : null}
<View className="flex-row justify-between items-center mb-2">
<View
className="px-2.5 py-0.5 rounded-full"
style={{ backgroundColor: theme.colors.backgroundTertiary }}
>
<Text className="text-[12px] font-semibold text-text-tertiary">
{incident.incidentNumberWithPrefix ||
`#${incident.incidentNumber}`}
</Text>
</View>
<Text className="text-[12px] text-text-tertiary">
{timeString}
</Text>
</View>
<Text
style={[
theme.typography.bodyLarge,
{ color: theme.colors.textPrimary, fontWeight: "600" },
]}
numberOfLines={2}
>
{incident.title}
</Text>
<View style={styles.badgeRow}>
{incident.currentIncidentState ? (
<View
style={[
styles.badge,
{ backgroundColor: theme.colors.backgroundTertiary },
]}
>
<View style={[styles.dot, { backgroundColor: stateColor }]} />
<Text
style={[styles.badgeText, { color: theme.colors.textPrimary }]}
className="text-body-lg text-text-primary font-semibold mt-0.5"
numberOfLines={2}
>
{incident.currentIncidentState.name}
{incident.title}
</Text>
</View>
) : null}
{incident.incidentSeverity ? (
<View
style={[styles.badge, { backgroundColor: severityColor + "26" }]}
>
<Text style={[styles.badgeText, { color: severityColor }]}>
{incident.incidentSeverity.name}
</Text>
</View>
) : null}
</View>
<View className="flex-row flex-wrap gap-2 mt-3">
{incident.currentIncidentState ? (
<View
className="flex-row items-center px-2.5 py-1 rounded-full"
style={{
backgroundColor: theme.colors.backgroundTertiary,
}}
>
<View
className="w-2 h-2 rounded-full mr-1.5"
style={{ backgroundColor: stateColor }}
/>
<Text className="text-[12px] font-semibold text-text-primary">
{incident.currentIncidentState.name}
</Text>
</View>
) : null}
{monitorCount > 0 ? (
<Text
style={[styles.monitors, { color: theme.colors.textSecondary }]}
numberOfLines={1}
>
{incident.monitors
.map((m: NamedEntity) => {
return m.name;
})
.join(", ")}
</Text>
) : null}
{incident.incidentSeverity ? (
<View
className="flex-row items-center px-2.5 py-1 rounded-full"
style={{ backgroundColor: severityColor + "15" }}
>
<Text
className="text-[12px] font-semibold"
style={{ color: severityColor }}
>
{incident.incidentSeverity.name}
</Text>
</View>
) : null}
</View>
{monitorCount > 0 ? (
<View className="flex-row items-center mt-3">
<Ionicons
name="desktop-outline"
size={12}
color={theme.colors.textTertiary}
style={{ marginRight: 5 }}
/>
<Text
className="text-[12px] text-text-secondary flex-1"
numberOfLines={1}
>
{incident.monitors
.map((m: NamedEntity) => {
return m.name;
})
.join(", ")}
</Text>
</View>
) : null}
</View>
</View>
</GlassCard>
</TouchableOpacity>
);
}
const styles: ReturnType<typeof StyleSheet.create> = StyleSheet.create({
card: {
padding: 16,
borderRadius: 12,
borderWidth: 1,
marginBottom: 12,
},
topRow: {
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
marginBottom: 6,
},
number: {
fontSize: 13,
fontWeight: "600",
},
time: {
fontSize: 12,
},
badgeRow: {
flexDirection: "row",
flexWrap: "wrap",
gap: 8,
marginTop: 10,
},
badge: {
flexDirection: "row",
alignItems: "center",
paddingHorizontal: 8,
paddingVertical: 4,
borderRadius: 6,
},
dot: {
width: 8,
height: 8,
borderRadius: 4,
marginRight: 6,
},
badgeText: {
fontSize: 12,
fontWeight: "600",
},
monitors: {
fontSize: 12,
marginTop: 8,
},
});

View File

@@ -0,0 +1,30 @@
import React from "react";
import { ViewStyle } from "react-native";
import Svg, { Path } from "react-native-svg";
interface LogoProps {
size?: number;
style?: ViewStyle;
}
export default function Logo({
size = 32,
style,
}: LogoProps): React.JSX.Element {
return (
<Svg width={size} height={size} viewBox="0 0 24 24" style={style}>
<Path
fill="#c1c0ff"
d="M12,14.19531c-0.17551-0.00004-0.34793-0.04618-0.5-0.13379l-9-5.19726C2.02161,8.58794,1.85779,7.97612,2.13411,7.49773C2.22187,7.34579,2.34806,7.2196,2.5,7.13184l9-5.19336c0.30964-0.17774,0.69036-0.17774,1,0l9,5.19336c0.4784,0.27632,0.64221,0.88814,0.36589,1.36653C21.77813,8.65031,21.65194,8.7765,21.5,8.86426l-9,5.19726C12.34793,14.14913,12.17551,14.19527,12,14.19531z"
/>
<Path
fill="#a2a1ff"
d="M21.5,11.13184l-1.96411-1.13337L12.5,14.06152c-0.30947,0.17839-0.69053,0.17839-1,0L4.46411,9.99847L2.5,11.13184c-0.47839,0.27632-0.64221,0.88814-0.36589,1.36653C2.22187,12.65031,2.34806,12.7765,2.5,12.86426l9,5.19726c0.30947,0.17838,0.69053,0.17838,1,0l9-5.19726c0.4784-0.27632,0.64221-0.88814,0.36589-1.36653C21.77813,11.34579,21.65194,11.2196,21.5,11.13184z"
/>
<Path
fill="#6563ff"
d="M21.5,15.13184l-1.96411-1.13337L12.5,18.06152c-0.30947,0.17838-0.69053,0.17838-1,0l-7.03589-4.06305L2.5,15.13184c-0.47839,0.27632-0.64221,0.88814-0.36589,1.36653C2.22187,16.65031,2.34806,16.7765,2.5,16.86426l9,5.19726c0.30947,0.17838,0.69053,0.17838,1,0l9-5.19726c0.4784-0.27632,0.64221-0.88814,0.36589-1.36653C21.77813,15.34579,21.65194,15.2196,21.5,15.13184z"
/>
</Svg>
);
}

View File

@@ -0,0 +1,109 @@
import React from "react";
import { View, Text, TouchableOpacity } from "react-native";
import { Ionicons } from "@expo/vector-icons";
import { LinearGradient } from "expo-linear-gradient";
import { useTheme } from "../theme";
import { formatDateTime } from "../utils/date";
import GlassCard from "./GlassCard";
import type { NoteItem } from "../api/types";
interface NotesSectionProps {
notes: NoteItem[] | undefined;
setNoteModalVisible: (visible: boolean) => void;
}
export default function NotesSection({
notes,
setNoteModalVisible,
}: NotesSectionProps): React.JSX.Element {
const { theme } = useTheme();
return (
<View className="mb-2">
<View className="flex-row justify-between items-center mb-3">
<View className="flex-row items-center">
<Ionicons
name="chatbubble-outline"
size={15}
color={theme.colors.textSecondary}
style={{ marginRight: 6 }}
/>
<Text className="text-[13px] font-semibold uppercase tracking-wide text-text-secondary">
Internal Notes
</Text>
</View>
<TouchableOpacity
className="flex-row items-center rounded-lg overflow-hidden"
onPress={() => {
return setNoteModalVisible(true);
}}
activeOpacity={0.85}
>
<LinearGradient
colors={[
theme.colors.accentGradientStart,
theme.colors.accentGradientEnd,
]}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
className="flex-row items-center px-3.5 py-2"
>
<Ionicons
name="add"
size={16}
color={theme.colors.textInverse}
style={{ marginRight: 4 }}
/>
<Text
className="text-[13px] font-semibold"
style={{ color: theme.colors.textInverse }}
>
Add Note
</Text>
</LinearGradient>
</TouchableOpacity>
</View>
{notes && notes.length > 0
? notes.map((note: NoteItem) => {
return (
<GlassCard
key={note._id}
style={{
marginBottom: 10,
borderTopWidth: 2,
borderTopColor: theme.colors.accentGradientStart + "30",
}}
>
<View className="p-4">
<Text className="text-body-md text-text-primary leading-6">
{note.note}
</Text>
<View className="flex-row justify-between mt-2.5">
{note.createdByUser ? (
<Text className="text-body-sm text-text-tertiary">
{note.createdByUser.name}
</Text>
) : null}
<Text className="text-body-sm text-text-tertiary">
{formatDateTime(note.createdAt)}
</Text>
</View>
</View>
</GlassCard>
);
})
: null}
{notes && notes.length === 0 ? (
<GlassCard>
<View className="p-4 items-center">
<Text className="text-body-sm text-text-tertiary">
No notes yet.
</Text>
</View>
</GlassCard>
) : null}
</View>
);
}

View File

@@ -1,5 +1,6 @@
import React, { useEffect, useRef } from "react";
import { View, Text, StyleSheet, Animated } from "react-native";
import { View, Text, Animated } from "react-native";
import { Ionicons } from "@expo/vector-icons";
import { useTheme } from "../theme";
import { useNetworkStatus } from "../hooks/useNetworkStatus";
@@ -25,51 +26,28 @@ export default function OfflineBanner(): React.JSX.Element | null {
return (
<Animated.View
style={[
styles.container,
{
backgroundColor: theme.colors.statusError,
transform: [{ translateY: slideAnim }],
},
]}
className="absolute top-0 left-0 right-0 z-[100] pt-[50px] pb-2.5 px-4"
style={{
backgroundColor: theme.colors.statusError,
transform: [{ translateY: slideAnim }],
shadowColor: theme.colors.statusError,
shadowOpacity: 0.3,
shadowOffset: { width: 0, height: 4 },
shadowRadius: 12,
elevation: 8,
}}
>
<View style={styles.content}>
<View style={styles.dot} />
<Text style={[styles.text, { color: "#FFFFFF" }]}>
<View className="flex-row items-center justify-center">
<Ionicons
name="cloud-offline-outline"
size={16}
color="#FFFFFF"
style={{ marginRight: 8, opacity: 0.9 }}
/>
<Text className="text-[13px] font-semibold tracking-tight text-white">
No internet connection
</Text>
</View>
</Animated.View>
);
}
const styles: ReturnType<typeof StyleSheet.create> = StyleSheet.create({
container: {
position: "absolute",
top: 0,
left: 0,
right: 0,
zIndex: 100,
paddingTop: 50,
paddingBottom: 8,
paddingHorizontal: 16,
},
content: {
flexDirection: "row",
alignItems: "center",
justifyContent: "center",
},
dot: {
width: 6,
height: 6,
borderRadius: 3,
backgroundColor: "#FFFFFF",
marginRight: 8,
opacity: 0.8,
},
text: {
fontSize: 13,
fontWeight: "600",
letterSpacing: 0.2,
},
});

View File

@@ -1,6 +1,5 @@
import React from "react";
import { View, Text, StyleSheet } from "react-native";
import { useTheme } from "../theme";
import { View, Text } from "react-native";
interface ProjectBadgeProps {
name: string;
@@ -11,35 +10,15 @@ export default function ProjectBadge({
name,
color,
}: ProjectBadgeProps): React.JSX.Element {
const { theme } = useTheme();
const dotColor: string = color || theme.colors.actionPrimary;
return (
<View style={styles.container}>
<View style={[styles.dot, { backgroundColor: dotColor }]} />
<Text
style={[
theme.typography.bodySmall,
{ color: theme.colors.textSecondary },
]}
numberOfLines={1}
>
<View className="flex-row items-center">
<View
className="w-2 h-2 rounded-full mr-1.5"
style={color ? { backgroundColor: color } : undefined}
/>
<Text className="text-body-sm text-text-secondary" numberOfLines={1}>
{name}
</Text>
</View>
);
}
const styles: ReturnType<typeof StyleSheet.create> = StyleSheet.create({
container: {
flexDirection: "row",
alignItems: "center",
},
dot: {
width: 8,
height: 8,
borderRadius: 4,
marginRight: 6,
},
});

View File

@@ -0,0 +1,29 @@
import React from "react";
import { View, Text } from "react-native";
import { Ionicons } from "@expo/vector-icons";
import { useTheme } from "../theme";
interface SectionHeaderProps {
title: string;
iconName: keyof typeof Ionicons.glyphMap;
}
export default function SectionHeader({
title,
iconName,
}: SectionHeaderProps): React.JSX.Element {
const { theme } = useTheme();
return (
<View className="flex-row items-center mb-3">
<Ionicons
name={iconName}
size={15}
color={theme.colors.textSecondary}
style={{ marginRight: 6 }}
/>
<Text className="text-[13px] font-semibold uppercase tracking-wide text-text-secondary">
{title}
</Text>
</View>
);
}

View File

@@ -0,0 +1,83 @@
import React from "react";
import { View, TouchableOpacity, Text } from "react-native";
import { LinearGradient } from "expo-linear-gradient";
import { useTheme } from "../theme";
interface Segment<T extends string> {
key: T;
label: string;
}
interface SegmentedControlProps<T extends string> {
segments: [Segment<T>, Segment<T>];
selected: T;
onSelect: (key: T) => void;
}
export default function SegmentedControl<T extends string>({
segments,
selected,
onSelect,
}: SegmentedControlProps<T>): React.JSX.Element {
const { theme } = useTheme();
return (
<View
className="flex-row mx-4 mt-3 mb-1 rounded-xl p-1"
style={{ backgroundColor: theme.colors.backgroundSecondary }}
>
{segments.map((segment: Segment<T>) => {
const isActive: boolean = segment.key === selected;
return (
<TouchableOpacity
key={segment.key}
className="flex-1 items-center py-2.5 rounded-[10px] overflow-hidden"
style={
isActive
? {
shadowColor: "#000000",
shadowOpacity: theme.isDark ? 0.4 : 0.15,
shadowOffset: { width: 0, height: 2 },
shadowRadius: 6,
elevation: 3,
}
: undefined
}
onPress={() => {
return onSelect(segment.key);
}}
activeOpacity={0.7}
>
{isActive ? (
<LinearGradient
colors={[
theme.colors.accentGradientStart,
theme.colors.accentGradientEnd,
]}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
style={{
position: "absolute",
top: 0,
left: 0,
right: 0,
bottom: 0,
}}
/>
) : null}
<Text
className="text-body-sm font-semibold"
style={{
color: isActive
? theme.colors.textInverse
: theme.colors.textTertiary,
}}
>
{segment.label}
</Text>
</TouchableOpacity>
);
})}
</View>
);
}

View File

@@ -1,5 +1,5 @@
import React from "react";
import { View, Text, StyleSheet } from "react-native";
import { View, Text } from "react-native";
import { useTheme } from "../theme";
export type SeverityLevel = "critical" | "major" | "minor" | "warning" | "info";
@@ -42,24 +42,16 @@ export default function SeverityBadge({
const displayLabel: string = label || severity;
return (
<View style={[styles.badge, { backgroundColor: colors.bg }]}>
<Text style={[styles.text, { color: colors.text }]}>
<View
className="px-2 py-1 rounded-md self-start"
style={{ backgroundColor: colors.bg }}
>
<Text
className="text-xs font-semibold tracking-wide"
style={{ color: colors.text }}
>
{displayLabel.toUpperCase()}
</Text>
</View>
);
}
const styles: ReturnType<typeof StyleSheet.create> = StyleSheet.create({
badge: {
paddingHorizontal: 8,
paddingVertical: 4,
borderRadius: 6,
alignSelf: "flex-start",
},
text: {
fontSize: 12,
fontWeight: "600",
letterSpacing: 0.5,
},
});

View File

@@ -1,7 +1,6 @@
import React, { useEffect, useRef } from "react";
import {
View,
StyleSheet,
Animated,
DimensionValue,
AccessibilityInfo,
@@ -36,13 +35,13 @@ export default function SkeletonCard({
const animation: Animated.CompositeAnimation = Animated.loop(
Animated.sequence([
Animated.timing(opacity, {
toValue: 0.7,
duration: 1000,
toValue: 0.6,
duration: 900,
useNativeDriver: true,
}),
Animated.timing(opacity, {
toValue: 0.3,
duration: 1000,
toValue: 0.25,
duration: 900,
useNativeDriver: true,
}),
]),
@@ -60,37 +59,38 @@ export default function SkeletonCard({
if (variant === "compact") {
return (
<Animated.View
style={[
styles.card,
{
backgroundColor: theme.colors.backgroundSecondary,
borderColor: theme.colors.borderSubtle,
opacity,
},
]}
className="rounded-2xl mb-3 overflow-hidden"
style={{
backgroundColor: theme.colors.backgroundElevated,
borderWidth: 1,
borderColor: theme.colors.borderSubtle,
opacity,
}}
accessibilityLabel="Loading content"
accessibilityRole="progressbar"
>
<View style={styles.compactRow}>
<View className="flex-row">
<View
style={[
styles.compactBadge,
{ backgroundColor: theme.colors.backgroundTertiary },
]}
/>
<View
style={[
styles.compactTime,
{ backgroundColor: theme.colors.backgroundTertiary },
]}
className="w-1"
style={{ backgroundColor: theme.colors.backgroundTertiary }}
/>
<View className="flex-1 p-4">
<View className="flex-row justify-between items-center mb-2.5">
<View
className="h-4 w-14 rounded-full"
style={{ backgroundColor: theme.colors.backgroundTertiary }}
/>
<View
className="h-3 w-8 rounded"
style={{ backgroundColor: theme.colors.backgroundTertiary }}
/>
</View>
<View
className="h-[18px] rounded w-3/4 mb-3"
style={{ backgroundColor: theme.colors.backgroundTertiary }}
/>
</View>
</View>
<View
style={[
styles.titleLine,
{ backgroundColor: theme.colors.backgroundTertiary, width: "75%" },
]}
/>
</Animated.View>
);
}
@@ -98,56 +98,56 @@ export default function SkeletonCard({
if (variant === "detail") {
return (
<Animated.View
style={[styles.detailContainer, { opacity }]}
className="p-5"
style={{ opacity }}
accessibilityLabel="Loading content"
accessibilityRole="progressbar"
>
{/* Badge row */}
<View style={styles.badgeRow}>
{/* Header area */}
<View
className="rounded-2xl p-5 mb-5"
style={{ backgroundColor: theme.colors.surfaceGlow }}
>
<View className="flex-row gap-2 mb-3">
<View
className="h-7 w-20 rounded-full"
style={{ backgroundColor: theme.colors.backgroundTertiary }}
/>
</View>
<View
style={[
styles.badgeSkeleton,
{ backgroundColor: theme.colors.backgroundTertiary },
]}
/>
<View
style={[
styles.badgeSkeleton,
{ backgroundColor: theme.colors.backgroundTertiary, width: 64 },
]}
className="h-7 w-4/5 rounded mb-3"
style={{ backgroundColor: theme.colors.backgroundTertiary }}
/>
<View className="flex-row gap-2">
<View
className="h-7 w-24 rounded-full"
style={{ backgroundColor: theme.colors.backgroundTertiary }}
/>
<View
className="h-7 w-16 rounded-full"
style={{ backgroundColor: theme.colors.backgroundTertiary }}
/>
</View>
</View>
{/* Title */}
{/* Details card */}
<View
style={[
styles.detailTitle,
{ backgroundColor: theme.colors.backgroundTertiary },
]}
/>
{/* Detail card */}
<View
style={[
styles.detailCard,
{
backgroundColor: theme.colors.backgroundSecondary,
borderColor: theme.colors.borderSubtle,
},
]}
className="rounded-2xl p-4"
style={{
backgroundColor: theme.colors.backgroundElevated,
borderWidth: 1,
borderColor: theme.colors.borderSubtle,
}}
>
{Array.from({ length: 3 }).map((_: unknown, index: number) => {
return (
<View key={index} style={styles.detailRow}>
<View key={index} className="flex-row mb-3">
<View
style={[
styles.detailLabel,
{ backgroundColor: theme.colors.backgroundTertiary },
]}
className="h-3.5 w-20 rounded mr-4"
style={{ backgroundColor: theme.colors.backgroundTertiary }}
/>
<View
style={[
styles.detailValue,
{ backgroundColor: theme.colors.backgroundTertiary },
]}
className="h-3.5 w-[120px] rounded"
style={{ backgroundColor: theme.colors.backgroundTertiary }}
/>
</View>
);
@@ -159,164 +159,62 @@ export default function SkeletonCard({
return (
<Animated.View
style={[
styles.card,
{
backgroundColor: theme.colors.backgroundSecondary,
borderColor: theme.colors.borderSubtle,
opacity,
},
]}
className="rounded-2xl mb-3 overflow-hidden"
style={{
backgroundColor: theme.colors.backgroundElevated,
borderWidth: 1,
borderColor: theme.colors.borderSubtle,
opacity,
}}
accessibilityLabel="Loading content"
accessibilityRole="progressbar"
>
{/* Top row: badge + time */}
<View style={styles.topRow}>
<View className="flex-row">
<View
style={[
styles.numberSkeleton,
{ backgroundColor: theme.colors.backgroundTertiary },
]}
className="w-1"
style={{ backgroundColor: theme.colors.backgroundTertiary }}
/>
<View
style={[
styles.timeSkeleton,
{ backgroundColor: theme.colors.backgroundTertiary },
]}
/>
</View>
{/* Title */}
<View
style={[
styles.titleLine,
{ backgroundColor: theme.colors.backgroundTertiary },
]}
/>
{/* Badge row */}
<View style={styles.badgeRow}>
<View
style={[
styles.badgeSkeleton,
{ backgroundColor: theme.colors.backgroundTertiary },
]}
/>
<View
style={[
styles.badgeSkeleton,
{ backgroundColor: theme.colors.backgroundTertiary, width: 56 },
]}
/>
</View>
{/* Body lines */}
{Array.from({ length: Math.max(lines - 1, 1) }).map(
(_: unknown, index: number) => {
return (
<View className="flex-1 p-4">
<View className="flex-row justify-between items-center mb-3">
<View
key={index}
style={[
styles.line,
{
backgroundColor: theme.colors.backgroundTertiary,
width: lineWidths[index % lineWidths.length],
},
]}
className="h-4 w-14 rounded-full"
style={{ backgroundColor: theme.colors.backgroundTertiary }}
/>
);
},
)}
<View
className="h-3 w-10 rounded"
style={{ backgroundColor: theme.colors.backgroundTertiary }}
/>
</View>
<View
className="h-[18px] rounded w-[70%] mb-3"
style={{ backgroundColor: theme.colors.backgroundTertiary }}
/>
<View className="flex-row gap-2 mb-3">
<View
className="h-7 w-24 rounded-full"
style={{ backgroundColor: theme.colors.backgroundTertiary }}
/>
<View
className="h-7 w-16 rounded-full"
style={{ backgroundColor: theme.colors.backgroundTertiary }}
/>
</View>
{Array.from({ length: Math.max(lines - 1, 1) }).map(
(_: unknown, index: number) => {
return (
<View
key={index}
className="h-3 rounded mb-2"
style={{
width: lineWidths[index % lineWidths.length],
backgroundColor: theme.colors.backgroundTertiary,
}}
/>
);
},
)}
</View>
</View>
</Animated.View>
);
}
const styles: ReturnType<typeof StyleSheet.create> = StyleSheet.create({
card: {
padding: 16,
borderRadius: 12,
borderWidth: 1,
marginBottom: 12,
},
topRow: {
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
marginBottom: 12,
},
numberSkeleton: {
height: 14,
width: 48,
borderRadius: 4,
},
timeSkeleton: {
height: 12,
width: 36,
borderRadius: 4,
},
titleLine: {
height: 18,
borderRadius: 4,
width: "70%",
marginBottom: 12,
},
badgeRow: {
flexDirection: "row",
gap: 8,
marginBottom: 12,
},
badgeSkeleton: {
height: 24,
width: 80,
borderRadius: 6,
},
line: {
height: 12,
borderRadius: 4,
marginBottom: 8,
},
// Compact variant
compactRow: {
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
marginBottom: 10,
},
compactBadge: {
height: 14,
width: 48,
borderRadius: 4,
},
compactTime: {
height: 12,
width: 32,
borderRadius: 4,
},
// Detail variant
detailContainer: {
padding: 20,
},
detailTitle: {
height: 24,
width: "80%",
borderRadius: 4,
marginBottom: 20,
},
detailCard: {
borderRadius: 12,
borderWidth: 1,
padding: 16,
},
detailRow: {
flexDirection: "row",
marginBottom: 12,
},
detailLabel: {
height: 14,
width: 80,
borderRadius: 4,
marginRight: 16,
},
detailValue: {
height: 14,
width: 120,
borderRadius: 4,
},
});

View File

@@ -1,5 +1,5 @@
import React from "react";
import { View, Text, StyleSheet } from "react-native";
import { View, Text } from "react-native";
import { useTheme } from "../theme";
export type StateType =
@@ -32,39 +32,14 @@ export default function StateBadge({
const displayLabel: string = label || state;
return (
<View
style={[
styles.badge,
{
backgroundColor: theme.colors.backgroundTertiary,
},
]}
>
<View style={[styles.dot, { backgroundColor: color }]} />
<Text style={[styles.text, { color: theme.colors.textPrimary }]}>
<View className="flex-row items-center px-2 py-1 rounded-md self-start bg-bg-tertiary">
<View
className="w-2 h-2 rounded-full mr-1.5"
style={{ backgroundColor: color }}
/>
<Text className="text-xs font-semibold text-text-primary">
{displayLabel.charAt(0).toUpperCase() + displayLabel.slice(1)}
</Text>
</View>
);
}
const styles: ReturnType<typeof StyleSheet.create> = StyleSheet.create({
badge: {
flexDirection: "row",
alignItems: "center",
paddingHorizontal: 8,
paddingVertical: 4,
borderRadius: 6,
alignSelf: "flex-start",
},
dot: {
width: 8,
height: 8,
borderRadius: 4,
marginRight: 6,
},
text: {
fontSize: 12,
fontWeight: "600",
},
});

View File

@@ -2,7 +2,6 @@ import React, { useRef } from "react";
import {
View,
Text,
StyleSheet,
Animated,
PanResponder,
type GestureResponderEvent,
@@ -48,7 +47,6 @@ export default function SwipeableCard({
_: GestureResponderEvent,
gestureState: PanResponderGestureState,
) => {
// Limit swipe range
const maxSwipe: number = 120;
let dx: number = gestureState.dx;
if (!rightAction && dx < 0) {
@@ -60,7 +58,6 @@ export default function SwipeableCard({
dx = Math.max(-maxSwipe, Math.min(maxSwipe, dx));
translateX.setValue(dx);
// Haptic feedback at threshold
if (Math.abs(dx) >= SWIPE_THRESHOLD && !hasTriggeredHaptic.current) {
hasTriggeredHaptic.current = true;
mediumImpact();
@@ -97,34 +94,38 @@ export default function SwipeableCard({
).current;
return (
<View style={styles.container}>
<View className="overflow-hidden rounded-xl">
{/* Background actions */}
<View style={styles.actionsContainer}>
<View className="absolute inset-0 flex-row justify-between items-center">
{leftAction ? (
<View
style={[styles.actionLeft, { backgroundColor: leftAction.color }]}
className="flex-1 h-full justify-center pl-5 rounded-xl"
style={{ backgroundColor: leftAction.color }}
>
<Text style={styles.actionText}>{leftAction.label}</Text>
<Text className="text-white text-sm font-bold tracking-tight">
{leftAction.label}
</Text>
</View>
) : null}
{rightAction ? (
<View
style={[styles.actionRight, { backgroundColor: rightAction.color }]}
className="flex-1 h-full justify-center items-end pr-5 rounded-xl"
style={{ backgroundColor: rightAction.color }}
>
<Text style={styles.actionText}>{rightAction.label}</Text>
<Text className="text-white text-sm font-bold tracking-tight">
{rightAction.label}
</Text>
</View>
) : null}
</View>
{/* Foreground content */}
<Animated.View
style={[
styles.foreground,
{
backgroundColor: theme.colors.backgroundPrimary,
transform: [{ translateX }],
},
]}
className="z-[1]"
style={{
transform: [{ translateX }],
backgroundColor: theme.colors.backgroundPrimary,
}}
{...panResponder.panHandlers}
>
{children}
@@ -132,40 +133,3 @@ export default function SwipeableCard({
</View>
);
}
const styles: ReturnType<typeof StyleSheet.create> = StyleSheet.create({
container: {
overflow: "hidden",
borderRadius: 12,
},
actionsContainer: {
...StyleSheet.absoluteFillObject,
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
},
actionLeft: {
flex: 1,
height: "100%",
justifyContent: "center",
paddingLeft: 20,
borderRadius: 12,
},
actionRight: {
flex: 1,
height: "100%",
justifyContent: "center",
alignItems: "flex-end",
paddingRight: 20,
borderRadius: 12,
},
actionText: {
color: "#FFFFFF",
fontSize: 14,
fontWeight: "700",
letterSpacing: 0.3,
},
foreground: {
zIndex: 1,
},
});

View File

@@ -3,8 +3,14 @@ import {
fetchAlertById,
fetchAlertStates,
fetchAlertStateTimeline,
fetchAlertFeed,
} from "../api/alerts";
import type { AlertItem, AlertState, StateTimelineItem } from "../api/types";
import type {
AlertItem,
AlertState,
StateTimelineItem,
FeedItem,
} from "../api/types";
export function useAlertDetail(
projectId: string,
@@ -43,3 +49,16 @@ export function useAlertStateTimeline(
enabled: Boolean(projectId) && Boolean(alertId),
});
}
export function useAlertFeed(
projectId: string,
alertId: string,
): UseQueryResult<FeedItem[], Error> {
return useQuery({
queryKey: ["alert-feed", projectId, alertId],
queryFn: () => {
return fetchAlertFeed(projectId, alertId);
},
enabled: Boolean(projectId) && Boolean(alertId),
});
}

View File

@@ -4,12 +4,14 @@ import {
fetchAlertEpisodeStates,
fetchAlertEpisodeStateTimeline,
fetchAlertEpisodeNotes,
fetchAlertEpisodeFeed,
} from "../api/alertEpisodes";
import type {
AlertEpisodeItem,
AlertState,
StateTimelineItem,
NoteItem,
FeedItem,
} from "../api/types";
export function useAlertEpisodeDetail(
@@ -62,3 +64,16 @@ export function useAlertEpisodeNotes(
enabled: Boolean(projectId) && Boolean(episodeId),
});
}
export function useAlertEpisodeFeed(
projectId: string,
episodeId: string,
): UseQueryResult<FeedItem[], Error> {
return useQuery({
queryKey: ["alert-episode-feed", projectId, episodeId],
queryFn: () => {
return fetchAlertEpisodeFeed(projectId, episodeId);
},
enabled: Boolean(projectId) && Boolean(episodeId),
});
}

View File

@@ -0,0 +1,68 @@
import { useMemo } from "react";
import { useQuery, UseQueryResult } from "@tanstack/react-query";
import { useProject } from "./useProject";
import { fetchAllAlertEpisodes } from "../api/alertEpisodes";
import type {
ListResponse,
AlertEpisodeItem,
ProjectAlertEpisodeItem,
ProjectItem,
} from "../api/types";
const FETCH_LIMIT: number = 100;
interface UseAllProjectAlertEpisodesResult {
items: ProjectAlertEpisodeItem[];
isLoading: boolean;
isError: boolean;
refetch: () => Promise<void>;
}
export function useAllProjectAlertEpisodes(): UseAllProjectAlertEpisodesResult {
const { projectList } = useProject();
const query: UseQueryResult<ListResponse<AlertEpisodeItem>, Error> = useQuery(
{
queryKey: ["alert-episodes", "all-projects"],
queryFn: () => {
return fetchAllAlertEpisodes({ skip: 0, limit: FETCH_LIMIT });
},
enabled: projectList.length > 0,
},
);
const projectMap: Map<string, string> = useMemo(() => {
const map: Map<string, string> = new Map();
projectList.forEach((p: ProjectItem) => {
map.set(p._id, p.name);
});
return map;
}, [projectList]);
const items: ProjectAlertEpisodeItem[] = useMemo(() => {
if (!query.data) {
return [];
}
return query.data.data.map(
(item: AlertEpisodeItem): ProjectAlertEpisodeItem => {
const pid: string = item.projectId ?? "";
return {
item,
projectId: pid,
projectName: projectMap.get(pid) ?? "",
};
},
);
}, [query.data, projectMap]);
const refetch: () => Promise<void> = async (): Promise<void> => {
await query.refetch();
};
return {
items,
isLoading: query.isPending,
isError: query.isError,
refetch,
};
}

View File

@@ -0,0 +1,45 @@
import { useMemo } from "react";
import { useQueries, UseQueryResult } from "@tanstack/react-query";
import { useProject } from "./useProject";
import { fetchAlertStates } from "../api/alerts";
import type { AlertState, ProjectItem } from "../api/types";
interface UseAllProjectAlertStatesResult {
statesMap: Map<string, AlertState[]>;
isLoading: boolean;
}
export function useAllProjectAlertStates(): UseAllProjectAlertStatesResult {
const { projectList } = useProject();
const queries: UseQueryResult<AlertState[], Error>[] = useQueries({
queries: projectList.map((project: ProjectItem) => {
return {
queryKey: ["alert-states", project._id],
queryFn: () => {
return fetchAlertStates(project._id);
},
enabled: Boolean(project._id),
};
}),
});
const isLoading: boolean = queries.some(
(q: UseQueryResult<AlertState[], Error>) => {
return q.isLoading;
},
);
const statesMap: Map<string, AlertState[]> = useMemo(() => {
const map: Map<string, AlertState[]> = new Map();
queries.forEach((q: UseQueryResult<AlertState[], Error>, index: number) => {
const project: ProjectItem | undefined = projectList[index];
if (project && q.data) {
map.set(project._id, q.data);
}
});
return map;
}, [queries, projectList]);
return { statesMap, isLoading };
}

View File

@@ -0,0 +1,64 @@
import { useMemo } from "react";
import { useQuery, UseQueryResult } from "@tanstack/react-query";
import { useProject } from "./useProject";
import { fetchAllAlerts } from "../api/alerts";
import type {
ListResponse,
AlertItem,
ProjectAlertItem,
ProjectItem,
} from "../api/types";
const FETCH_LIMIT: number = 100;
interface UseAllProjectAlertsResult {
items: ProjectAlertItem[];
isLoading: boolean;
isError: boolean;
refetch: () => Promise<void>;
}
export function useAllProjectAlerts(): UseAllProjectAlertsResult {
const { projectList } = useProject();
const query: UseQueryResult<ListResponse<AlertItem>, Error> = useQuery({
queryKey: ["alerts", "all-projects"],
queryFn: () => {
return fetchAllAlerts({ skip: 0, limit: FETCH_LIMIT });
},
enabled: projectList.length > 0,
});
const projectMap: Map<string, string> = useMemo(() => {
const map: Map<string, string> = new Map();
projectList.forEach((p: ProjectItem) => {
map.set(p._id, p.name);
});
return map;
}, [projectList]);
const items: ProjectAlertItem[] = useMemo(() => {
if (!query.data) {
return [];
}
return query.data.data.map((item: AlertItem): ProjectAlertItem => {
const pid: string = item.projectId ?? "";
return {
item,
projectId: pid,
projectName: projectMap.get(pid) ?? "",
};
});
}, [query.data, projectMap]);
const refetch: () => Promise<void> = async (): Promise<void> => {
await query.refetch();
};
return {
items,
isLoading: query.isPending,
isError: query.isError,
refetch,
};
}

View File

@@ -0,0 +1,104 @@
import { useQuery, UseQueryResult } from "@tanstack/react-query";
import { useProject } from "./useProject";
import { fetchAllIncidents } from "../api/incidents";
import { fetchAllAlerts } from "../api/alerts";
import { fetchAllIncidentEpisodes } from "../api/incidentEpisodes";
import { fetchAllAlertEpisodes } from "../api/alertEpisodes";
import type {
ListResponse,
IncidentItem,
AlertItem,
IncidentEpisodeItem,
AlertEpisodeItem,
} from "../api/types";
interface UseAllProjectCountsResult {
incidentCount: number;
alertCount: number;
incidentEpisodeCount: number;
alertEpisodeCount: number;
isLoading: boolean;
refetch: () => Promise<void>;
}
export function useAllProjectCounts(): UseAllProjectCountsResult {
const { projectList } = useProject();
const enabled: boolean = projectList.length > 0;
const incidentQuery: UseQueryResult<
ListResponse<IncidentItem>,
Error
> = useQuery({
queryKey: ["incidents", "unresolved-count", "all-projects"],
queryFn: () => {
return fetchAllIncidents({
skip: 0,
limit: 1,
unresolvedOnly: true,
});
},
enabled,
});
const alertQuery: UseQueryResult<ListResponse<AlertItem>, Error> = useQuery({
queryKey: ["alerts", "unresolved-count", "all-projects"],
queryFn: () => {
return fetchAllAlerts({ skip: 0, limit: 1, unresolvedOnly: true });
},
enabled,
});
const incidentEpisodeQuery: UseQueryResult<
ListResponse<IncidentEpisodeItem>,
Error
> = useQuery({
queryKey: ["incident-episodes", "unresolved-count", "all-projects"],
queryFn: () => {
return fetchAllIncidentEpisodes({
skip: 0,
limit: 1,
unresolvedOnly: true,
});
},
enabled,
});
const alertEpisodeQuery: UseQueryResult<
ListResponse<AlertEpisodeItem>,
Error
> = useQuery({
queryKey: ["alert-episodes", "unresolved-count", "all-projects"],
queryFn: () => {
return fetchAllAlertEpisodes({
skip: 0,
limit: 1,
unresolvedOnly: true,
});
},
enabled,
});
const isLoading: boolean =
incidentQuery.isPending ||
alertQuery.isPending ||
incidentEpisodeQuery.isPending ||
alertEpisodeQuery.isPending;
const refetch: () => Promise<void> = async (): Promise<void> => {
await Promise.all([
incidentQuery.refetch(),
alertQuery.refetch(),
incidentEpisodeQuery.refetch(),
alertEpisodeQuery.refetch(),
]);
};
return {
incidentCount: incidentQuery.data?.count ?? 0,
alertCount: alertQuery.data?.count ?? 0,
incidentEpisodeCount: incidentEpisodeQuery.data?.count ?? 0,
alertEpisodeCount: alertEpisodeQuery.data?.count ?? 0,
isLoading,
refetch,
};
}

View File

@@ -0,0 +1,69 @@
import { useMemo } from "react";
import { useQuery, UseQueryResult } from "@tanstack/react-query";
import { useProject } from "./useProject";
import { fetchAllIncidentEpisodes } from "../api/incidentEpisodes";
import type {
ListResponse,
IncidentEpisodeItem,
ProjectIncidentEpisodeItem,
ProjectItem,
} from "../api/types";
const FETCH_LIMIT: number = 100;
interface UseAllProjectIncidentEpisodesResult {
items: ProjectIncidentEpisodeItem[];
isLoading: boolean;
isError: boolean;
refetch: () => Promise<void>;
}
export function useAllProjectIncidentEpisodes(): UseAllProjectIncidentEpisodesResult {
const { projectList } = useProject();
const query: UseQueryResult<
ListResponse<IncidentEpisodeItem>,
Error
> = useQuery({
queryKey: ["incident-episodes", "all-projects"],
queryFn: () => {
return fetchAllIncidentEpisodes({ skip: 0, limit: FETCH_LIMIT });
},
enabled: projectList.length > 0,
});
const projectMap: Map<string, string> = useMemo(() => {
const map: Map<string, string> = new Map();
projectList.forEach((p: ProjectItem) => {
map.set(p._id, p.name);
});
return map;
}, [projectList]);
const items: ProjectIncidentEpisodeItem[] = useMemo(() => {
if (!query.data) {
return [];
}
return query.data.data.map(
(item: IncidentEpisodeItem): ProjectIncidentEpisodeItem => {
const pid: string = item.projectId ?? "";
return {
item,
projectId: pid,
projectName: projectMap.get(pid) ?? "",
};
},
);
}, [query.data, projectMap]);
const refetch: () => Promise<void> = async (): Promise<void> => {
await query.refetch();
};
return {
items,
isLoading: query.isPending,
isError: query.isError,
refetch,
};
}

View File

@@ -0,0 +1,47 @@
import { useMemo } from "react";
import { useQueries, UseQueryResult } from "@tanstack/react-query";
import { useProject } from "./useProject";
import { fetchIncidentStates } from "../api/incidents";
import type { IncidentState, ProjectItem } from "../api/types";
interface UseAllProjectIncidentStatesResult {
statesMap: Map<string, IncidentState[]>;
isLoading: boolean;
}
export function useAllProjectIncidentStates(): UseAllProjectIncidentStatesResult {
const { projectList } = useProject();
const queries: UseQueryResult<IncidentState[], Error>[] = useQueries({
queries: projectList.map((project: ProjectItem) => {
return {
queryKey: ["incident-states", project._id],
queryFn: () => {
return fetchIncidentStates(project._id);
},
enabled: Boolean(project._id),
};
}),
});
const isLoading: boolean = queries.some(
(q: UseQueryResult<IncidentState[], Error>) => {
return q.isLoading;
},
);
const statesMap: Map<string, IncidentState[]> = useMemo(() => {
const map: Map<string, IncidentState[]> = new Map();
queries.forEach(
(q: UseQueryResult<IncidentState[], Error>, index: number) => {
const project: ProjectItem | undefined = projectList[index];
if (project && q.data) {
map.set(project._id, q.data);
}
},
);
return map;
}, [queries, projectList]);
return { statesMap, isLoading };
}

View File

@@ -0,0 +1,64 @@
import { useMemo } from "react";
import { useQuery, UseQueryResult } from "@tanstack/react-query";
import { useProject } from "./useProject";
import { fetchAllIncidents } from "../api/incidents";
import type {
ListResponse,
IncidentItem,
ProjectIncidentItem,
ProjectItem,
} from "../api/types";
const FETCH_LIMIT: number = 100;
interface UseAllProjectIncidentsResult {
items: ProjectIncidentItem[];
isLoading: boolean;
isError: boolean;
refetch: () => Promise<void>;
}
export function useAllProjectIncidents(): UseAllProjectIncidentsResult {
const { projectList } = useProject();
const query: UseQueryResult<ListResponse<IncidentItem>, Error> = useQuery({
queryKey: ["incidents", "all-projects"],
queryFn: () => {
return fetchAllIncidents({ skip: 0, limit: FETCH_LIMIT });
},
enabled: projectList.length > 0,
});
const projectMap: Map<string, string> = useMemo(() => {
const map: Map<string, string> = new Map();
projectList.forEach((p: ProjectItem) => {
map.set(p._id, p.name);
});
return map;
}, [projectList]);
const items: ProjectIncidentItem[] = useMemo(() => {
if (!query.data) {
return [];
}
return query.data.data.map((item: IncidentItem): ProjectIncidentItem => {
const pid: string = item.projectId ?? "";
return {
item,
projectId: pid,
projectName: projectMap.get(pid) ?? "",
};
});
}, [query.data, projectMap]);
const refetch: () => Promise<void> = async (): Promise<void> => {
await query.refetch();
};
return {
items,
isLoading: query.isPending,
isError: query.isError,
refetch,
};
}

Some files were not shown because too many files have changed in this diff Show More