diff --git a/.eslintrc.json b/.eslintrc.json index b5e13c1..02a9f43 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -16,6 +16,8 @@ "unicorn/prefer-spread": 0, "unicorn/no-array-reduce": 0, "unicorn/prevent-abbreviations": 0, + "unicorn/prefer-global-this": 0, + "unicorn/prefer-logical-operator-over-ternary": 0, // TODO: Check these rules later "@typescript-eslint/no-explicit-any": 0, "@typescript-eslint/no-unused-vars": [ diff --git a/.github/assets/home.gif b/.github/assets/home.gif new file mode 100644 index 0000000..73f6a66 Binary files /dev/null and b/.github/assets/home.gif differ diff --git a/.github/assets/keyspace-details.gif b/.github/assets/keyspace-details.gif new file mode 100644 index 0000000..0802a0c Binary files /dev/null and b/.github/assets/keyspace-details.gif differ diff --git a/.github/assets/query-runner-details.gif b/.github/assets/query-runner-details.gif new file mode 100644 index 0000000..a4f7ca5 Binary files /dev/null and b/.github/assets/query-runner-details.gif differ diff --git a/.github/assets/query-ruuner-dashboard.gif b/.github/assets/query-ruuner-dashboard.gif new file mode 100644 index 0000000..3533714 Binary files /dev/null and b/.github/assets/query-ruuner-dashboard.gif differ diff --git a/.github/assets/table-details.gif b/.github/assets/table-details.gif new file mode 100644 index 0000000..4ee736b Binary files /dev/null and b/.github/assets/table-details.gif differ diff --git a/README.md b/README.md index e36f297..9ca5c4d 100644 --- a/README.md +++ b/README.md @@ -16,8 +16,13 @@ - **Visual Management of Keyspaces and Tables:** Create, edit, and visualize keyspaces and tables directly from the interface. + keyspace showcase + keyspace tableshowcase +

- **Integrated Metrics Monitoring:** Leverages ScyllaDB's Prometheus and Grafana integrations to display important metrics within the app. + + query runner dashboard - **Cluster Connectivity:** Easily connect to your local cluster using `https://local.scylladb.studio` or manage cloud-based clusters. diff --git a/src/app/(main)/keyspace/[keyspace]/_components/keyspace-info.tsx b/src/app/(main)/keyspace/[keyspace]/_components/keyspace-info.tsx index 294de03..0292dd8 100644 --- a/src/app/(main)/keyspace/[keyspace]/_components/keyspace-info.tsx +++ b/src/app/(main)/keyspace/[keyspace]/_components/keyspace-info.tsx @@ -58,29 +58,25 @@ export default function KeyspaceInfo({ keyspace }: KeyspaceInfoProperties) { "", )} /> -

-
- -
-
-

- Replication Sizes -

-

- {replicationSizes.map(([key, value]) => ( - - - {key}: {value} - - - ))} -

-
-
+ } + label="Replication Sizes" + > +

+ {replicationSizes.map(([key, value]) => ( + + + {key}: {value} + + + ))} +

