- {taskRun.flow_run_name && taskRun.flow_run_id && (
+ {taskRun.flow_run_id ? (
- Flow Run
-
@@ -56,85 +56,84 @@ export const TaskRunDetails = ({ taskRun }: TaskRunDetailsProps) => {
- )}
-
- {taskRun.start_time && (
+ ) : (
- - Start Time
- - {formatTaskDate(taskRun.start_time)}
+ - Flow Run
+ - None
)}
- {taskRun.estimated_run_time !== null &&
- taskRun.estimated_run_time !== undefined && (
-
- - Duration
- -
-
-
- {formatTaskDuration(taskRun.total_run_time)}
-
-
-
- )}
-
- {taskRun.run_count !== null && taskRun.run_count !== undefined && (
-
- - Run Count
- - {taskRun.run_count.toString()}
-
- )}
+
+ - Start Time
+ -
+ {taskRun.start_time ? formatTaskDate(taskRun.start_time) : "None"}
+
+
- {taskRun.estimated_run_time !== null &&
- taskRun.estimated_run_time !== undefined && (
-
- - Estimated Run Time
- -
- {formatTaskDuration(taskRun.estimated_run_time)}
-
-
- )}
-
- {taskRun.created && (
-
- - Created
- - {formatTaskDate(taskRun.created)}
-
- )}
+
+ - Duration
+ -
+
+
+ {taskRun.total_run_time !== null &&
+ taskRun.total_run_time !== undefined
+ ? formatTaskDuration(taskRun.total_run_time)
+ : "None"}
+
+
+
- {taskRun.updated && (
-
- - Last Updated
- - {formatTaskDate(taskRun.updated)}
-
- )}
+
+ - Run Count
+ - {taskRun.run_count || 0}
+
- {taskRun.cache_key && (
-
- - Cache Key
- - {taskRun.cache_key}
-
- )}
+
+ - Estimated Run Time
+ -
+ {taskRun.estimated_run_time !== null &&
+ taskRun.estimated_run_time !== undefined
+ ? formatTaskDuration(taskRun.estimated_run_time)
+ : "None"}
+
+
- {taskRun.cache_expiration && (
-
- - Cache Expiration
- -
- {formatTaskDate(taskRun.cache_expiration)}
-
-
- )}
+
+ - Created
+ -
+ {taskRun.created ? formatTaskDate(taskRun.created) : "None"}
+
+
- {taskRun.dynamic_key && (
-
- - Dynamic Key
- - {taskRun.dynamic_key}
-
- )}
+
+ - Last Updated
+ -
+ {taskRun.updated ? formatTaskDate(taskRun.updated) : "None"}
+
+
+
+
+ - Cache Key
+ - {taskRun.cache_key || "None"}
+
+
+
+ - Cache Expiration
+ -
+ {taskRun.cache_expiration
+ ? formatTaskDate(taskRun.cache_expiration)
+ : "None"}
+
+
- - Task Run ID
- - {taskRun.id}
+ - Dynamic Key
+ - {taskRun.dynamic_key || "None"}
+
+
+
+ - Task Run ID
+ - {taskRun.id}
{resultArtifact?.description && (
@@ -159,30 +158,29 @@ export const TaskRunDetails = ({ taskRun }: TaskRunDetailsProps) => {
- - Retries
+ - Retries
-
- {taskRun.empirical_policy?.retries?.toString() || "0"}
+ {taskRun.empirical_policy?.retries ?? "0"}
- {typeof taskRun.empirical_policy?.retry_delay === "number" && (
-
- - Retry Delay
- -
- {formatTaskDuration(taskRun.empirical_policy.retry_delay)}
-
-
- )}
+
+ - Retry Delay
+ -
+ {typeof taskRun.empirical_policy?.retry_delay === "number"
+ ? formatTaskDuration(taskRun.empirical_policy.retry_delay)
+ : "None"}
+
+
- {taskRun.empirical_policy?.retry_jitter_factor !== null &&
- taskRun.empirical_policy?.retry_jitter_factor !== undefined && (
-
- - Retry Jitter Factor
- -
- {taskRun.empirical_policy.retry_jitter_factor.toString()}
-
-
- )}
+
+ - Retry Jitter Factor
+ -
+ {taskRun.empirical_policy?.retry_jitter_factor
+ ? taskRun.empirical_policy.retry_jitter_factor.toString()
+ : "None"}
+
+
- Tags
diff --git a/ui-v2/src/hooks/use-state-favicon.ts b/ui-v2/src/hooks/use-state-favicon.ts
new file mode 100644
index 000000000000..0047dd5d1db4
--- /dev/null
+++ b/ui-v2/src/hooks/use-state-favicon.ts
@@ -0,0 +1,66 @@
+import { useEffect } from "react";
+
+type StateType =
+ | "SCHEDULED"
+ | "PENDING"
+ | "RUNNING"
+ | "COMPLETED"
+ | "FAILED"
+ | "CANCELLED"
+ | "CANCELLING"
+ | "CRASHED"
+ | "PAUSED";
+
+function getPreferredColorScheme(): "dark" | "light" | "no-preference" {
+ if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
+ return "dark";
+ }
+ if (window.matchMedia("(prefers-color-scheme: light)").matches) {
+ return "light";
+ }
+ return "no-preference";
+}
+
+/**
+ * A hook that sets the browser favicon based on the provided state type.
+ * Resets the favicon to the default when the component unmounts.
+ *
+ * @param stateType - The state type to display in the favicon (e.g., "COMPLETED", "FAILED")
+ * @returns void
+ *
+ * @example
+ * ```tsx
+ * // Set favicon based on task run state
+ * useStateFavicon(taskRun.state_type);
+ * ```
+ */
+export function useStateFavicon(stateType: StateType | null | undefined): void {
+ useEffect(() => {
+ const colorScheme = getPreferredColorScheme();
+ const favicon16 =
+ colorScheme === "dark"
+ ? document.getElementById("favicon-16-dark")
+ : document.getElementById("favicon-16");
+ const favicon32 =
+ colorScheme === "dark"
+ ? document.getElementById("favicon-32-dark")
+ : document.getElementById("favicon-32");
+
+ if (stateType) {
+ const faviconPath = `/${stateType.toLowerCase()}.svg`;
+ favicon16?.setAttribute("href", faviconPath);
+ favicon32?.setAttribute("href", faviconPath);
+ }
+
+ return () => {
+ // Reset to default favicon on unmount
+ if (colorScheme === "dark") {
+ favicon16?.setAttribute("href", "/favicon-16x16-dark.png");
+ favicon32?.setAttribute("href", "/favicon-32x32-dark.png");
+ } else {
+ favicon16?.setAttribute("href", "/favicon-16x16.png");
+ favicon32?.setAttribute("href", "/favicon-32x32.png");
+ }
+ };
+ }, [stateType]);
+}