Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 7 additions & 13 deletions app/components/Assistant/ChatMessage/chatMessage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,22 +26,16 @@ export const ChatMessage = ({
<MessageRow isUser={isUser}>
{!isUser && (
<Box
alt="BRC Analytics"
component="img"
src="/logo/brc.svg"
sx={{
alignItems: "center",
backgroundColor: "#1976d2",
borderRadius: "50%",
color: "white",
display: "flex",
flexShrink: 0,
fontSize: "14px",
fontWeight: 600,
height: 32,
justifyContent: "center",
width: 32,
height: 20,
marginTop: "6px",
width: "auto",
}}
>
AI
</Box>
/>
)}
<Bubble>
{isUser ? (
Expand Down
45 changes: 30 additions & 15 deletions app/components/Assistant/ChatPanel/chatPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,26 +19,20 @@ interface ChatMessageDisplay {

interface ChatPanelProps {
error: string | null;
isRestoring?: boolean;
loading: boolean;
messages: ChatMessageDisplay[];
onRetry?: () => Promise<void>;
onSend: (message: string) => void;
suggestions: SuggestionChip[];
}

/**
* The main chat interface with message list, input, and suggestion chips.
* @param props - Component props
* @param props.error - Error message to display
* @param props.loading - Whether the assistant is processing
* @param props.messages - Chat message history
* @param props.onSend - Callback to send a message
* @param props.suggestions - Suggestion chips to display
* @returns Chat panel element
*/
export const ChatPanel = ({
error,
isRestoring,
loading,
messages,
onRetry,
onSend,
suggestions,
}: ChatPanelProps): JSX.Element => {
Expand Down Expand Up @@ -70,10 +64,21 @@ export const ChatPanel = ({
}
};

const inputDisabled = loading || !!isRestoring;

return (
<ChatContainer>
<MessagesContainer>
{messages.length === 0 && (
{isRestoring && (
<Box sx={{ alignItems: "center", display: "flex", gap: 1, p: 4 }}>
<CircularProgress size={20} />
<Typography color="text.secondary" variant="body2">
Restoring conversation...
</Typography>
</Box>
)}

{!isRestoring && messages.length === 0 && (
<Box sx={{ p: 4, textAlign: "center" }}>
<Typography color="text.secondary" variant="body1">
Welcome! I can help you explore BRC Analytics data and set up
Expand All @@ -96,7 +101,17 @@ export const ChatPanel = ({
)}

{error && (
<Alert severity="error" sx={{ mx: 1 }}>
<Alert
action={
onRetry && (
<Button onClick={onRetry} size="small">
Retry
</Button>
)
}
severity="error"
sx={{ mx: 1 }}
>
{error}
</Alert>
)}
Expand All @@ -106,13 +121,13 @@ export const ChatPanel = ({

<SuggestionChips
chips={suggestions}
disabled={loading}
disabled={inputDisabled}
onSelect={handleChipSelect}
/>

<InputRow>
<TextField
disabled={loading}
disabled={inputDisabled}
fullWidth
maxRows={4}
multiline
Expand All @@ -123,7 +138,7 @@ export const ChatPanel = ({
value={input}
/>
<Button
disabled={loading || !input.trim()}
disabled={inputDisabled || !input.trim()}
onClick={handleSend}
variant="contained"
>
Expand Down
33 changes: 31 additions & 2 deletions app/components/Assistant/SchemaPanel/schemaPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { JSX } from "react";
import { JSX, useCallback } from "react";
import { Box, Button, Chip, Divider, Typography } from "@mui/material";
import {
AnalysisSchema,
FieldStatus,
SchemaFieldState,
} from "../../../types/api";
import { ASSISTANT_HANDOFF_KEY } from "../../../views/WorkflowInputsView/hooks/UseAssistantHandoff/types";
import {
FieldRow,
FieldValue,
Expand Down Expand Up @@ -43,6 +44,19 @@ const FIELD_ORDER: (keyof AnalysisSchema)[] = [
...OPTIONAL_FIELDS,
];

function resolveDataSource(value: string | null | undefined): "ena" | "upload" {
if (!value) return "ena";
const lower = value.toLowerCase();
if (
lower.includes("upload") ||
lower.includes("own") ||
lower.includes("local")
) {
return "upload";
}
return "ena";
}

/**
* Get the status indicator for a schema field.
* @param props - Component props
Expand Down Expand Up @@ -70,6 +84,16 @@ export const SchemaPanel = ({
handoffUrl,
schema,
}: SchemaPanelProps): JSX.Element => {
const handleContinue = useCallback((): void => {
if (!handoffUrl || !schema) return;
const handoff = {
dataSource: resolveDataSource(schema.data_source.value),
timestamp: Date.now(),
};
localStorage.setItem(ASSISTANT_HANDOFF_KEY, JSON.stringify(handoff));
window.location.href = handoffUrl;
}, [handoffUrl, schema]);

if (!schema) {
return (
<PanelContainer>
Expand Down Expand Up @@ -126,7 +150,12 @@ export const SchemaPanel = ({

{handoffUrl && (
<Box sx={{ p: 2 }}>
<Button fullWidth href={handoffUrl} size="large" variant="contained">
<Button
fullWidth
onClick={handleContinue}
size="large"
variant="contained"
>
Continue to Workflow Setup
</Button>
</Box>
Expand Down
17 changes: 15 additions & 2 deletions app/components/Assistant/SuggestionChips/suggestionChips.styles.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,22 @@
import { Box } from "@mui/material";
import { Box, Chip } from "@mui/material";
import styled from "@emotion/styled";

export const ChipsContainer = styled(Box)({
display: "flex",
flexWrap: "wrap",
gap: "8px",
padding: "4px 0",
padding: "8px 12px",
});

export const StyledSuggestionChip = styled(Chip)({
"&:hover": {
backgroundColor: "#e3f2fd",
borderColor: "#90caf9",
},
borderColor: "#bbdefb",
borderRadius: "16px",
fontSize: "0.8125rem",
height: "auto",
padding: "4px 2px",
transition: "background-color 0.15s, border-color 0.15s",
});
13 changes: 2 additions & 11 deletions app/components/Assistant/SuggestionChips/suggestionChips.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,13 @@
import { JSX } from "react";
import { Chip } from "@mui/material";
import { SuggestionChip } from "../../../types/api";
import { ChipsContainer } from "./suggestionChips.styles";
import { ChipsContainer, StyledSuggestionChip } from "./suggestionChips.styles";

interface SuggestionChipsProps {
chips: SuggestionChip[];
disabled?: boolean;
onSelect: (message: string) => void;
}

/**
* Quick-tap suggestion chips shown below the chat input.
* @param props - Component props
* @param props.chips - Available suggestion chips
* @param props.disabled - Whether chips are disabled
* @param props.onSelect - Callback when a chip is selected
* @returns Suggestion chips row
*/
export const SuggestionChips = ({
chips,
disabled,
Expand All @@ -27,7 +18,7 @@ export const SuggestionChips = ({
return (
<ChipsContainer>
{chips.map((chip) => (
<Chip
<StyledSuggestionChip
clickable
disabled={disabled}
key={chip.label}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { StepContent } from "@databiosphere/findable-ui/lib/components/Stepper/components/Step/components/StepContent/stepContent";
import { StepLabel } from "@databiosphere/findable-ui/lib/components/Stepper/components/Step/components/StepLabel/stepLabel";
import { Step } from "@databiosphere/findable-ui/lib/components/Stepper/components/Step/step";
import { JSX } from "react";
import { JSX, useEffect, useRef } from "react";
import { ToggleButtonGroup } from "../components/ToggleButtonGroup/toggleButtonGroup";
import { useToggleButtonGroup } from "../hooks/UseToggleButtonGroup/useToggleButtonGroup";
import { StepProps } from "../types";
Expand Down Expand Up @@ -30,6 +30,7 @@ export const SequencingStep = ({
entryLabel,
genome,
index,
initialDataSourceView,
onConfigure,
stepKey,
workflow,
Expand All @@ -40,9 +41,22 @@ export const SequencingStep = ({
const rowSelection = useRowSelection(configuredInput);
const state = { columnFilters, rowSelection };
const { actions, table } = useTable(enaTaxonomyId, state, onConfigure);
const { onChange, value } = useToggleButtonGroup(VIEW.ENA);
const initialView =
initialDataSourceView === VIEW.UPLOAD_MY_DATA
? VIEW.UPLOAD_MY_DATA
: VIEW.ENA;
const { onChange, value } = useToggleButtonGroup(initialView);
const { taxonomyMatches } = useTaxonomyMatches(table);
const { requirementsMatches } = useRequirementsMatches(table, genome);

const didInitRef = useRef(false);
useEffect(() => {
if (didInitRef.current || initialDataSourceView !== VIEW.UPLOAD_MY_DATA)
return;
didInitRef.current = true;
onConfigure(clearSequencingData());
onConfigure(getUploadMyOwnSequencingData(stepKey));
}, [initialDataSourceView, onConfigure, stepKey]);
return (
<Step active={active} completed={completed} index={index}>
<StepLabel>{entryLabel}</StepLabel>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
OnConfigure,
} from "../../../../../../../../../../views/WorkflowInputsView/hooks/UseConfigureInputs/types";
import { Status, OnLaunchGalaxy } from "./hooks/UseLaunchGalaxy/types";
import { VIEW } from "./SequencingStep/components/ToggleButtonGroup/types";
import { OnContinue, OnEdit } from "../../hooks/UseStepper/types";
import { Assembly } from "../../../../../../../../../../views/WorkflowInputsView/types";

Expand All @@ -27,6 +28,7 @@ export interface StepProps
configuredInput: ConfiguredInput;
entryLabel: string;
genome?: Assembly;
initialDataSourceView?: VIEW;
onConfigure: OnConfigure;
onContinue: OnContinue;
onEdit: OnEdit;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@ import { UseStepper } from "./types";
import { getInitialActiveStep, getNextActiveStep } from "./utils";
import { StepConfig } from "../../components/Step/types";

export const useStepper = (steps: StepConfig[]): UseStepper => {
export const useStepper = (
steps: StepConfig[],
initialActiveStepOverride?: number
): UseStepper => {
const [activeStep, setActiveStep] = useState<number>(
getInitialActiveStep(steps)
initialActiveStepOverride ?? getInitialActiveStep(steps)
);

const onContinue = useCallback((): void => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
OnLaunchGalaxy,
} from "./components/Step/hooks/UseLaunchGalaxy/types";
import { StepConfig } from "./components/Step/types";
import { VIEW } from "./components/Step/SequencingStep/components/ToggleButtonGroup/types";
import { ConfiguredInput } from "../../../../../../../../views/WorkflowInputsView/hooks/UseConfigureInputs/types";
import { Assembly } from "../../../../../../../../views/WorkflowInputsView/types";
import { OnContinue, OnEdit } from "./hooks/UseStepper/types";
Expand All @@ -14,6 +15,7 @@ export interface Props {
configuredInput: ConfiguredInput;
configuredSteps: StepConfig[];
genome?: Assembly;
initialDataSourceView?: VIEW;
onConfigure: OnConfigure;
onContinue: OnContinue;
onEdit: OnEdit;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export const Main = ({
configuredInput,
configuredSteps,
genome,
initialDataSourceView,
onConfigure,
onContinue,
onEdit,
Expand All @@ -35,6 +36,7 @@ export const Main = ({
configuredInput={configuredInput}
configuredSteps={configuredSteps}
genome={genome}
initialDataSourceView={initialDataSourceView}
onConfigure={onConfigure}
onContinue={onContinue}
onEdit={onEdit}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
OnConfigure,
} from "../../../../../../views/WorkflowInputsView/hooks/UseConfigureInputs/types";
import { StepConfig } from "./components/Stepper/components/Step/types";
import { VIEW } from "./components/Stepper/components/Step/SequencingStep/components/ToggleButtonGroup/types";
import { Assembly } from "../../../../../../views/WorkflowInputsView/types";
import {
OnContinue,
Expand All @@ -15,6 +16,7 @@ export interface Props {
configuredInput: ConfiguredInput;
configuredSteps: StepConfig[];
genome?: Assembly;
initialDataSourceView?: VIEW;
onConfigure: OnConfigure;
onContinue: OnContinue;
onEdit: OnEdit;
Expand Down
Loading
Loading