This is an automated email from the ASF dual-hosted git repository.

bbovenzi pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/main by this push:
     new 802705581cd AIP-38 | List Import Errors (#44021)
802705581cd is described below

commit 802705581cd2a59da49b379a868e007254849398
Author: Shubham Raj <[email protected]>
AuthorDate: Mon Nov 18 19:27:40 2024 +0530

    AIP-38 | List Import Errors (#44021)
    
    * create skeleton
    
    * added modal
    
    * add data in modal
    
    * refactor
    
    * time stamp
    
    * handling reviews
    
    * colour fix and search
    
    * add skeleton and colour fix
    
    * add error
    
    * handle reviews
---
 airflow/ui/src/pages/DagsList/DagCard.tsx          |   6 +-
 airflow/ui/src/pages/Dashboard/Dashboard.tsx       |   4 +
 .../ui/src/pages/Dashboard/{ => Health}/Health.tsx |   6 +-
 .../pages/Dashboard/{ => Health}/HealthSection.tsx |   0
 .../src/pages/Dashboard/{ => Health}/HealthTag.tsx |   0
 .../Dashboard/{Dashboard.tsx => Health/index.ts}   |  16 +--
 .../src/pages/Dashboard/Stats/DAGImportErrors.tsx  |  78 +++++++++++++
 .../pages/Dashboard/Stats/DAGImportErrorsModal.tsx | 125 +++++++++++++++++++++
 .../Dashboard/{Dashboard.tsx => Stats/Stats.tsx}   |  22 ++--
 .../Dashboard/{Dashboard.tsx => Stats/index.ts}    |  16 +--
 10 files changed, 226 insertions(+), 47 deletions(-)

diff --git a/airflow/ui/src/pages/DagsList/DagCard.tsx 
b/airflow/ui/src/pages/DagsList/DagCard.tsx
index a2c67b4eb8c..13f15735991 100644
--- a/airflow/ui/src/pages/DagsList/DagCard.tsx
+++ b/airflow/ui/src/pages/DagsList/DagCard.tsx
@@ -79,13 +79,13 @@ export const DagCard = ({ dag }: Props) => {
       </Flex>
       <SimpleGrid columns={4} gap={4} height={20} px={3} py={2}>
         <VStack align="flex-start" gap={1}>
-          <Heading color="gray.500" fontSize="xs">
+          <Heading color="fg.muted" fontSize="xs">
             Schedule
           </Heading>
           <Schedule dag={dag} />
         </VStack>
         <VStack align="flex-start" gap={1}>
-          <Heading color="gray.500" fontSize="xs">
+          <Heading color="fg.muted" fontSize="xs">
             Latest Run
           </Heading>
           {latestRun ? (
@@ -99,7 +99,7 @@ export const DagCard = ({ dag }: Props) => {
           ) : undefined}
         </VStack>
         <VStack align="flex-start" gap={1}>
-          <Heading color="gray.500" fontSize="xs">
+          <Heading color="fg.muted" fontSize="xs">
             Next Run
           </Heading>
           {Boolean(dag.next_dagrun) ? (
diff --git a/airflow/ui/src/pages/Dashboard/Dashboard.tsx 
b/airflow/ui/src/pages/Dashboard/Dashboard.tsx
index 38727b0809e..39131f1969c 100644
--- a/airflow/ui/src/pages/Dashboard/Dashboard.tsx
+++ b/airflow/ui/src/pages/Dashboard/Dashboard.tsx
@@ -20,6 +20,7 @@ import { Box, Heading } from "@chakra-ui/react";
 
 import { Health } from "./Health";
 import { HistoricalMetrics } from "./HistoricalMetrics";
+import { Stats } from "./Stats";
 
 export const Dashboard = () => (
   <Box>
@@ -27,6 +28,9 @@ export const Dashboard = () => (
     <Box>
       <Health />
     </Box>
+    <Box mt={5}>
+      <Stats />
+    </Box>
     <Box mt={5}>
       <HistoricalMetrics />
     </Box>
diff --git a/airflow/ui/src/pages/Dashboard/Health.tsx 
b/airflow/ui/src/pages/Dashboard/Health/Health.tsx
similarity index 94%
rename from airflow/ui/src/pages/Dashboard/Health.tsx
rename to airflow/ui/src/pages/Dashboard/Health/Health.tsx
index d105b7035a8..6697d6e7d0d 100644
--- a/airflow/ui/src/pages/Dashboard/Health.tsx
+++ b/airflow/ui/src/pages/Dashboard/Health/Health.tsx
@@ -17,7 +17,7 @@
  * under the License.
  */
 import { Box, Flex, Heading, HStack } from "@chakra-ui/react";
-import { FiClipboard } from "react-icons/fi";
+import { MdOutlineHealthAndSafety } from "react-icons/md";
 
 import { useMonitorServiceGetHealth } from "openapi/queries";
 import { ErrorAlert } from "src/components/ErrorAlert";
@@ -29,8 +29,8 @@ export const Health = () => {
 
   return (
     <Box>
-      <Flex color="gray.500" mb={2}>
-        <FiClipboard />
+      <Flex color="fg.muted" mb={2}>
+        <MdOutlineHealthAndSafety />
         <Heading ml={1} size="xs">
           Health
         </Heading>
diff --git a/airflow/ui/src/pages/Dashboard/HealthSection.tsx 
b/airflow/ui/src/pages/Dashboard/Health/HealthSection.tsx
similarity index 100%
rename from airflow/ui/src/pages/Dashboard/HealthSection.tsx
rename to airflow/ui/src/pages/Dashboard/Health/HealthSection.tsx
diff --git a/airflow/ui/src/pages/Dashboard/HealthTag.tsx 
b/airflow/ui/src/pages/Dashboard/Health/HealthTag.tsx
similarity index 100%
rename from airflow/ui/src/pages/Dashboard/HealthTag.tsx
rename to airflow/ui/src/pages/Dashboard/Health/HealthTag.tsx
diff --git a/airflow/ui/src/pages/Dashboard/Dashboard.tsx 
b/airflow/ui/src/pages/Dashboard/Health/index.ts
similarity index 71%
copy from airflow/ui/src/pages/Dashboard/Dashboard.tsx
copy to airflow/ui/src/pages/Dashboard/Health/index.ts
index 38727b0809e..e738024b047 100644
--- a/airflow/ui/src/pages/Dashboard/Dashboard.tsx
+++ b/airflow/ui/src/pages/Dashboard/Health/index.ts
@@ -16,19 +16,5 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { Box, Heading } from "@chakra-ui/react";
 
-import { Health } from "./Health";
-import { HistoricalMetrics } from "./HistoricalMetrics";
-
-export const Dashboard = () => (
-  <Box>
-    <Heading mb={4}>Welcome</Heading>
-    <Box>
-      <Health />
-    </Box>
-    <Box mt={5}>
-      <HistoricalMetrics />
-    </Box>
-  </Box>
-);
+export { Health } from "./Health";
diff --git a/airflow/ui/src/pages/Dashboard/Stats/DAGImportErrors.tsx 
b/airflow/ui/src/pages/Dashboard/Stats/DAGImportErrors.tsx
new file mode 100644
index 00000000000..3d62b08d3c6
--- /dev/null
+++ b/airflow/ui/src/pages/Dashboard/Stats/DAGImportErrors.tsx
@@ -0,0 +1,78 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {
+  Box,
+  Badge,
+  Text,
+  Button,
+  useDisclosure,
+  Skeleton,
+} from "@chakra-ui/react";
+import { FiChevronRight } from "react-icons/fi";
+
+import { useImportErrorServiceGetImportErrors } from "openapi/queries";
+import { ErrorAlert } from "src/components/ErrorAlert";
+
+import { DAGImportErrorsModal } from "./DAGImportErrorsModal";
+
+export const DAGImportErrors = () => {
+  const { onClose, onOpen, open } = useDisclosure();
+
+  const { data, error, isLoading } = useImportErrorServiceGetImportErrors();
+  const importErrorsCount = data?.total_entries ?? 0;
+  const importErrors = data?.import_errors ?? [];
+
+  if (isLoading) {
+    return <Skeleton height="9" width="225px" />;
+  }
+
+  return (
+    <Box alignItems="center" display="flex" gap={2}>
+      <ErrorAlert error={error} />
+      {importErrorsCount > 0 && (
+        <Button
+          alignItems="center"
+          borderRadius="md"
+          display="flex"
+          gap={2}
+          onClick={onOpen}
+          variant="outline"
+        >
+          <Badge
+            background="red.solid"
+            borderRadius="full"
+            color="red.contrast"
+            px={2}
+          >
+            {importErrorsCount}
+          </Badge>
+          <Box alignItems="center" display="flex" gap={1}>
+            <Text fontWeight="bold">Dag Import Errors</Text>
+            <FiChevronRight />
+          </Box>
+        </Button>
+      )}
+      <DAGImportErrorsModal
+        importErrors={importErrors}
+        onClose={onClose}
+        open={open}
+      />
+    </Box>
+  );
+};
diff --git a/airflow/ui/src/pages/Dashboard/Stats/DAGImportErrorsModal.tsx 
b/airflow/ui/src/pages/Dashboard/Stats/DAGImportErrorsModal.tsx
new file mode 100644
index 00000000000..2735864112e
--- /dev/null
+++ b/airflow/ui/src/pages/Dashboard/Stats/DAGImportErrorsModal.tsx
@@ -0,0 +1,125 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { Heading, Text, HStack, Input } from "@chakra-ui/react";
+import { useEffect, useState } from "react";
+import { PiFilePy } from "react-icons/pi";
+
+import type { ImportErrorResponse } from "openapi/requests/types.gen";
+import Time from "src/components/Time";
+import { Accordion, Dialog } from "src/components/ui";
+import { Pagination } from "src/components/ui/Pagination";
+
+type ImportDAGErrorModalProps = {
+  importErrors: Array<ImportErrorResponse>;
+  onClose: () => void;
+  open: boolean;
+};
+
+const PAGE_LIMIT = 15;
+
+export const DAGImportErrorsModal: React.FC<ImportDAGErrorModalProps> = ({
+  importErrors,
+  onClose,
+  open,
+}) => {
+  const [page, setPage] = useState(1);
+  const [searchQuery, setSearchQuery] = useState("");
+  const [filteredErrors, setFilteredErrors] = useState(importErrors);
+
+  const startRange = (page - 1) * PAGE_LIMIT;
+  const endRange = startRange + PAGE_LIMIT;
+  const visibleItems = filteredErrors.slice(startRange, endRange);
+
+  const onOpenChange = () => {
+    setSearchQuery("");
+    setPage(1);
+    onClose();
+  };
+
+  useEffect(() => {
+    const query = searchQuery.toLowerCase();
+    const filtered = importErrors.filter((error) =>
+      error.filename.toLowerCase().includes(query),
+    );
+
+    setFilteredErrors(filtered);
+    setPage(1);
+  }, [searchQuery, importErrors]);
+
+  return (
+    <Dialog.Root
+      onOpenChange={onOpenChange}
+      open={open}
+      scrollBehavior="inside"
+      size="xl"
+    >
+      <Dialog.Content backdrop>
+        <Dialog.Header>
+          <Heading size="xl">Dag Import Errors</Heading>
+          <Input
+            mt={4}
+            onChange={(event) => setSearchQuery(event.target.value)}
+            placeholder="Search by file"
+            value={searchQuery}
+          />
+        </Dialog.Header>
+
+        <Dialog.CloseTrigger />
+
+        <Dialog.Body>
+          <Accordion.Root collapsible multiple size="md" variant="enclosed">
+            {visibleItems.map((importError) => (
+              <Accordion.Item
+                key={importError.import_error_id}
+                value={importError.filename}
+              >
+                <Accordion.ItemTrigger cursor="pointer">
+                  <PiFilePy />
+                  {importError.filename}
+                </Accordion.ItemTrigger>
+                <Accordion.ItemContent>
+                  <Text color="fg.muted" fontSize="sm" mb={1}>
+                    Timestamp: <Time datetime={importError.timestamp} />
+                  </Text>
+                  <Text color="fg.error" fontSize="sm" whiteSpace="pre-wrap">
+                    <code>{importError.stack_trace}</code>
+                  </Text>
+                </Accordion.ItemContent>
+              </Accordion.Item>
+            ))}
+          </Accordion.Root>
+        </Dialog.Body>
+
+        <Pagination.Root
+          count={filteredErrors.length}
+          onPageChange={(event) => setPage(event.page)}
+          p={4}
+          page={page}
+          pageSize={PAGE_LIMIT}
+        >
+          <HStack>
+            <Pagination.PrevTrigger />
+            <Pagination.Items />
+            <Pagination.NextTrigger />
+          </HStack>
+        </Pagination.Root>
+      </Dialog.Content>
+    </Dialog.Root>
+  );
+};
diff --git a/airflow/ui/src/pages/Dashboard/Dashboard.tsx 
b/airflow/ui/src/pages/Dashboard/Stats/Stats.tsx
similarity index 70%
copy from airflow/ui/src/pages/Dashboard/Dashboard.tsx
copy to airflow/ui/src/pages/Dashboard/Stats/Stats.tsx
index 38727b0809e..69211367172 100644
--- a/airflow/ui/src/pages/Dashboard/Dashboard.tsx
+++ b/airflow/ui/src/pages/Dashboard/Stats/Stats.tsx
@@ -16,19 +16,19 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { Box, Heading } from "@chakra-ui/react";
+import { Box, Flex, Heading } from "@chakra-ui/react";
+import { FiClipboard } from "react-icons/fi";
 
-import { Health } from "./Health";
-import { HistoricalMetrics } from "./HistoricalMetrics";
+import { DAGImportErrors } from "./DAGImportErrors";
 
-export const Dashboard = () => (
+export const Stats = () => (
   <Box>
-    <Heading mb={4}>Welcome</Heading>
-    <Box>
-      <Health />
-    </Box>
-    <Box mt={5}>
-      <HistoricalMetrics />
-    </Box>
+    <Flex color="fg.muted" my={2}>
+      <FiClipboard />
+      <Heading ml={1} size="xs">
+        Stats
+      </Heading>
+    </Flex>
+    <DAGImportErrors />
   </Box>
 );
diff --git a/airflow/ui/src/pages/Dashboard/Dashboard.tsx 
b/airflow/ui/src/pages/Dashboard/Stats/index.ts
similarity index 71%
copy from airflow/ui/src/pages/Dashboard/Dashboard.tsx
copy to airflow/ui/src/pages/Dashboard/Stats/index.ts
index 38727b0809e..bda3388ef64 100644
--- a/airflow/ui/src/pages/Dashboard/Dashboard.tsx
+++ b/airflow/ui/src/pages/Dashboard/Stats/index.ts
@@ -16,19 +16,5 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { Box, Heading } from "@chakra-ui/react";
 
-import { Health } from "./Health";
-import { HistoricalMetrics } from "./HistoricalMetrics";
-
-export const Dashboard = () => (
-  <Box>
-    <Heading mb={4}>Welcome</Heading>
-    <Box>
-      <Health />
-    </Box>
-    <Box mt={5}>
-      <HistoricalMetrics />
-    </Box>
-  </Box>
-);
+export { Stats } from "./Stats";

Reply via email to