Skip to content

Commit

Permalink
[POR-2209] add filters to db dashboard (#4175)
Browse files Browse the repository at this point in the history
  • Loading branch information
Feroze Mohideen authored Jan 22, 2024
1 parent cceb3fb commit 51a39d8
Show file tree
Hide file tree
Showing 5 changed files with 185 additions and 64 deletions.
8 changes: 4 additions & 4 deletions dashboard/src/components/porter/Image.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const StyledIcon = styled.img<{
opacity?: number;
additionalStyles?: string;
}>`
height: ${props => props.size || 20}px;
opacity: ${props => props.opacity || 1};
${props => props.additionalStyles ? props.additionalStyles : ""}
`;
height: ${(props) => props.size || 20}px;
opacity: ${(props) => props.opacity || 1};
${(props) => (props.additionalStyles ? props.additionalStyles : "")}
`;
60 changes: 29 additions & 31 deletions dashboard/src/components/porter/Select.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import React, { useEffect, useRef, useState } from "react";
import React from "react";
import styled from "styled-components";

import arrow from "assets/arrow-down.svg";

import Container from "./Container";

type Props = {
width?: string;
options: Array<{
options: Array<{
label: string;
value: string;
icon?: HTMLImageElement | string;
icon?: string;
disabled?: boolean;
}>;
label?: string | React.ReactNode;
Expand All @@ -23,48 +25,46 @@ type Props = {
};

const Select: React.FC<Props> = ({
width,
options,
label,
labelColor,
height,
error,
children,
disabled,
value,
setValue,
prefix,
width = "200px",
height = "35px",
}) => {
const prefixRef = useRef<HTMLDivElement>(null);

return (
<Block width={width}>
{label && <Label color={labelColor}>{label}</Label>}
<SelectWrapper>
<AbsoluteWrapper>
<Prefix>{prefix}</Prefix>
<Bar />
{options.map((option) => {
if (option.value === value) {
return (
<Container key={1} row>
{option.icon && <Img src={option?.icon} />}
{option.label}
</Container>
)
}
return null;
})}
<img src={arrow} />
<Prefix>{prefix}</Prefix>
<Bar />
{options.map((option) => {
if (option.value === value) {
return (
<Container key={1} row>
{option.icon && <Img src={option?.icon} />}
{option.label}
</Container>
);
}
return null;
})}
<img src={arrow} />
</AbsoluteWrapper>
<StyledSelect
onChange={(e) => {
setValue(e.target.value);
setValue?.(e.target.value);
}}
width={width}
height={height}
hasError={(error && true) || error === ""}
disabled={disabled ? disabled : false}
disabled={disabled || false}
value={value}
>
{options.map((option, i) => {
Expand Down Expand Up @@ -134,9 +134,9 @@ const Block = styled.div<{
width: ${(props) => props.width || "200px"};
`;

const Label = styled.div<{color?: string}>`
const Label = styled.div<{ color?: string }>`
font-size: 13px;
color: ${({color = "#aaaabb"}) => color};
color: ${({ color = "#aaaabb" }) => color};
margin-bottom: 10px;
`;

Expand All @@ -156,9 +156,9 @@ const Error = styled.div`
const SelectWrapper = styled.div`
position: relative;
background: ${(props) => props.theme.fg};
border: 1px solid ${(props) => (props.hasError ? "#ff3b62" : "#494b4f")};
border: 1px solid #494b4f;
:hover {
border: 1px solid ${(props) => (props.hasError ? "#ff3b62" : "#7a7b80")};
border: 1px solid #7a7b80;
}
z-index: 0;
display: flex;
Expand All @@ -172,12 +172,10 @@ const StyledSelect = styled.select<{
width: string;
height: string;
hasError: boolean;
paddingLeft: number;
}>`
height: ${(props) => props.height || "35px"};
height: ${(props) => props.height};
padding: 5px 10px;
padding-left: ${(props) => props.paddingLeft}px;
width: ${(props) => props.width || "200px"};
width: ${(props) => props.width};
color: #ffffff;
font-size: 13px;
outline: none;
Expand Down
41 changes: 19 additions & 22 deletions dashboard/src/lib/databases/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ export type DatastoreMetadataWithSource = z.infer<

export const datastoreValidator = z.object({
name: z.string(),
type: z.string(),
engine: z.string(),
type: z.enum(["RDS", "ELASTICACHE"]),
engine: z.enum(["POSTGRES", "AURORA-POSTGRES", "REDIS", "MEMCACHED"]),
created_at: z.string().default(""),
metadata: datastoreMetadataValidator.array().default([]),
env: datastoreEnvValidator.optional(),
Expand Down Expand Up @@ -70,11 +70,10 @@ export type CloudProviderDatastore = z.infer<
typeof cloudProviderDatastoreSchema
>;

export type DatabaseEngine =
| typeof DATABASE_ENGINE_POSTGRES
| typeof DATABASE_ENGINE_AURORA_POSTGRES
| typeof DATABASE_ENGINE_REDIS
| typeof DATABASE_ENGINE_MEMCACHED;
export type DatabaseEngine = {
name: z.infer<typeof datastoreValidator>["engine"];
displayName: string;
};
export const DATABASE_ENGINE_POSTGRES = {
name: "POSTGRES" as const,
displayName: "PostgreSQL",
Expand All @@ -92,38 +91,36 @@ export const DATABASE_ENGINE_MEMCACHED = {
displayName: "Memcached",
};

export type DatabaseType =
| typeof DATABASE_TYPE_RDS
| typeof DATABASE_TYPE_ELASTICACHE;
export type DatabaseType = z.infer<typeof datastoreValidator>["type"];
export const DATABASE_TYPE_RDS = "RDS" as const;
export const DATABASE_TYPE_ELASTICACHE = "ELASTICACHE" as const;

export type DatabaseState = {
state: z.infer<typeof datastoreValidator>["status"];
displayName: string;
};
export const DATABASE_STATE_CREATING = {
state: "CREATING" as const,
export const DATABASE_STATE_CREATING: DatabaseState = {
state: "CREATING",
displayName: "Creating",
};
export const DATABASE_STATE_CONFIGURING_LOG_EXPORTS = {
state: "CONFIGURING_LOG_EXPORTS" as const,
export const DATABASE_STATE_CONFIGURING_LOG_EXPORTS: DatabaseState = {
state: "CONFIGURING_LOG_EXPORTS",
displayName: "Configuring log exports",
};
export const DATABASE_STATE_MODIFYING = {
state: "MODIFYING" as const,
export const DATABASE_STATE_MODIFYING: DatabaseState = {
state: "MODIFYING",
displayName: "Modifying",
};
export const DATABASE_STATE_CONFIGURING_ENHANCED_MONITORING = {
state: "CONFIGURING_ENHANCED_MONITORING" as const,
export const DATABASE_STATE_CONFIGURING_ENHANCED_MONITORING: DatabaseState = {
state: "CONFIGURING_ENHANCED_MONITORING",
displayName: "Configuring enhanced monitoring",
};
export const DATABASE_STATE_BACKING_UP = {
state: "BACKING_UP" as const,
export const DATABASE_STATE_BACKING_UP: DatabaseState = {
state: "BACKING_UP",
displayName: "Backing up",
};
export const DATABASE_STATE_AVAILABLE = {
state: "AVAILABLE" as const,
export const DATABASE_STATE_AVAILABLE: DatabaseState = {
state: "AVAILABLE",
displayName: "Finishing provision",
};

Expand Down
119 changes: 114 additions & 5 deletions dashboard/src/main/home/database-dashboard/DatabaseDashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ import Button from "components/porter/Button";
import Container from "components/porter/Container";
import DashboardPlaceholder from "components/porter/DashboardPlaceholder";
import Fieldset from "components/porter/Fieldset";
import Image from "components/porter/Image";
import PorterLink from "components/porter/Link";
import SearchBar from "components/porter/SearchBar";
import Select from "components/porter/Select";
import Spacer from "components/porter/Spacer";
import StatusDot from "components/porter/StatusDot";
import Text from "components/porter/Text";
Expand All @@ -22,19 +24,27 @@ import { useDatabaseList } from "lib/hooks/useDatabaseList";
import { Context } from "shared/Context";
import { search } from "shared/search";
import { readableDate } from "shared/string_utils";
import engine from "assets/computer-chip.svg";
import database from "assets/database.svg";
import grid from "assets/grid.png";
import list from "assets/list.png";
import notFound from "assets/not-found.png";
import time from "assets/time.png";

import { getDatastoreIcon, getEngineIcon } from "./icons";
import EngineTag from "./tags/EngineTag";

const DatabaseDashboard: React.FC = () => {
const { currentCluster } = useContext(Context);

const [searchValue, setSearchValue] = useState("");
const [view, setView] = useState<"grid" | "list">("grid");
const [typeFilter, setTypeFilter] = useState<"all" | "RDS" | "ELASTICACHE">(
"all"
);
const [engineFilter, setEngineFilter] = useState<
"all" | "POSTGRES" | "AURORA-POSTGRES" | "REDIS"
>("all");

const { datastores, isLoading } = useDatabaseList();

Expand All @@ -44,8 +54,28 @@ const DatabaseDashboard: React.FC = () => {
isCaseSensitive: false,
});

return _.sortBy(filteredBySearch, ["name"]);
}, [datastores, searchValue]);
const sortedFilteredBySearch = _.sortBy(filteredBySearch, ["name"]);

const filteredByDatastoreType = sortedFilteredBySearch.filter(
(datastore: ClientDatastore) => {
if (typeFilter === "all") {
return true;
}
return datastore.type === typeFilter;
}
);

const filteredByEngine = filteredByDatastoreType.filter(
(datastore: ClientDatastore) => {
if (engineFilter === "all") {
return true;
}
return datastore.template.engine.name === engineFilter;
}
);

return filteredByEngine;
}, [datastores, searchValue, typeFilter, engineFilter]);

const renderContents = (): JSX.Element => {
if (currentCluster?.status === "UPDATING_UNAVAILABLE") {
Expand Down Expand Up @@ -86,6 +116,83 @@ const DatabaseDashboard: React.FC = () => {
return (
<>
<Container row spaced>
<Select
options={[
{ value: "all", label: "All" },
{
value: "RDS",
label: "RDS",
icon: getDatastoreIcon("RDS"),
},
{
value: "ELASTICACHE",
label: "Elasticache",
icon: getDatastoreIcon("ELASTICACHE"),
},
]}
width="210px"
height="29px"
value={typeFilter}
setValue={(value) => {
if (
value === "all" ||
value === "RDS" ||
value === "ELASTICACHE"
) {
setTypeFilter(value);
setEngineFilter("all");
}
}}
prefix={
<Container row>
<Image src={database} size={15} opacity={0.6} />
<Spacer inline x={0.5} />
Type
</Container>
}
/>
<Spacer inline x={1} />
<Select
options={[
{ value: "all", label: "All" },
{
value: "POSTGRES",
label: "PostgreSQL",
icon: getEngineIcon("POSTGRES"),
},
{
value: "AURORA-POSTGRES",
label: "Aurora PostgreSQL",
icon: getEngineIcon("POSTGRES"),
},
{
value: "REDIS",
label: "Redis",
icon: getEngineIcon("REDIS"),
},
]}
width="270px"
height="29px"
value={engineFilter}
setValue={(value) => {
if (
value === "all" ||
value === "POSTGRES" ||
value === "AURORA-POSTGRES" ||
value === "REDIS"
) {
setEngineFilter(value);
}
}}
prefix={
<Container row>
<Image src={engine} size={15} opacity={0.6} />
<Spacer inline x={0.5} />
Engine
</Container>
}
/>
<Spacer inline x={2} />
<SearchBar
value={searchValue}
setValue={(x) => {
Expand Down Expand Up @@ -119,9 +226,9 @@ const DatabaseDashboard: React.FC = () => {
true
}
height="30px"
width="140px"
width="70px"
>
<I className="material-icons">add</I> New database
<I className="material-icons">add</I> New
</Button>
</PorterLink>
</Container>
Expand All @@ -131,7 +238,9 @@ const DatabaseDashboard: React.FC = () => {
<Fieldset>
<Container row>
<PlaceholderIcon src={notFound} />
<Text color="helper">No matching databases were found.</Text>
<Text color="helper">
No databases matching filters were found.
</Text>
</Container>
</Fieldset>
) : isLoading ? (
Expand Down
Loading

0 comments on commit 51a39d8

Please sign in to comment.