diff --git a/dashboard/src/assets/eye-off.svg b/dashboard/src/assets/eye-off.svg
new file mode 100644
index 0000000000..c9fc51a35f
--- /dev/null
+++ b/dashboard/src/assets/eye-off.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/dashboard/src/assets/eye.svg b/dashboard/src/assets/eye.svg
new file mode 100644
index 0000000000..f2885c1efa
--- /dev/null
+++ b/dashboard/src/assets/eye.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/dashboard/src/components/porter/ControlledInput.tsx b/dashboard/src/components/porter/ControlledInput.tsx
index 33f4708da0..be33b51502 100644
--- a/dashboard/src/components/porter/ControlledInput.tsx
+++ b/dashboard/src/components/porter/ControlledInput.tsx
@@ -1,5 +1,12 @@
-import React from "react";
+import React, { useState } from "react";
import styled from "styled-components";
+
+import eyeOff from "assets/eye-off.svg";
+import eye from "assets/eye.svg";
+
+import Container from "./Container";
+import Icon from "./Icon";
+import Spacer from "./Spacer";
import Tooltip from "./Tooltip";
/*
@@ -45,6 +52,11 @@ export const ControlledInput = React.forwardRef<
},
ref
) => {
+ const [isVisible, setIsVisible] = useState(false);
+ const toggleVisibility = (): void => {
+ setIsVisible(!isVisible);
+ };
+
return disabled && disabledTooltip ? (
@@ -72,29 +84,41 @@ export const ControlledInput = React.forwardRef<
) : (
-
- {label && }
-
+
+
+
+ {label && }
+
+
+ {type === "password" && (
+ <>
+
+
+
+
+ >
+ )}
+
{error && (
error
{error}
)}
-
+
);
}
);
diff --git a/dashboard/src/lib/addons/index.ts b/dashboard/src/lib/addons/index.ts
index 9241e8c842..1346720c36 100644
--- a/dashboard/src/lib/addons/index.ts
+++ b/dashboard/src/lib/addons/index.ts
@@ -57,8 +57,11 @@ export const clientAddonValidator = z.object({
tailscaleConfigValidator,
]),
});
+export type ClientAddonType = z.infer<
+ typeof clientAddonValidator
+>["config"]["type"];
export type ClientAddon = z.infer & {
- template: AddonTemplate;
+ template: AddonTemplate;
};
export const legacyAddonValidator = z.object({
name: z.string(),
diff --git a/dashboard/src/lib/addons/metabase.ts b/dashboard/src/lib/addons/metabase.ts
index e38290afa2..d85ef5ed26 100644
--- a/dashboard/src/lib/addons/metabase.ts
+++ b/dashboard/src/lib/addons/metabase.ts
@@ -8,7 +8,7 @@ export const metabaseConfigValidator = z.object({
datastore: z
.object({
host: z.string().nonempty(),
- port: z.number(),
+ port: z.coerce.number(),
databaseName: z.string().nonempty(),
username: z.string().nonempty(),
password: z.string().nonempty(),
diff --git a/dashboard/src/lib/addons/template.ts b/dashboard/src/lib/addons/template.ts
index 51cdbfc47d..ad43a7b624 100644
--- a/dashboard/src/lib/addons/template.ts
+++ b/dashboard/src/lib/addons/template.ts
@@ -7,7 +7,7 @@ import NewRelicForm from "main/home/add-on-dashboard/newrelic/NewRelicForm";
import TailscaleForm from "main/home/add-on-dashboard/tailscale/TailscaleForm";
import TailscaleOverview from "main/home/add-on-dashboard/tailscale/TailscaleOverview";
-import { type ClientAddon } from ".";
+import { type ClientAddon, type ClientAddonType } from ".";
export type AddonTemplateTag =
| "Monitoring"
@@ -39,34 +39,68 @@ export const DEFAULT_ADDON_TAB = {
component: () => null,
};
-export type AddonTemplate = {
- type: ClientAddon["config"]["type"];
+export type AddonTemplate = {
+ type: T;
displayName: string;
description: string;
icon: string;
tags: AddonTemplateTag[];
tabs: AddonTab[]; // this what is rendered on the dashboard after the addon is deployed
+ defaultValues: ClientAddon["config"] & { type: T };
};
-export const ADDON_TEMPLATE_REDIS: AddonTemplate = {
+export const ADDON_TEMPLATE_REDIS: AddonTemplate<"redis"> = {
type: "redis",
displayName: "Redis",
description: "An in-memory database that persists on disk.",
icon: "https://cdn4.iconfinder.com/data/icons/redis-2/1451/Untitled-2-512.png",
tags: ["Database"],
tabs: [],
+ defaultValues: {
+ type: "redis",
+ cpuCores: {
+ value: 0.5,
+ readOnly: false,
+ },
+ ramMegabytes: {
+ value: 512,
+ readOnly: false,
+ },
+ storageGigabytes: {
+ value: 1,
+ readOnly: false,
+ },
+ password: "",
+ },
};
-export const ADDON_TEMPLATE_POSTGRES: AddonTemplate = {
+export const ADDON_TEMPLATE_POSTGRES: AddonTemplate<"postgres"> = {
type: "postgres",
displayName: "Postgres",
description: "An object-relational database system.",
icon: "https://cdn.jsdelivr.net/gh/devicons/devicon/icons/postgresql/postgresql-original.svg",
tags: ["Database"],
tabs: [],
+ defaultValues: {
+ type: "postgres",
+ cpuCores: {
+ value: 0.5,
+ readOnly: false,
+ },
+ ramMegabytes: {
+ value: 512,
+ readOnly: false,
+ },
+ storageGigabytes: {
+ value: 1,
+ readOnly: false,
+ },
+ username: "postgres",
+ password: "postgres",
+ },
};
-export const ADDON_TEMPLATE_DATADOG: AddonTemplate = {
+export const ADDON_TEMPLATE_DATADOG: AddonTemplate<"datadog"> = {
type: "datadog",
displayName: "DataDog",
description:
@@ -91,9 +125,19 @@ export const ADDON_TEMPLATE_DATADOG: AddonTemplate = {
component: Settings,
},
],
+ defaultValues: {
+ type: "datadog",
+ cpuCores: 0.5,
+ ramMegabytes: 512,
+ site: "datadoghq.com",
+ apiKey: "",
+ loggingEnabled: false,
+ apmEnabled: false,
+ dogstatsdEnabled: false,
+ },
};
-export const ADDON_TEMPLATE_MEZMO: AddonTemplate = {
+export const ADDON_TEMPLATE_MEZMO: AddonTemplate<"mezmo"> = {
type: "mezmo",
displayName: "Mezmo",
description: "A popular logging management system.",
@@ -117,9 +161,13 @@ export const ADDON_TEMPLATE_MEZMO: AddonTemplate = {
component: Settings,
},
],
+ defaultValues: {
+ type: "mezmo",
+ ingestionKey: "",
+ },
};
-export const ADDON_TEMPLATE_METABASE: AddonTemplate = {
+export const ADDON_TEMPLATE_METABASE: AddonTemplate<"metabase"> = {
type: "metabase",
displayName: "Metabase",
description: "An open-source business intelligence tool.",
@@ -142,9 +190,22 @@ export const ADDON_TEMPLATE_METABASE: AddonTemplate = {
component: Settings,
},
],
+ defaultValues: {
+ type: "metabase",
+ exposedToExternalTraffic: true,
+ porterDomain: "",
+ customDomain: "",
+ datastore: {
+ host: "",
+ port: 0,
+ databaseName: "",
+ username: "",
+ password: "",
+ },
+ },
};
-export const ADDON_TEMPLATE_NEWRELIC: AddonTemplate = {
+export const ADDON_TEMPLATE_NEWRELIC: AddonTemplate<"newrelic"> = {
type: "newrelic",
displayName: "New Relic",
description: "Monitor your applications and infrastructure.",
@@ -168,9 +229,21 @@ export const ADDON_TEMPLATE_NEWRELIC: AddonTemplate = {
component: Settings,
},
],
+ defaultValues: {
+ type: "newrelic",
+ licenseKey: "",
+ insightsKey: "",
+ personalApiKey: "",
+ accountId: "",
+ loggingEnabled: false,
+ kubeEventsEnabled: false,
+ metricsAdapterEnabled: false,
+ prometheusEnabled: false,
+ pixieEnabled: false,
+ },
};
-export const ADDON_TEMPLATE_TAILSCALE: AddonTemplate = {
+export const ADDON_TEMPLATE_TAILSCALE: AddonTemplate<"tailscale"> = {
type: "tailscale",
displayName: "Tailscale",
description: "A VPN for your applications and datastores.",
@@ -199,12 +272,18 @@ export const ADDON_TEMPLATE_TAILSCALE: AddonTemplate = {
component: Settings,
},
],
+ defaultValues: {
+ type: "tailscale",
+ authKey: "",
+ subnetRoutes: [],
+ },
};
-export const SUPPORTED_ADDON_TEMPLATES: AddonTemplate[] = [
- ADDON_TEMPLATE_DATADOG,
- ADDON_TEMPLATE_MEZMO,
- ADDON_TEMPLATE_METABASE,
- ADDON_TEMPLATE_NEWRELIC,
- ADDON_TEMPLATE_TAILSCALE,
-];
+export const SUPPORTED_ADDON_TEMPLATES: Array> =
+ [
+ ADDON_TEMPLATE_DATADOG,
+ ADDON_TEMPLATE_MEZMO,
+ ADDON_TEMPLATE_METABASE,
+ ADDON_TEMPLATE_NEWRELIC,
+ ADDON_TEMPLATE_TAILSCALE,
+ ];
diff --git a/dashboard/src/main/home/add-on-dashboard/AddonForm.tsx b/dashboard/src/main/home/add-on-dashboard/AddonForm.tsx
index b93737e0ec..2303e32955 100644
--- a/dashboard/src/main/home/add-on-dashboard/AddonForm.tsx
+++ b/dashboard/src/main/home/add-on-dashboard/AddonForm.tsx
@@ -9,7 +9,7 @@ import { ControlledInput } from "components/porter/ControlledInput";
import Spacer from "components/porter/Spacer";
import Text from "components/porter/Text";
import VerticalSteps from "components/porter/VerticalSteps";
-import { defaultClientAddon, type ClientAddon } from "lib/addons";
+import { type ClientAddon, type ClientAddonType } from "lib/addons";
import { type AddonTemplate } from "lib/addons/template";
import { useAddonList } from "lib/hooks/useAddon";
import { useDefaultDeploymentTarget } from "lib/hooks/useDeploymentTarget";
@@ -20,10 +20,12 @@ import DashboardHeader from "../cluster-dashboard/DashboardHeader";
import ClusterContextProvider from "../infrastructure-dashboard/ClusterContextProvider";
import Configuration from "./common/Configuration";
-type Props = {
- template: AddonTemplate;
+type Props = {
+ template: AddonTemplate;
};
-const AddonForm: React.FC = ({ template }) => {
+const AddonForm = ({
+ template,
+}: Props): JSX.Element => {
const { currentProject, currentCluster } = useContext(Context);
const { defaultDeploymentTarget, isDefaultDeploymentTargetLoading } =
useDefaultDeploymentTarget();
@@ -56,7 +58,12 @@ const AddonForm: React.FC = ({ template }) => {
}, [watchName]);
useEffect(() => {
- reset(defaultClientAddon(template.type));
+ reset({
+ expanded: true,
+ name: { readOnly: false, value: template.type },
+ config: template.defaultValues,
+ template,
+ });
}, [template]);
useEffect(() => {
diff --git a/dashboard/src/main/home/add-on-dashboard/AddonTemplates.tsx b/dashboard/src/main/home/add-on-dashboard/AddonTemplates.tsx
index 0945c20f00..0b1122cac2 100644
--- a/dashboard/src/main/home/add-on-dashboard/AddonTemplates.tsx
+++ b/dashboard/src/main/home/add-on-dashboard/AddonTemplates.tsx
@@ -4,6 +4,7 @@ import styled from "styled-components";
import Back from "components/porter/Back";
import Spacer from "components/porter/Spacer";
+import { type ClientAddonType } from "lib/addons";
import {
AddonTemplateTagColor,
SUPPORTED_ADDON_TEMPLATES,
@@ -47,31 +48,35 @@ const AddonTemplates: React.FC = () => {
disableLineBreak
/>
- {SUPPORTED_ADDON_TEMPLATES.map((template: AddonTemplate) => {
- return (
- {
- history.push(`/addons/new?addon_name=${template.type}`);
- }}
- >
-
- {template.displayName}
- {template.description}
-
- {template.tags.map((t) => (
-
- {t}
-
- ))}
-
- );
- })}
+ {SUPPORTED_ADDON_TEMPLATES.map(
+ (template: AddonTemplate) => {
+ return (
+ {
+ history.push(`/addons/new?addon_name=${template.type}`);
+ }}
+ >
+
+ {template.displayName}
+
+ {template.description}
+
+
+ {template.tags.map((t) => (
+
+ {t}
+
+ ))}
+
+ );
+ }
+ )}
);
diff --git a/dashboard/src/main/home/add-on-dashboard/datadog/DatadogForm.tsx b/dashboard/src/main/home/add-on-dashboard/datadog/DatadogForm.tsx
index 03ac2573b6..cfed959394 100644
--- a/dashboard/src/main/home/add-on-dashboard/datadog/DatadogForm.tsx
+++ b/dashboard/src/main/home/add-on-dashboard/datadog/DatadogForm.tsx
@@ -39,10 +39,10 @@ const DatadogForm: React.FC = () => {
DataDog API Key
diff --git a/dashboard/src/main/home/add-on-dashboard/metabase/MetabaseForm.tsx b/dashboard/src/main/home/add-on-dashboard/metabase/MetabaseForm.tsx
index cf53c1792f..09d6fabf78 100644
--- a/dashboard/src/main/home/add-on-dashboard/metabase/MetabaseForm.tsx
+++ b/dashboard/src/main/home/add-on-dashboard/metabase/MetabaseForm.tsx
@@ -133,6 +133,7 @@ const MetabaseDatastoreConnection: React.FC = () => {
type="text"
width="600px"
{...register("config.datastore.host")}
+ placeholder="my-host.com"
error={errors.config?.datastore?.host?.message}
/>
@@ -174,6 +175,7 @@ const MetabaseDatastoreConnection: React.FC = () => {
type="text"
width="600px"
{...register("config.datastore.username")}
+ placeholder="my-username"
error={errors.config?.datastore?.username?.message}
/>
@@ -184,9 +186,10 @@ const MetabaseDatastoreConnection: React.FC = () => {
|
diff --git a/dashboard/src/main/home/add-on-dashboard/mezmo/MezmoForm.tsx b/dashboard/src/main/home/add-on-dashboard/mezmo/MezmoForm.tsx
index 2984263319..1455e88b2f 100644
--- a/dashboard/src/main/home/add-on-dashboard/mezmo/MezmoForm.tsx
+++ b/dashboard/src/main/home/add-on-dashboard/mezmo/MezmoForm.tsx
@@ -28,10 +28,10 @@ const MezmoForm: React.FC = () => {
Ingestion Key
diff --git a/dashboard/src/main/home/add-on-dashboard/newrelic/NewRelicForm.tsx b/dashboard/src/main/home/add-on-dashboard/newrelic/NewRelicForm.tsx
index 7e07d4f8f7..e8b1122d5a 100644
--- a/dashboard/src/main/home/add-on-dashboard/newrelic/NewRelicForm.tsx
+++ b/dashboard/src/main/home/add-on-dashboard/newrelic/NewRelicForm.tsx
@@ -28,7 +28,7 @@ const NewRelicForm: React.FC = () => {
NewRelic License Key
{
NewRelic Insights Key
{
NewRelic Personal API Key
{