Skip to content

Commit

Permalink
Implement UX Tweaks (#35)
Browse files Browse the repository at this point in the history
Previously, we have noticed that users have had difficulties navigating
over to the API model page after the Docker Extension successfully
collects traffic. This PR is intended to reduce some of this friction.

Changes include:
- Updating the primary button on the Agent screen so that it navigates
to the API model page instead of opening container logs
- Explicity linking to the specified service's API model page. I'm not
sure if this was also an issue, but I think it's a good sanity check
- Slight tweaks to the `dev-extension` Makefile target to make it easier
to use Chrome Web tools for debugging & inspection
  • Loading branch information
versilis authored Feb 17, 2023
1 parent 5434b5a commit 96f9545
Show file tree
Hide file tree
Showing 6 changed files with 57 additions and 23 deletions.
9 changes: 8 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
IMAGE ?= akitasoftware/akita-docker-extension
TAG ?= latest
CONFIG_FILE ?= application.yml

CHROME_TOOLS ?= 0
BUILDER=buildx-multi-arch

INFO_COLOR = \033[0;36m
Expand All @@ -21,6 +21,13 @@ install-extension: build-extension ## Install the extension
.PHONY: install-extension

dev-extension: install-extension
ifeq ($(CHROME_TOOLS),1)
@docker extension dev debug $(IMAGE):$(TAG)
echo "Chrome tools enabled. Open the extension in Docker Desktop to inspect the extension."
else
@docker extension dev reset $(IMAGE):$(TAG)
echo "Chrome tools are disabled. To enable them, run 'make dev-extension CHROME_TOOLS=1'"
endif
@docker extension dev ui-source $(IMAGE):$(TAG) http://localhost:3000 && npm run dev --prefix ui
.PHONY: dev-extension

Expand Down
1 change: 1 addition & 0 deletions ui/src/data/queries/service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { AkitaURL, addAuthHeader } from "./utils";

export interface Service {
id: string;
name: string;
}

Expand Down
5 changes: 5 additions & 0 deletions ui/src/views/agent/AgentPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { removeAkitaContainer } from "../../data/queries/container";
import { useAkitaAgent } from "../../hooks/use-akita-agent";
import { useAkitaUser } from "../../hooks/use-akita-user";
import { useDockerDesktopClient } from "../../hooks/use-docker-desktop-client";
import { useAkitaServices } from "../../hooks/user-akita-services";
import { HelpSpeedDial } from "../shared/components/HelpSpeedDial";
import { AgentHeader } from "./components/AgentHeader";
import { AgentStatus } from "./components/AgentStatus";
Expand All @@ -16,6 +17,7 @@ export const AgentPage = () => {
const [isSettingsOpen, setIsSettingsOpen] = React.useState(false);
const { config, containerInfo, restartAgent, isInitialized, hasInitializationFailed } =
useAkitaAgent();
const services = useAkitaServices(config);
const navigate = useNavigate();
const wasWarned = useRef(false);
const wasViewEventSent = useRef(false);
Expand Down Expand Up @@ -83,6 +85,8 @@ export const AgentPage = () => {
onSendAnalyticsEvent={sendAnalyticsEvent}
/>
<AgentStatus
targetedProjectName={config?.project_name}
services={services}
containerInfo={containerInfo}
onRestartAgent={restartAgent}
onFailure={handleFailure}
Expand All @@ -93,6 +97,7 @@ export const AgentPage = () => {
</Stack>
<SettingsDialog
config={config}
services={services}
isOpen={isSettingsOpen && containerInfo !== undefined}
onConfigChange={handleConfigChange}
onCloseDialog={() => setIsSettingsOpen(false)}
Expand Down
58 changes: 38 additions & 20 deletions ui/src/views/agent/components/AgentStatus.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import ErrorOutlineOutlinedIcon from "@mui/icons-material/ErrorOutlineOutlined";
import { Box, Button, CircularProgress, Link, Paper, Typography } from "@mui/material";
import React, { useEffect, useState } from "react";
import { ContainerInfo, ContainerState } from "../../../data/queries/container";
import { Service } from "../../../data/queries/service";
import { useContainerState } from "../../../hooks/use-container-state";
import { useDockerDesktopClient } from "../../../hooks/use-docker-desktop-client";

Expand All @@ -13,6 +14,8 @@ interface AgentStatusProps {
onFailure: () => void;
onSendAnalyticsEvent: (eventName: string, properties?: Record<string, any>) => void;
hasInitializationFailed: boolean;
services: Service[];
targetedProjectName?: string;
}

export const AgentStatus = ({
Expand All @@ -22,6 +25,8 @@ export const AgentStatus = ({
isInitialized,
hasInitializationFailed,
onSendAnalyticsEvent,
services,
targetedProjectName,
}: AgentStatusProps) => {
const ddClient = useDockerDesktopClient();
const containerState = useContainerState(2000, containerInfo?.Id);
Expand Down Expand Up @@ -78,6 +83,25 @@ export const AgentStatus = ({
.catch((err) => console.error("Failed to navigate to container", err));
};

const resolveAPIModelURL = () => {
const service = services.find((service) => service.name === targetedProjectName);
// If the service is not found, just send them to the dashboard's overview page
// It might not send them to the right project, but it's better than nothing ¯\_(ツ)_/¯
if (!service) {
return "https://app.akita.software";
}

// If the service is found, return the API Model URL with the project ID
return `https://app.akita.software/service/${service.id}/deployment/default/model`;
};

const handleViewWebDashboard = () => {
onSendAnalyticsEvent("Opened Akita Web Dashboard");
ddClient.host.openExternal(resolveAPIModelURL());
};

// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
return (
<Paper
elevation={3}
Expand All @@ -101,19 +125,18 @@ export const AgentStatus = ({
</Box>
{status === "Running" ? (
<Typography variant={"body1"}>
Akita is running. Check the{" "}
<Link
onClick={() => {
onSendAnalyticsEvent("Opened Akita Web Dashboard");
ddClient.host.openExternal("https://app.akita.software");
}}
sx={{
cursor: "pointer",
}}
>
Akita Dashboard
</Link>{" "}
to view your models.
Akita is running.{" "}
{canViewContainer && (
<Link
onClick={handleViewContainer}
sx={{
cursor: "pointer",
}}
>
Check the Agent container
</Link>
)}
{canViewContainer && " to view the logs."}
</Typography>
) : status === "Starting" ? (
<Typography variant={"body1"}>Akita is starting...</Typography>
Expand All @@ -125,13 +148,8 @@ export const AgentStatus = ({
<Typography variant={"body1"}>Fetching Akita Agent status...</Typography>
)}
<Box alignContent={"center"} marginLeft={"auto"} whiteSpace={"nowrap"} textAlign={"center"}>
<Button
variant={"contained"}
color={"primary"}
onClick={handleViewContainer}
disabled={!canViewContainer}
>
View Container
<Button variant={"contained"} color={"primary"} onClick={handleViewWebDashboard}>
View API Model
</Button>
</Box>
</Paper>
Expand Down
5 changes: 3 additions & 2 deletions ui/src/views/agent/components/SettingsDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@ import {
import React, { useEffect, useState } from "react";
import { AgentConfig } from "../../../data/queries/agent-config";
import { ContainerInfo, ContainerState, useContainers } from "../../../data/queries/container";
import { useAkitaServices } from "../../../hooks/user-akita-services";
import { Service } from "../../../data/queries/service";

interface SettingsDialogProps {
config?: AgentConfig;
isOpen: boolean;
onConfigChange: (config: AgentConfig) => void;
onCloseDialog: () => void;
onSendAnalyticsEvent: (eventName: string, properties?: Record<string, any>) => void;
services: Service[];
}

interface InputState {
Expand Down Expand Up @@ -56,6 +57,7 @@ export const SettingsDialog = ({
onCloseDialog,
config,
onSendAnalyticsEvent,
services,
}: SettingsDialogProps) => {
const containers = useContainers(
(container: ContainerInfo) =>
Expand All @@ -68,7 +70,6 @@ export const SettingsDialog = ({
const [input, setInput] = useState<InputState>(inputStateFromConfig(config));

const [isUpdatedConfigValid, setIsUpdatedConfigValid] = useState(false);
const services = useAkitaServices(config);

useEffect(() => {
setInput(inputStateFromConfig(config));
Expand Down
2 changes: 2 additions & 0 deletions ui/src/views/config/ConfigPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ export const ConfigPage = () => {
}
}, [agentConfig]);

// TODO: Validation doesn't work in dev mode because of CORS. We should probably add an env var to account for this.
// As a hacky workaround, you can comment out any checks in this function and just return true.
const validateSubmission = async () => {
const serviceResponse = await getServices(configInput.apiKey, configInput.apiSecret).catch(
(err) => {
Expand Down

0 comments on commit 96f9545

Please sign in to comment.