+
+ } label="Durable Writes" @@ -99,9 +95,9 @@ export default function KeyspaceInfo({ keyspace }: KeyspaceInfoProperties) { ); } -function KeyspaceInfoItem({ icon, label, value }: any) { +function KeyspaceInfoItem({ icon, label, value, children }: any) { return ( -
+
{icon}
@@ -109,9 +105,14 @@ function KeyspaceInfoItem({ icon, label, value }: any) {

{label}

-

- {value} -

+ + {children ? ( + children + ) : ( +

+ {value} +

+ )}
); @@ -119,7 +120,9 @@ function KeyspaceInfoItem({ icon, label, value }: any) { function KeyspaceCQLTooltip({ keyspaceInfo, -}: { keyspaceInfo: KeyspaceDefinition }) { +}: { + keyspaceInfo: KeyspaceDefinition; +}) { const cql = `CREATE KEYSPACE ${keyspaceInfo.name} WITH replication = { 'class': '${keyspaceInfo?.replication?.class}', @@ -154,12 +157,17 @@ function DeleteKeyspaceButton() { - - + + Work In Progress + ); diff --git a/src/app/(main)/keyspace/[keyspace]/_components/keyspace-tables.tsx b/src/app/(main)/keyspace/[keyspace]/_components/keyspace-tables.tsx index 4cab4b8..22c41d8 100644 --- a/src/app/(main)/keyspace/[keyspace]/_components/keyspace-tables.tsx +++ b/src/app/(main)/keyspace/[keyspace]/_components/keyspace-tables.tsx @@ -1,3 +1,4 @@ +import { CustomTooltip } from "@scylla-studio/components/composed/custom-tooltip"; import { Badge } from "@scylla-studio/components/ui/badge"; import { Button } from "@scylla-studio/components/ui/button"; import { @@ -14,12 +15,7 @@ import { TableHeader, TableRow, } from "@scylla-studio/components/ui/table"; -import { - Tooltip, - TooltipContent, - TooltipProvider, - TooltipTrigger, -} from "@scylla-studio/components/ui/tooltip"; + import { KeyspaceDefinition } from "@scylla-studio/lib/cql-parser/keyspace-parser"; import { ArrowDownIcon, @@ -54,42 +50,41 @@ export default function KeyspaceDefinitions({ -
+
- + Table Name - - Keys - - - Actions - + Keys + Actions {tables.map(({ name, table }) => ( - - - + + + + + {table.tableName} + + + } > - - - {table.tableName} - - + View details + - +
- +
- - - - - - -

View Data

-
-
-
- - - - - - -

Generate MV

-
-
-
- - - - - - -

Drop Table

-
-
-
+ + + + } + > +

View Data

+
+ + + + + } + > +

Generate MV

+ (Work In Progress) +
+ + + + + } + > +

Drop Table

+ (Work In Progress) +
diff --git a/src/app/(main)/keyspace/[keyspace]/table/[table]/_components/table-structure.tsx b/src/app/(main)/keyspace/[keyspace]/table/[table]/_components/table-structure.tsx index d3d8ae7..fdbe76e 100644 --- a/src/app/(main)/keyspace/[keyspace]/table/[table]/_components/table-structure.tsx +++ b/src/app/(main)/keyspace/[keyspace]/table/[table]/_components/table-structure.tsx @@ -94,7 +94,7 @@ export default function TableStructure({ table }: TableStructureProperties) {
- + @@ -118,7 +118,9 @@ export default function TableStructure({ table }: TableStructureProperties) { } label="Default TTL" - value={`${table.options.defaultTimeToLive} seconds ${table.options.defaultTimeToLive === 0 ? "(disabled)" : ""}`} + value={`${table.options.defaultTimeToLive} seconds ${ + table.options.defaultTimeToLive === 0 ? "(disabled)" : "" + }`} /> } @@ -148,7 +150,7 @@ function SettingItem({ icon, label, value }: any) { -
+
{icon}
diff --git a/src/app/(main)/page.tsx b/src/app/(main)/page.tsx index 4427430..bad373d 100644 --- a/src/app/(main)/page.tsx +++ b/src/app/(main)/page.tsx @@ -1,11 +1,13 @@ "use client"; +import { CustomTooltip } from "@scylla-studio/components/composed/custom-tooltip"; import { ContentLayout } from "@scylla-studio/components/composed/sidebar/content-layout"; import { Badge } from "@scylla-studio/components/ui/badge"; import { Card, CardContent, CardHeader, + CardTitle, } from "@scylla-studio/components/ui/card"; import { Tooltip, @@ -48,9 +50,9 @@ type MergedContributor = PackageContributor & GithubContributor; export default function DashboardPage() { return ( - + + - @@ -61,26 +63,25 @@ export default function DashboardPage() { function DashboardHeader() { return ( -
+
+ +

+ ScyllaDB Studio +

+

+ A front-end application designed for the ScyllaDB ecosystem, inspired + by tools like Drizzle and Prisma Studio. It provides an intuitive + interface for managing your ScyllaDB keyspaces and tables, integrating + essential performance metrics, and offering a unified solution to + interact with both local and cloud-based ScyllaDB clusters. +

+
ScyllaDB Studio Logo -
- -

ScyllaDB Studio

-

- a front-end application designed for the ScyllaDB ecosystem, - inspired by tools like Drizzle and Prisma Studio. It provides an - intuitive interface for managing your ScyllaDB keyspaces and tables, - integrating essential performance metrics, and offering a unified - solution to interact with both local and cloud-based ScyllaDB - clusters. -

-
-
); } @@ -91,23 +92,39 @@ function DashboardKeyFeatures() { { name: "Explore your data with a powerful query editor", status: "wip" }, { name: "Visualize your data model", status: "done" }, { name: "Support for both local and cloud-based clusters", status: "done" }, - { name: "Keyspace Autocomplete based on Active Connection", status: "wip" }, + { + name: "Keyspace Autocomplete based on Active Connection", + status: "done", + }, { name: "Query History", status: "wip" }, ]; return ( -
+

Key Features:

-
    +
      {features.map((feature, index) => ( -
    • - {feature.status === "done" ? ( - - ) : ( - - )} - {feature.name} -
    • + + {feature.name} + + + {feature.status === "done" ? ( + + ) : ( + + )} + + } + > + {feature.status} + + + ))}
@@ -195,33 +212,34 @@ const DashboardContributors = () => { }, []); return ( -
+

Contributors:

-
+
{contributors.map((contributor, index) => ( - -
- {`${contributor.name}'s - {contributor.openToWork && ( -
- Open to Work -
- )} -
- -
-

{contributor.name}

+ + {`${contributor.name}'s + + +
+ {contributor.openToWork && ( + + Open to Work + + )} {contributor.core && ( Core Member )}
+
+

{contributor.name}

+
@@ -272,14 +290,14 @@ const DashboardContributors = () => {
-
+
- + {contributor.linkedin && ( Run current query - Ctrl + Enter + + {isMacEnviroment ? "⌘" : "Ctrl"} + Enter + - + + + } > - - + Results + - + + + } > - - + Tracing + - + + + } > - - + Dashboard + )} diff --git a/src/app/(main)/query-runner/_components/results-render.tsx b/src/app/(main)/query-runner/_components/results-render.tsx index 472e4e0..0f4e1eb 100644 --- a/src/app/(main)/query-runner/_components/results-render.tsx +++ b/src/app/(main)/query-runner/_components/results-render.tsx @@ -96,10 +96,12 @@ const ResultsRenderComponent = ({ - + {headers.map((header) => ( - {header} + + {header} + ))} @@ -108,7 +110,7 @@ const ResultsRenderComponent = ({ // biome-ignore lint/suspicious/noArrayIndexKey: it needs to be {headers.map((header) => ( - + {row[header] === undefined ? "N/A" : String(row[header])} ))} diff --git a/src/app/(main)/query-runner/_components/tracing-dashboard-render.tsx b/src/app/(main)/query-runner/_components/tracing-dashboard-render.tsx index 3f85925..5232bfe 100644 --- a/src/app/(main)/query-runner/_components/tracing-dashboard-render.tsx +++ b/src/app/(main)/query-runner/_components/tracing-dashboard-render.tsx @@ -23,6 +23,18 @@ import { YAxis, } from "recharts"; +// Define colors for charts +const colors = [ + "#8884d8", + "#82ca9d", + "#ffc658", + "#d0ed57", + "#a4de6c", + "#8dd1e1", + "#83a6ed", + "#8e4585", +]; + export default function QueryDashboard({ tracingInfo, }: { @@ -183,18 +195,6 @@ export default function QueryDashboard({ setTotalSourceElapsed(maxSourceElapsed); }, [data]); - // Define colors for charts - const colors = [ - "#8884d8", - "#82ca9d", - "#ffc658", - "#d0ed57", - "#a4de6c", - "#8dd1e1", - "#83a6ed", - "#8e4585", - ]; - return (

Query Dashboard

diff --git a/src/app/(main)/query-runner/_components/tracing-render.tsx b/src/app/(main)/query-runner/_components/tracing-render.tsx index 073101d..5ada383 100644 --- a/src/app/(main)/query-runner/_components/tracing-render.tsx +++ b/src/app/(main)/query-runner/_components/tracing-render.tsx @@ -23,7 +23,9 @@ export const TracingRender = ({ data }: { data: TracingResult }) => { {headers.map((header) => ( - {header} + + {header} + ))} @@ -33,7 +35,7 @@ export const TracingRender = ({ data }: { data: TracingResult }) => { {headers.map((header) => ( - + {row[header as keyof typeof row] === undefined ? "N/A" : String(row[header as keyof typeof row])} diff --git a/src/components/composed/custom-tooltip.tsx b/src/components/composed/custom-tooltip.tsx new file mode 100644 index 0000000..cec0873 --- /dev/null +++ b/src/components/composed/custom-tooltip.tsx @@ -0,0 +1,29 @@ +import type { PropsWithChildren, ReactNode } from "react"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "../ui/tooltip"; + +interface CustomTooltipProps { + Trigger: ReactNode; + side?: "right" | "top" | "bottom" | "left"; + triggerAsChild?: boolean; +} + +export function CustomTooltip({ + Trigger, + children, + side, + triggerAsChild, +}: PropsWithChildren) { + return ( + + + {Trigger} + {children} + + + ); +} diff --git a/src/components/composed/sidebar/menu.tsx b/src/components/composed/sidebar/menu.tsx index 8289686..06fce5f 100644 --- a/src/components/composed/sidebar/menu.tsx +++ b/src/components/composed/sidebar/menu.tsx @@ -15,6 +15,7 @@ import { } from "@scylla-studio/components/ui/tooltip"; import { useGetMenuList } from "@scylla-studio/lib/menu-list"; import { cn } from "@scylla-studio/lib/utils"; +import { getIsMacEnviroment } from "@scylla-studio/utils"; interface MenuProperties { isOpen: boolean | undefined; @@ -34,6 +35,8 @@ const triggerCtrlK = () => { document.dispatchEvent(keyEvent); }; +const isMacEnvironment = getIsMacEnviroment(navigator.platform.toUpperCase()); + export function Menu({ isOpen }: MenuProperties) { const pathname = usePathname(); const menuList = useGetMenuList(pathname); @@ -138,7 +141,9 @@ export function Menu({ isOpen }: MenuProperties) {

Press{" "} - ⌘ K + + {isMacEnvironment ? "⌘" : "Ctrl"} + K +
to open the Command Pallete diff --git a/src/utils/index.tsx b/src/utils/index.tsx new file mode 100644 index 0000000..2a92035 --- /dev/null +++ b/src/utils/index.tsx @@ -0,0 +1,3 @@ +export const getIsMacEnviroment = (OS: string) => { + return OS.includes("MAC") ? true : false; +};