          (fn [[name value]]
            (create-field name (display-value value)))
          log-map)])))
+
+(react/defc Failures
+  {:get-initial-state
+   (fn []
+     {:expanded false})
+   :render
+   (fn [{:keys [props state]}]
+     [:div {}
+      (create-field
+        "Failures"
+        (style/create-link {:text (if (:expanded @state) "Hide" "Show")
+                            :onClick #(swap! state assoc :expanded (not (:expanded @state)))}))
+      (when (:expanded @state)
+        [comps/Tree {:start-collapsed? false
+                     :highlight-ends false
+                     :data (prewalk
+                             (fn [elem]
+                               (cond
+                                 ;; reorder map elements to message then causedBy
+                                 (map? elem) (select-keys elem ["message" "causedBy"])
+                                 ;; remove empty causedBy[] arrays - prewalk means this executes after the above
+                                 (= (second elem) []) nil
+                                 :else elem))
+                             (:data props))}])])})
 
 (react/defc CallDetail
   {:get-initial-state

     (let [wlogurl (str "gs://" bucketName "/" submission-id "/workflow.logs/workflow."
                        (workflow "id") ".log")]
       (create-field "Workflow Log" (display-value wlogurl (str "workflow." (workflow "id") ".log"))))]
    [WorkflowTiming {:label "Workflow Timing" :data raw-data :workflow-name workflow-name}]]

   [:div {:style {:marginTop "1em" :fontWeight 500}} "Calls:"]
   (for [[call data] (workflow "calls")]
     [CallDetail {:label call :data data :submission-id submission-id :bucketName bucketName :workflowId (workflow "id")}])])


        [:div {:style {:padding "0.25em 0 0.25em 1em"}}
         (for [[k v] (:data props)]
           [:div {} k [:span {:style {:margin "0 1em"}} "→"] (display-value v)])])])})

(react/defc WorkflowTiming
  {:get-initial-state
