updateTable component

This commit is contained in:
anovazzi1 2024-06-07 15:07:53 -03:00
commit 384977e156
14 changed files with 185 additions and 119 deletions

View file

@ -115,6 +115,7 @@ function CsvOutputComponent({
style={{ height: "100%", width: "100%" }} style={{ height: "100%", width: "100%" }}
> >
<TableComponent <TableComponent
key={"csv-output"}
rowData={rowData} rowData={rowData}
columnDefs={colDefs} columnDefs={colDefs}
defaultColDef={defaultColDef} defaultColDef={defaultColDef}

View file

@ -23,6 +23,7 @@ function RecordsOutputComponent({
return ( return (
<TableComponent <TableComponent
key={"recordsOutputComponent"}
overlayNoRowsTemplate="No data available" overlayNoRowsTemplate="No data available"
suppressRowClickSelection={true} suppressRowClickSelection={true}
pagination={pagination} pagination={pagination}

View file

@ -0,0 +1,95 @@
import { cn } from "../../../../utils/utils";
import ShadTooltip from "../../../shadTooltipComponent";
import { Button } from "../../../ui/button";
import IconComponent from "../../../genericIconComponent";
export default function TableOptions({
resetGrid,
duplicateRow,
deleteRow,
hasSelection,
stateChange,
}: {
resetGrid: () => void;
duplicateRow?: () => void;
deleteRow?: () => void;
hasSelection: boolean;
stateChange: boolean;
}): JSX.Element {
return (
<div className={cn("absolute bottom-4 left-6")}>
<div className="flex items-center gap-2">
<div>
<ShadTooltip content="Reset Columns">
<Button
variant="none"
size="none"
onClick={() => {
resetGrid();
}}
disabled={!stateChange}
>
<IconComponent
name="RotateCcw"
className={cn("h-5 w-5 text-primary transition-all")}
/>
</Button>
</ShadTooltip>
</div>
{duplicateRow && (
<div>
<ShadTooltip
content={
!hasSelection ? (
<span>Select items to duplicate</span>
) : (
<span>Duplicate selected items</span>
)
}
>
<Button
variant="none"
size="none"
onClick={duplicateRow}
disabled={!hasSelection}
>
<IconComponent
name="Copy"
className={cn("h-5 w-5 text-primary transition-all")}
/>
</Button>
</ShadTooltip>
</div>
)}
{deleteRow && (
<div>
<ShadTooltip
content={
!hasSelection ? (
<span>Select items to delete</span>
) : (
<span>Delete selected items</span>
)
}
>
<Button
variant="none"
size="none"
onClick={deleteRow}
disabled={!hasSelection}
>
<IconComponent
name="Trash2"
className={cn(
"h-5 w-5 text-primary transition-all",
!hasSelection ? "" : "hover:text-destructive",
)}
/>
</Button>
</ShadTooltip>
</div>
)}{" "}
</div>
</div>
);
}

View file

@ -1,7 +1,7 @@
import "ag-grid-community/styles/ag-grid.css"; // Mandatory CSS required by the grid import "ag-grid-community/styles/ag-grid.css"; // Mandatory CSS required by the grid
import "ag-grid-community/styles/ag-theme-quartz.css"; // Optional Theme applied to the grid import "ag-grid-community/styles/ag-theme-quartz.css"; // Optional Theme applied to the grid
import { AgGridReact, AgGridReactProps } from "ag-grid-react"; import { AgGridReact, AgGridReactProps } from "ag-grid-react";
import { ElementRef, forwardRef, useRef } from "react"; import { ElementRef, forwardRef, useRef, useState } from "react";
import { import {
DEFAULT_TABLE_ALERT_MSG, DEFAULT_TABLE_ALERT_MSG,
DEFAULT_TABLE_ALERT_TITLE, DEFAULT_TABLE_ALERT_TITLE,
@ -11,7 +11,7 @@ import "../../style/ag-theme-shadcn.css"; // Custom CSS applied to the grid
import { cn, toTitleCase } from "../../utils/utils"; import { cn, toTitleCase } from "../../utils/utils";
import ForwardedIconComponent from "../genericIconComponent"; import ForwardedIconComponent from "../genericIconComponent";
import { Alert, AlertDescription, AlertTitle } from "../ui/alert"; import { Alert, AlertDescription, AlertTitle } from "../ui/alert";
import ResetColumns from "./components/ResetColumns"; import TableOptions from "./components/TableOptions";
import resetGrid from "./utils/reset-grid-columns"; import resetGrid from "./utils/reset-grid-columns";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
@ -21,8 +21,8 @@ interface TableComponentProps extends AgGridReactProps {
alertTitle?: string; alertTitle?: string;
alertDescription?: string; alertDescription?: string;
editable?: boolean | string[]; editable?: boolean | string[];
onDelete?: (selectedRows: any) => void; onDelete?: () => void;
onDuplicate?: (selectedRows: any) => void; onDuplicate?: () => void;
} }
const TableComponent = forwardRef< const TableComponent = forwardRef<
@ -70,9 +70,12 @@ const TableComponent = forwardRef<
}); });
const gridRef = useRef(null); const gridRef = useRef(null);
// @ts-ignore // @ts-ignore
const realRef = ref?.current ? ref : gridRef; const realRef: React.MutableRefObject<AgGridReact> = ref?.current
? ref
: gridRef;
const dark = useDarkStore((state) => state.dark); const dark = useDarkStore((state) => state.dark);
const initialColumnDefs = useRef(colDef); const initialColumnDefs = useRef(colDef);
const [columnStateChange, setColumnStateChange] = useState(false);
const makeLastColumnNonResizable = (columnDefs) => { const makeLastColumnNonResizable = (columnDefs) => {
columnDefs.forEach((colDef, index) => { columnDefs.forEach((colDef, index) => {
@ -88,6 +91,9 @@ const TableComponent = forwardRef<
params.api.setGridOption("columnDefs", updatedColumnDefs); params.api.setGridOption("columnDefs", updatedColumnDefs);
initialColumnDefs.current = params.api.getColumnDefs(); initialColumnDefs.current = params.api.getColumnDefs();
if (props.onGridReady) props.onGridReady(params); if (props.onGridReady) props.onGridReady(params);
setTimeout(() => {
setColumnStateChange(false);
}, 50);
}; };
const onColumnMoved = (params) => { const onColumnMoved = (params) => {
@ -132,8 +138,27 @@ const TableComponent = forwardRef<
pagination={true} pagination={true}
onGridReady={onGridReady} onGridReady={onGridReady}
onColumnMoved={onColumnMoved} onColumnMoved={onColumnMoved}
onStateUpdated={(e) => {
if (
e.sources.includes("columnVisibility") ||
e.sources.includes("columnOrder")
) {
setColumnStateChange(true);
}
}}
/>
<TableOptions
stateChange={columnStateChange}
hasSelection={realRef.current?.api.getSelectedRows().length > 0}
duplicateRow={props.onDuplicate ? props.onDuplicate : undefined}
deleteRow={props.onDelete ? props.onDelete : undefined}
resetGrid={() => {
resetGrid(realRef, initialColumnDefs);
setTimeout(() => {
setColumnStateChange(false);
}, 100);
}}
/> />
<ResetColumns resetGrid={() => resetGrid(realRef, initialColumnDefs)} />
</div> </div>
); );
}, },

View file

@ -44,43 +44,23 @@ export default function SessionView({ rows }: { rows: Array<any> }) {
} }
return ( return (
<div className="flex h-full w-full flex-col justify-between gap-6"> <TableComponent
<> key={"sessionView"}
<div className="flex w-full items-center justify-end gap-4 space-y-0.5"> onDelete={handleRemoveMessages}
<div className="flex flex-shrink-0 items-center gap-2"> readOnlyEdit
<Button onCellEditRequest={(event) => {
data-testid="api-key-button-store" handleUpdateMessage(event);
variant="primary" }}
className="group px-2" editable={["Sender Name", "Message"]}
disabled={selectedRows.length === 0} overlayNoRowsTemplate="No data available"
onClick={handleRemoveMessages} onSelectionChanged={(event: SelectionChangedEvent) => {
> setSelectedRows(event.api.getSelectedRows().map((row) => row.index));
<ForwardedIconComponent }}
name="Trash2" rowSelection="multiple"
className={cn( suppressRowClickSelection={true}
"h-5 w-5 text-destructive group-disabled:text-primary", pagination={true}
)} columnDefs={columns}
/> rowData={rows}
</Button> />
</div>
</div>
</>
<TableComponent
readOnlyEdit
onCellEditRequest={(event) => {
handleUpdateMessage(event);
}}
editable={["Sender Name", "Message"]}
overlayNoRowsTemplate="No data available"
onSelectionChanged={(event: SelectionChangedEvent) => {
setSelectedRows(event.api.getSelectedRows().map((row) => row.index));
}}
rowSelection="multiple"
suppressRowClickSelection={true}
pagination={true}
columnDefs={columns}
rowData={rows}
/>
</div>
); );
} }

View file

@ -36,25 +36,25 @@ export default function IOModal({
const allNodes = useFlowStore((state) => state.nodes); const allNodes = useFlowStore((state) => state.nodes);
const setMessages = useMessagesStore((state) => state.setMessages); const setMessages = useMessagesStore((state) => state.setMessages);
const inputs = useFlowStore((state) => state.inputs).filter( const inputs = useFlowStore((state) => state.inputs).filter(
(input) => input.type !== "ChatInput" (input) => input.type !== "ChatInput",
); );
const chatInput = useFlowStore((state) => state.inputs).find( const chatInput = useFlowStore((state) => state.inputs).find(
(input) => input.type === "ChatInput" (input) => input.type === "ChatInput",
); );
const outputs = useFlowStore((state) => state.outputs).filter( const outputs = useFlowStore((state) => state.outputs).filter(
(output) => output.type !== "ChatOutput" (output) => output.type !== "ChatOutput",
); );
const chatOutput = useFlowStore((state) => state.outputs).find( const chatOutput = useFlowStore((state) => state.outputs).find(
(output) => output.type === "ChatOutput" (output) => output.type === "ChatOutput",
); );
const nodes = useFlowStore((state) => state.nodes).filter( const nodes = useFlowStore((state) => state.nodes).filter(
(node) => (node) =>
inputs.some((input) => input.id === node.id) || inputs.some((input) => input.id === node.id) ||
outputs.some((output) => output.id === node.id) outputs.some((output) => output.id === node.id),
); );
const haveChat = chatInput || chatOutput; const haveChat = chatInput || chatOutput;
const [selectedTab, setSelectedTab] = useState( const [selectedTab, setSelectedTab] = useState(
inputs.length > 0 ? 1 : outputs.length > 0 ? 2 : 0 inputs.length > 0 ? 1 : outputs.length > 0 ? 2 : 0,
); );
const setErrorData = useAlertStore((state) => state.setErrorData); const setErrorData = useAlertStore((state) => state.setErrorData);
const setSuccessData = useAlertStore((state) => state.setSuccessData); const setSuccessData = useAlertStore((state) => state.setSuccessData);
@ -111,6 +111,9 @@ export default function IOModal({
setLockChat(false); setLockChat(false);
}); });
} }
const { rows, columns } = await getMessagesTable("union", currentFlow!.id);
setMessages(rows);
setColumns(columns);
setLockChat(false); setLockChat(false);
if (chatInput) { if (chatInput) {
setNode(chatInput.id, (node: NodeType) => { setNode(chatInput.id, (node: NodeType) => {
@ -124,7 +127,7 @@ export default function IOModal({
const { handleRemoveSession } = useRemoveSession( const { handleRemoveSession } = useRemoveSession(
setSuccessData, setSuccessData,
setErrorData setErrorData,
); );
useEffect(() => { useEffect(() => {
@ -185,7 +188,7 @@ export default function IOModal({
<div className="flex-max-width h-full"> <div className="flex-max-width h-full">
<div <div
className={cn( className={cn(
"mr-6 flex h-full w-2/6 flex-shrink-0 flex-col justify-start transition-all duration-300" "mr-6 flex h-full w-2/6 flex-shrink-0 flex-col justify-start transition-all duration-300",
)} )}
> >
<Tabs <Tabs
@ -212,11 +215,11 @@ export default function IOModal({
<TabsContent value={"1"} className="api-modal-tabs-content"> <TabsContent value={"1"} className="api-modal-tabs-content">
{nodes {nodes
.filter((node) => .filter((node) =>
inputs.some((input) => input.id === node.id) inputs.some((input) => input.id === node.id),
) )
.map((node, index) => { .map((node, index) => {
const input = inputs.find( const input = inputs.find(
(input) => input.id === node.id (input) => input.id === node.id,
)!; )!;
return ( return (
<div <div
@ -277,11 +280,11 @@ export default function IOModal({
<TabsContent value={"2"} className="api-modal-tabs-content"> <TabsContent value={"2"} className="api-modal-tabs-content">
{nodes {nodes
.filter((node) => .filter((node) =>
outputs.some((output) => output.id === node.id) outputs.some((output) => output.id === node.id),
) )
.map((node, index) => { .map((node, index) => {
const output = outputs.find( const output = outputs.find(
(output) => output.id === node.id (output) => output.id === node.id,
)!; )!;
return ( return (
<div <div
@ -381,7 +384,7 @@ export default function IOModal({
content={ content={
flow_sessions.some( flow_sessions.some(
(f_session) => (f_session) =>
f_session?.session_id === session f_session?.session_id === session,
) )
? "Active Session" ? "Active Session"
: "Inactive Session" : "Inactive Session"
@ -392,10 +395,10 @@ export default function IOModal({
"h-2 w-2 rounded-full", "h-2 w-2 rounded-full",
flow_sessions.some( flow_sessions.some(
(f_session) => (f_session) =>
f_session?.session_id === session f_session?.session_id === session,
) )
? "bg-status-green" ? "bg-status-green"
: "bg-slate-500" : "bg-slate-500",
)} )}
></div> ></div>
</ShadTooltip> </ShadTooltip>
@ -413,7 +416,7 @@ export default function IOModal({
<div <div
className={cn( className={cn(
"flex h-full w-full flex-col items-start gap-4 pt-4", "flex h-full w-full flex-col items-start gap-4 pt-4",
!selectedViewField ? "hidden" : "" !selectedViewField ? "hidden" : "",
)} )}
> >
<div className="font-xl flex items-center justify-center gap-3 font-semibold"> <div className="font-xl flex items-center justify-center gap-3 font-semibold">
@ -432,7 +435,7 @@ export default function IOModal({
</div> </div>
<div className="h-full w-full"> <div className="h-full w-full">
{inputs.some( {inputs.some(
(input) => input.id === selectedViewField.id (input) => input.id === selectedViewField.id,
) && ( ) && (
<IOFieldView <IOFieldView
type={InputOutput.INPUT} type={InputOutput.INPUT}
@ -442,7 +445,7 @@ export default function IOModal({
/> />
)} )}
{outputs.some( {outputs.some(
(output) => output.id === selectedViewField.id (output) => output.id === selectedViewField.id,
) && ( ) && (
<IOFieldView <IOFieldView
type={InputOutput.OUTPUT} type={InputOutput.OUTPUT}
@ -452,12 +455,12 @@ export default function IOModal({
/> />
)} )}
{sessions.some( {sessions.some(
(session) => session === selectedViewField.id (session) => session === selectedViewField.id,
) && ( ) && (
<SessionView <SessionView
rows={messages.filter( rows={messages.filter(
(message) => (message) =>
message.session_id === selectedViewField.id message.session_id === selectedViewField.id,
)} )}
/> />
)} )}
@ -467,7 +470,7 @@ export default function IOModal({
<div <div
className={cn( className={cn(
"flex h-full w-full", "flex h-full w-full",
selectedViewField ? "hidden" : "" selectedViewField ? "hidden" : "",
)} )}
> >
{haveChat ? ( {haveChat ? (
@ -499,7 +502,7 @@ export default function IOModal({
"h-4 w-4", "h-4 w-4",
isBuilding isBuilding
? "animate-spin" ? "animate-spin"
: "fill-current text-medium-indigo" : "fill-current text-medium-indigo",
)} )}
/> />
), ),

View file

@ -22,7 +22,7 @@ const EditNodeModal = forwardRef(
setOpen: (open: boolean) => void; setOpen: (open: boolean) => void;
data: NodeDataType; data: NodeDataType;
}, },
ref ref,
) => { ) => {
const myData = useRef(data); const myData = useRef(data);
@ -43,7 +43,7 @@ const EditNodeModal = forwardRef(
data, data,
handleOnNewValue, handleOnNewValue,
changeAdvanced, changeAdvanced,
open open,
); );
const [gridApi, setGridApi] = useState<GridApi | null>(null); const [gridApi, setGridApi] = useState<GridApi | null>(null);
@ -92,6 +92,7 @@ const EditNodeModal = forwardRef(
<div className="h-full"> <div className="h-full">
{nodeLength > 0 && ( {nodeLength > 0 && (
<TableComponent <TableComponent
key={"editNode"}
onGridReady={(params) => { onGridReady={(params) => {
setGridApi(params.api); setGridApi(params.api);
}} }}
@ -107,7 +108,7 @@ const EditNodeModal = forwardRef(
<BaseModal.Footer submit={{ label: "Save Changes" }} /> <BaseModal.Footer submit={{ label: "Save Changes" }} />
</BaseModal> </BaseModal>
); );
} },
); );
export default EditNodeModal; export default EditNodeModal;

View file

@ -86,6 +86,7 @@ export default function FlowLogsModal({
</TabsList> </TabsList>
</Tabs> </Tabs>
<TableComponent <TableComponent
key={activeTab}
readOnlyEdit readOnlyEdit
className="h-max-full h-full w-full" className="h-max-full h-full w-full"
pagination={rows.length === 0 ? false : true} pagination={rows.length === 0 ? false : true}

View file

@ -6,13 +6,11 @@ import { cn } from "../../../../../../utils/utils";
type ApiKeyHeaderComponentProps = { type ApiKeyHeaderComponentProps = {
selectedRows: string[]; selectedRows: string[];
handleDeleteKey: () => void;
fetchApiKeys: () => void; fetchApiKeys: () => void;
userId: string; userId: string;
}; };
const ApiKeyHeaderComponent = ({ const ApiKeyHeaderComponent = ({
selectedRows, selectedRows,
handleDeleteKey,
fetchApiKeys, fetchApiKeys,
userId, userId,
}: ApiKeyHeaderComponentProps) => { }: ApiKeyHeaderComponentProps) => {
@ -30,20 +28,6 @@ const ApiKeyHeaderComponent = ({
<p className="text-sm text-muted-foreground">{API_PAGE_PARAGRAPH}</p> <p className="text-sm text-muted-foreground">{API_PAGE_PARAGRAPH}</p>
</div> </div>
<div className="flex flex-shrink-0 items-center gap-2"> <div className="flex flex-shrink-0 items-center gap-2">
<Button
data-testid="api-key-button-store"
variant="primary"
className="group px-2"
disabled={selectedRows.length === 0}
onClick={handleDeleteKey}
>
<ForwardedIconComponent
name="Trash2"
className={cn(
"h-5 w-5 text-destructive group-disabled:text-primary",
)}
/>
</Button>
<SecretKeyModal data={userId} onCloseModal={fetchApiKeys}> <SecretKeyModal data={userId} onCloseModal={fetchApiKeys}>
<Button data-testid="api-key-button-store" variant="primary"> <Button data-testid="api-key-button-store" variant="primary">
<ForwardedIconComponent name="Plus" className="mr-2 w-4" /> <ForwardedIconComponent name="Plus" className="mr-2 w-4" />

View file

@ -25,7 +25,7 @@ export default function ApiKeysPage() {
userData, userData,
setLoadingKeys, setLoadingKeys,
keysList, keysList,
setUserId setUserId,
); );
function resetFilter() { function resetFilter() {
@ -36,7 +36,7 @@ export default function ApiKeysPage() {
selectedRows, selectedRows,
resetFilter, resetFilter,
setSuccessData, setSuccessData,
setErrorData setErrorData,
); );
const columnDefs = getColumnDefs(); const columnDefs = getColumnDefs();
@ -45,13 +45,14 @@ export default function ApiKeysPage() {
<div className="flex h-full w-full flex-col justify-between gap-6"> <div className="flex h-full w-full flex-col justify-between gap-6">
<ApiKeyHeaderComponent <ApiKeyHeaderComponent
selectedRows={selectedRows} selectedRows={selectedRows}
handleDeleteKey={handleDeleteKey}
fetchApiKeys={fetchApiKeys} fetchApiKeys={fetchApiKeys}
userId={userId} userId={userId}
/> />
<div className="flex h-full w-full flex-col justify-between"> <div className="flex h-full w-full flex-col justify-between">
<TableComponent <TableComponent
key={"apiKeys"}
onDelete={handleDeleteKey}
overlayNoRowsTemplate="No data available" overlayNoRowsTemplate="No data available"
onSelectionChanged={(event: SelectionChangedEvent) => { onSelectionChanged={(event: SelectionChangedEvent) => {
setSelectedRows(event.api.getSelectedRows().map((row) => row.id)); setSelectedRows(event.api.getSelectedRows().map((row) => row.id));

View file

@ -143,20 +143,6 @@ export default function GlobalVariablesPage() {
</p> </p>
</div> </div>
<div className="flex flex-shrink-0 items-center gap-2"> <div className="flex flex-shrink-0 items-center gap-2">
<Button
data-testid="api-key-button-store"
variant="primary"
className="group px-2"
disabled={selectedRows.length === 0}
onClick={removeVariables}
>
<IconComponent
name="Trash2"
className={cn(
"h-5 w-5 text-destructive group-disabled:text-primary",
)}
/>
</Button>
<AddNewVariableButton> <AddNewVariableButton>
<Button data-testid="api-key-button-store" variant="primary"> <Button data-testid="api-key-button-store" variant="primary">
<IconComponent name="Plus" className="mr-2 w-4" /> <IconComponent name="Plus" className="mr-2 w-4" />
@ -168,6 +154,7 @@ export default function GlobalVariablesPage() {
<div className="flex h-full w-full flex-col justify-between"> <div className="flex h-full w-full flex-col justify-between">
<TableComponent <TableComponent
key={"globalVariables"}
overlayNoRowsTemplate="No data available" overlayNoRowsTemplate="No data available"
onSelectionChanged={(event: SelectionChangedEvent) => { onSelectionChanged={(event: SelectionChangedEvent) => {
setSelectedRows(event.api.getSelectedRows().map((row) => row.name)); setSelectedRows(event.api.getSelectedRows().map((row) => row.name));
@ -177,6 +164,7 @@ export default function GlobalVariablesPage() {
pagination={true} pagination={true}
columnDefs={colDefs} columnDefs={colDefs}
rowData={rowData} rowData={rowData}
onDelete={removeVariables}
/> />
</div> </div>
</div> </div>

View file

@ -113,6 +113,7 @@ export default function ShortcutsPage() {
</div> </div>
<div className="flex h-full w-full flex-col justify-between"> <div className="flex h-full w-full flex-col justify-between">
<TableComponent <TableComponent
key={"shortcuts"}
pagination={false} pagination={false}
columnDefs={colDefs} columnDefs={colDefs}
rowData={nodesRowData} rowData={nodesRowData}

View file

@ -8,7 +8,6 @@ type HeaderMessagesComponentProps = {
}; };
const HeaderMessagesComponent = ({ const HeaderMessagesComponent = ({
selectedRows, selectedRows,
handleRemoveMessages,
}: HeaderMessagesComponentProps) => { }: HeaderMessagesComponentProps) => {
return ( return (
<> <>
@ -26,22 +25,6 @@ const HeaderMessagesComponent = ({
behaviors. behaviors.
</p> </p>
</div> </div>
<div className="flex flex-shrink-0 items-center gap-2">
<Button
data-testid="api-key-button-store"
variant="primary"
className="group px-2"
disabled={selectedRows.length === 0}
onClick={handleRemoveMessages}
>
<ForwardedIconComponent
name="Trash2"
className={cn(
"h-5 w-5 text-destructive group-disabled:text-primary",
)}
/>
</Button>
</div>
</div> </div>
</> </>
); );

View file

@ -53,6 +53,8 @@ export default function MessagesPage() {
<div className="flex h-full w-full flex-col justify-between"> <div className="flex h-full w-full flex-col justify-between">
<TableComponent <TableComponent
key={"sessionView"}
onDelete={handleRemoveMessages}
readOnlyEdit readOnlyEdit
onCellEditRequest={(event) => { onCellEditRequest={(event) => {
handleUpdateMessage(event); handleUpdateMessage(event);