diff --git a/.github/workflows/build-app.yml b/.github/workflows/build-app.yml
deleted file mode 100644
index 8e9060a..0000000
--- a/.github/workflows/build-app.yml
+++ /dev/null
@@ -1,26 +0,0 @@
-name: Build Application
-
-on:
- - push
-
-jobs:
- build:
- runs-on: ubuntu-latest
-
- steps:
- - name: Checkout code
- uses: actions/checkout@v4
-
- - name: Setup Node.js
- uses: actions/setup-node@v4
- with:
- node-version: '20'
-
- - name: Install dependencies
- run: yarn install
-
- - name: Generate code
- run: make gen
-
- - name: Build application
- run: make build
diff --git a/makefile b/makefile
index fc6ecd7..d78700f 100644
--- a/makefile
+++ b/makefile
@@ -1,18 +1,17 @@
-GIT_VERSION = `git rev-parse --short HEAD`
+check:
+ @output=$$(yarn run --silent prettier --check src 2>&1) || { echo "$$output"; exit 1; }
+ @output=$$(yarn run --silent eslint src 2>&1) || { echo "$$output"; exit 1; }
+ @yarn build
-run:
- yarn dev
+fix:
+ @output=$$(yarn run --silent prettier --write src 2>&1) || { echo "$$output"; exit 1; }
+ @output=$$(yarn run --silent eslint --fix src 2>&1) || { echo "$$output"; exit 1; }
-build:
- yarn build
-check:
- yarn run prettier --check src
- yarn eslint src
+GIT_VERSION = `git rev-parse --short HEAD`
-fix:
- yarn run prettier --write src
- yarn eslint --fix src
+run:
+ yarn dev
gen:
yarn run openapi-ts -i http://leda.sao.ru/api/openapi.json -o ./src/clients/backend
diff --git a/src/App.tsx b/src/App.tsx
index af8d624..8d0bf2a 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -14,76 +14,48 @@ function App() {
return (
-
-
-
-
- }
- />
-
-
-
- }
- />
-
-
-
-
- }
- />
-
-
-
-
- }
- />
-
-
-
-
- }
- />
-
-
-
- }
- />
-
-
-
-
- }
- />
-
-
-
-
- }
- />
+ }>
+
+
+
+ >
+ }
+ />
+ } />
+
+
+
+ >
+ }
+ />
+ } />
+ } />
+ } />
+
+
+
+ >
+ }
+ />
+
+
+
+ >
+ }
+ />
+
);
diff --git a/src/components/ui/Accordion.tsx b/src/components/core/Accordion.tsx
similarity index 100%
rename from src/components/ui/Accordion.tsx
rename to src/components/core/Accordion.tsx
diff --git a/src/components/ui/Aladin.tsx b/src/components/core/Aladin.tsx
similarity index 100%
rename from src/components/ui/Aladin.tsx
rename to src/components/core/Aladin.tsx
diff --git a/src/components/ui/Astronomy.tsx b/src/components/core/Astronomy.tsx
similarity index 100%
rename from src/components/ui/Astronomy.tsx
rename to src/components/core/Astronomy.tsx
diff --git a/src/components/ui/Button.tsx b/src/components/core/Button.tsx
similarity index 100%
rename from src/components/ui/Button.tsx
rename to src/components/core/Button.tsx
diff --git a/src/components/ui/DropdownFilter.tsx b/src/components/core/DropdownFilter.tsx
similarity index 100%
rename from src/components/ui/DropdownFilter.tsx
rename to src/components/core/DropdownFilter.tsx
diff --git a/src/components/ui/Hint.tsx b/src/components/core/Hint.tsx
similarity index 100%
rename from src/components/ui/Hint.tsx
rename to src/components/core/Hint.tsx
diff --git a/src/components/ui/Link.tsx b/src/components/core/Link.tsx
similarity index 52%
rename from src/components/ui/Link.tsx
rename to src/components/core/Link.tsx
index a4b37c6..9d4b966 100644
--- a/src/components/ui/Link.tsx
+++ b/src/components/core/Link.tsx
@@ -1,4 +1,5 @@
import { ReactElement, ReactNode } from "react";
+import { Link as RouterLink } from "react-router-dom";
import { MdOpenInNew } from "react-icons/md";
interface LinkProps {
@@ -14,22 +15,25 @@ export function Link(props: LinkProps): ReactElement {
const className = props.className
? `${baseClass} ${props.className}`
: baseClass;
+ const combinedClassName = `${className} inline-flex items-center gap-1`;
- const linkProps = props.external
- ? {
- target: "_blank",
- rel: "noopener noreferrer",
- }
- : {};
+ if (props.external) {
+ return (
+
+ {content}
+
+
+ );
+ }
return (
-
+
{content}
- {props.external && }
-
+
);
}
diff --git a/src/components/ui/Loading.tsx b/src/components/core/Loading.tsx
similarity index 100%
rename from src/components/ui/Loading.tsx
rename to src/components/core/Loading.tsx
diff --git a/src/components/ui/TextFilter.tsx b/src/components/core/TextFilter.tsx
similarity index 100%
rename from src/components/ui/TextFilter.tsx
rename to src/components/core/TextFilter.tsx
diff --git a/src/components/ui/Badge.tsx b/src/components/ui/Badge.tsx
index 22d7343..7898ad9 100644
--- a/src/components/ui/Badge.tsx
+++ b/src/components/ui/Badge.tsx
@@ -1,5 +1,5 @@
import { ReactElement } from "react";
-import { Link } from "./Link";
+import { Link } from "../core/Link";
interface BadgeProps {
children: React.ReactNode;
diff --git a/src/components/ui/CatalogData.tsx b/src/components/ui/CatalogData.tsx
index 0772629..a3e0b34 100644
--- a/src/components/ui/CatalogData.tsx
+++ b/src/components/ui/CatalogData.tsx
@@ -6,7 +6,7 @@ import {
RightAscension,
Quantity,
QuantityWithError,
-} from "./Astronomy";
+} from "../core/Astronomy";
interface CatalogDataProps {
catalogs: Catalogs;
diff --git a/src/components/ui/CommonTable.tsx b/src/components/ui/CommonTable.tsx
index fe1ba3c..185194f 100644
--- a/src/components/ui/CommonTable.tsx
+++ b/src/components/ui/CommonTable.tsx
@@ -1,7 +1,7 @@
import React, { ReactElement, ReactNode } from "react";
import classNames from "classnames";
-import { Hint } from "./Hint";
-import { Loading } from "./Loading";
+import { Hint } from "../core/Hint";
+import { Loading } from "../core/Loading";
export type CellPrimitive = ReactElement | string | number;
diff --git a/src/components/ui/CopyButton.tsx b/src/components/ui/CopyButton.tsx
index 0f03110..69c9b8b 100644
--- a/src/components/ui/CopyButton.tsx
+++ b/src/components/ui/CopyButton.tsx
@@ -1,5 +1,5 @@
import { ReactElement, useState } from "react";
-import { Button } from "./Button";
+import { Button } from "../core/Button";
import { MdCheck, MdContentCopy } from "react-icons/md";
interface CopyButtonProps {
diff --git a/src/components/ui/ErrorPage.tsx b/src/components/ui/ErrorPage.tsx
index 05a091f..ec8b28e 100644
--- a/src/components/ui/ErrorPage.tsx
+++ b/src/components/ui/ErrorPage.tsx
@@ -1,5 +1,5 @@
import { ReactElement, ReactNode } from "react";
-import { Button } from "./Button";
+import { Button } from "../core/Button";
interface ErrorPageProps {
title?: string;
diff --git a/src/components/ui/Footer.tsx b/src/components/ui/Footer.tsx
deleted file mode 100644
index 7deeabe..0000000
--- a/src/components/ui/Footer.tsx
+++ /dev/null
@@ -1,68 +0,0 @@
-import { useState } from "react";
-import { Link as ReactDomLink } from "react-router-dom";
-import { Button } from "./Button";
-import { Link } from "./Link";
-import { MdKeyboardArrowDown, MdKeyboardArrowUp } from "react-icons/md";
-
-const footerContent = (
-
-
- Information:{" "}
-
- The next generation of the HyperLeda database
-
-
-
- Original version:{" "}
-
- OHP Mirror
-
-
-
-);
-
-export function Footer() {
- const [isCollapsed, setIsCollapsed] = useState(true);
-
- function toggleCollapse() {
- setIsCollapsed(!isCollapsed);
- }
-
- return (
- <>
-
-
-
- >
- );
-}
diff --git a/src/components/ui/Layout.tsx b/src/components/ui/Layout.tsx
index 68e6b40..d229496 100644
--- a/src/components/ui/Layout.tsx
+++ b/src/components/ui/Layout.tsx
@@ -1,10 +1,15 @@
-import { Footer } from "./Footer";
+import { Outlet } from "react-router-dom";
+import { Navbar } from "./Navbar";
-export function Layout({ children }: { children: React.ReactNode }) {
+export function Layout() {
return (
-
-
{children}
-
+
);
}
diff --git a/src/components/ui/Navbar.tsx b/src/components/ui/Navbar.tsx
new file mode 100644
index 0000000..1c451dd
--- /dev/null
+++ b/src/components/ui/Navbar.tsx
@@ -0,0 +1,100 @@
+import { useEffect, useRef, useState } from "react";
+import { NavLink } from "react-router-dom";
+import { Tooltip } from "flowbite-react";
+import { MdInfo, MdSearch, MdTableChart } from "react-icons/md";
+import { Link } from "../core/Link";
+
+const navItems = [
+ { to: "/", icon:
, label: "Object search" },
+ { to: "/tables", icon:
, label: "Tables" },
+];
+
+export function Navbar() {
+ const [footerOpen, setFooterOpen] = useState(false);
+ const infoPanelRef = useRef
(null);
+ const infoButtonRef = useRef(null);
+
+ useEffect(() => {
+ function handleClickOutside(e: MouseEvent) {
+ const clickedInside =
+ infoPanelRef.current?.contains(e.target as Node) ||
+ infoButtonRef.current?.contains(e.target as Node);
+ if (!clickedInside) {
+ setFooterOpen(false);
+ }
+ }
+
+ if (footerOpen) {
+ document.addEventListener("mousedown", handleClickOutside);
+ }
+ return () => document.removeEventListener("mousedown", handleClickOutside);
+ }, [footerOpen]);
+
+ return (
+ <>
+
+
+
+
+
+ Information:{" "}
+
+ The next generation of the HyperLeda database
+
+
+
+ Original version:{" "}
+
+ OHP Mirror
+
+
+
+
+ >
+ );
+}
diff --git a/src/components/ui/Pagination.tsx b/src/components/ui/Pagination.tsx
index 1d6d886..32b62fb 100644
--- a/src/components/ui/Pagination.tsx
+++ b/src/components/ui/Pagination.tsx
@@ -1,5 +1,5 @@
import { ReactElement } from "react";
-import { Button } from "./Button";
+import { Button } from "../core/Button";
type PaginationProps = {
page: number;
diff --git a/src/components/ui/Searchbar.tsx b/src/components/ui/Searchbar.tsx
index d7b494f..eaf3537 100644
--- a/src/components/ui/Searchbar.tsx
+++ b/src/components/ui/Searchbar.tsx
@@ -1,7 +1,7 @@
import { ReactElement, useState } from "react";
import { Link, NavigateFunction, useNavigate } from "react-router-dom";
import classNames from "classnames";
-import { Button } from "./Button";
+import { Button } from "../core/Button";
interface SearchBarProps {
initialValue?: string;
diff --git a/src/pages/CrossmatchResults.tsx b/src/pages/CrossmatchResults.tsx
index 0faea51..de1891f 100644
--- a/src/pages/CrossmatchResults.tsx
+++ b/src/pages/CrossmatchResults.tsx
@@ -6,8 +6,8 @@ import {
CellPrimitive,
} from "../components/ui/CommonTable";
import { Badge } from "../components/ui/Badge";
-import { DropdownFilter } from "../components/ui/DropdownFilter";
-import { TextFilter } from "../components/ui/TextFilter";
+import { DropdownFilter } from "../components/core/DropdownFilter";
+import { TextFilter } from "../components/core/TextFilter";
import { getCrossmatchRecords } from "../clients/admin/sdk.gen";
import type {
GetRecordsCrossmatchResponse,
@@ -16,10 +16,10 @@ import type {
ValidationError,
} from "../clients/admin/types.gen";
import { getResource } from "../resources/resources";
-import { Button } from "../components/ui/Button";
-import { Loading } from "../components/ui/Loading";
+import { Button } from "../components/core/Button";
+import { Loading } from "../components/core/Loading";
import { ErrorPage } from "../components/ui/ErrorPage";
-import { Link } from "../components/ui/Link";
+import { Link } from "../components/core/Link";
import { useDataFetching } from "../hooks/useDataFetching";
import { Pagination } from "../components/ui/Pagination";
import { adminClient } from "../clients/config";
diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx
index e7a2224..0214c4f 100644
--- a/src/pages/Home.tsx
+++ b/src/pages/Home.tsx
@@ -1,5 +1,5 @@
import { ReactElement } from "react";
-import { Link } from "../components/ui/Link";
+import { Link } from "../components/core/Link";
const homePageHint: ReactElement = (
diff --git a/src/pages/NotFound.tsx b/src/pages/NotFound.tsx
index d4ac5a9..ad045c3 100644
--- a/src/pages/NotFound.tsx
+++ b/src/pages/NotFound.tsx
@@ -1,6 +1,6 @@
import { useNavigate } from "react-router-dom";
import { ErrorPage } from "../components/ui/ErrorPage";
-import { Button } from "../components/ui/Button";
+import { Button } from "../components/core/Button";
import { useEffect } from "react";
export function NotFoundPage() {
diff --git a/src/pages/ObjectDetails.tsx b/src/pages/ObjectDetails.tsx
index b6e2c1e..1158b25 100644
--- a/src/pages/ObjectDetails.tsx
+++ b/src/pages/ObjectDetails.tsx
@@ -1,10 +1,10 @@
import { ReactElement, useEffect } from "react";
import { useParams } from "react-router-dom";
-import { AladinViewer } from "../components/ui/Aladin";
-import { Loading } from "../components/ui/Loading";
+import { AladinViewer } from "../components/core/Aladin";
+import { Loading } from "../components/core/Loading";
import { ErrorPage } from "../components/ui/ErrorPage";
import { CatalogData } from "../components/ui/CatalogData";
-import { Link } from "../components/ui/Link";
+import { Link } from "../components/core/Link";
import { querySimple } from "../clients/backend/sdk.gen";
import { PgcObject, Schema } from "../clients/backend/types.gen";
import { useDataFetching } from "../hooks/useDataFetching";
@@ -63,7 +63,10 @@ async function fetcher(
});
if (response.error || !response.data) {
- throw new Error(`Error during query: ${response.error}`);
+ const err = response.error;
+ throw new Error(
+ `Error during query: ${typeof err === "object" ? JSON.stringify(err) : err}`,
+ );
}
const objects = response.data.data.objects;
diff --git a/src/pages/RecordCrossmatchDetails.tsx b/src/pages/RecordCrossmatchDetails.tsx
index c59c5cc..75a6f65 100644
--- a/src/pages/RecordCrossmatchDetails.tsx
+++ b/src/pages/RecordCrossmatchDetails.tsx
@@ -1,7 +1,7 @@
import { ReactElement, useEffect } from "react";
import { useParams } from "react-router-dom";
-import { AladinViewer } from "../components/ui/Aladin";
-import { Loading } from "../components/ui/Loading";
+import { AladinViewer } from "../components/core/Aladin";
+import { Loading } from "../components/core/Loading";
import { ErrorPage } from "../components/ui/ErrorPage";
import { CatalogData } from "../components/ui/CatalogData";
import {
@@ -18,9 +18,9 @@ import {
} from "../clients/admin/types.gen";
import { Schema as BackendSchema } from "../clients/backend/types.gen";
import { getResource } from "../resources/resources";
-import { Link } from "../components/ui/Link";
+import { Link } from "../components/core/Link";
import { CopyButton } from "../components/ui/CopyButton";
-import { Accordion } from "../components/ui/Accordion";
+import { Accordion } from "../components/core/Accordion";
import { useDataFetching } from "../hooks/useDataFetching";
import { adminClient } from "../clients/config";
@@ -242,7 +242,7 @@ async function fetcher(
if (response.error || !response.data?.data) {
throw new Error(
- `Error fetching crossmatch details: ${response.error || "Unknown error"}`,
+ `Error fetching crossmatch details: ${typeof response.error === "object" ? JSON.stringify(response.error) : response.error || "Unknown error"}`,
);
}
diff --git a/src/pages/SearchResults.tsx b/src/pages/SearchResults.tsx
index b5bc6c7..f234495 100644
--- a/src/pages/SearchResults.tsx
+++ b/src/pages/SearchResults.tsx
@@ -6,13 +6,13 @@ import {
} from "react-router-dom";
import { SearchBar } from "../components/ui/Searchbar";
import { CommonTable, Column } from "../components/ui/CommonTable";
-import { Loading } from "../components/ui/Loading";
+import { Loading } from "../components/core/Loading";
import { ErrorPage, ErrorPageHomeButton } from "../components/ui/ErrorPage";
import { useDataFetching } from "../hooks/useDataFetching";
import { querySimple } from "../clients/backend/sdk.gen";
import { QuerySimpleResponse } from "../clients/backend/types.gen";
-import { Link } from "../components/ui/Link";
-import { Declination, RightAscension } from "../components/ui/Astronomy";
+import { Link } from "../components/core/Link";
+import { Declination, RightAscension } from "../components/core/Astronomy";
import { Pagination } from "../components/ui/Pagination";
import { backendClient } from "../clients/config";
@@ -139,7 +139,10 @@ async function fetcher(
}
if (response.error || !response.data) {
- throw new Error(`Error during query: ${response.error}`);
+ const err = response.error;
+ throw new Error(
+ `Error during query: ${typeof err === "object" ? JSON.stringify(err) : err}`,
+ );
}
return response.data.data;
diff --git a/src/pages/TableDetails.tsx b/src/pages/TableDetails.tsx
index ffffe92..5eb2c47 100644
--- a/src/pages/TableDetails.tsx
+++ b/src/pages/TableDetails.tsx
@@ -11,11 +11,11 @@ import {
Column,
CommonTable,
} from "../components/ui/CommonTable";
-import { Button } from "../components/ui/Button";
+import { Button } from "../components/core/Button";
import { CopyButton } from "../components/ui/CopyButton";
import { Badge } from "../components/ui/Badge";
-import { Link } from "../components/ui/Link";
-import { Loading } from "../components/ui/Loading";
+import { Link } from "../components/core/Link";
+import { Loading } from "../components/core/Loading";
import { ErrorPage } from "../components/ui/ErrorPage";
import { getResource } from "../resources/resources";
import { useDataFetching } from "../hooks/useDataFetching";
diff --git a/src/pages/Tables.tsx b/src/pages/Tables.tsx
index 58187f9..cc8048f 100644
--- a/src/pages/Tables.tsx
+++ b/src/pages/Tables.tsx
@@ -5,17 +5,17 @@ import {
Column,
CellPrimitive,
} from "../components/ui/CommonTable";
-import { DropdownFilter } from "../components/ui/DropdownFilter";
-import { TextFilter } from "../components/ui/TextFilter";
+import { DropdownFilter } from "../components/core/DropdownFilter";
+import { TextFilter } from "../components/core/TextFilter";
import { getTableList } from "../clients/admin/sdk.gen";
import type {
GetTableListResponse,
TableListItem,
ValidationError,
} from "../clients/admin/types.gen";
-import { Loading } from "../components/ui/Loading";
+import { Loading } from "../components/core/Loading";
import { ErrorPage } from "../components/ui/ErrorPage";
-import { Link } from "../components/ui/Link";
+import { Link } from "../components/core/Link";
import { useDataFetching } from "../hooks/useDataFetching";
import { Pagination } from "../components/ui/Pagination";
import { adminClient } from "../clients/config";