fix: Enhance Growth UI Layout and Consistency (#7768)

This commit is contained in:
Cristhian Zanforlin Lousa 2025-04-23 19:17:22 -03:00 committed by GitHub
commit 60de34074b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
32 changed files with 260 additions and 323 deletions

View file

@ -1,51 +0,0 @@
---
description:
globs:
alwaysApply: true
---
# Concise Task Management Protocol - Sequential Mode
## File Creation
- Filename: `YYYYMMDD_HHMMSS_task_name.md`
- IMPORTANT: Always include a descriptive task name after the timestamp (e.g., `20250404_135621_api_integration.md`)
- Never create files with timestamp only
- Store in `tasks/` directory
## Task Structure
```
# Task: [Task Name]
**Status**: [Not Started | In Progress | Completed]
## Analysis
- [ ] Requirements
- [ ] Subtask 1
- [ ] Challenges
- [ ] Dependencies
## Plan
- [ ] Step 1: [Description]
- [ ] Subtask 1.1
- [ ] Step 2: [Description]
## Execution
- [ ] Implementation 1
- [ ] Details
- [ ] Implementation 2
- [ ] Files modified: [files]
## Summary
- [ ] Files modified: `filename.ext` (lines X-Y)
- [ ] Dependencies added/changed
- [ ] Edge cases considered
- [ ] Known limitations
- [ ] Future impact points
```
## Execution Rules
- Execute one subtask at a time in sequence
- Update the task file after EACH subtask is completed
- Mark completed subtasks with [x] as they are finished
- Update main task status throughout execution
- Document work incrementally, not all at once
- Never proceed to the next section until all subtasks in the current section are completed and marked

View file

@ -53,7 +53,7 @@ const AlertDropdown = forwardRef<HTMLDivElement, AlertDropdownType>(
Notifications
<div className="flex gap-3 pr-3">
<button
className="text-foreground hover:text-status-red"
className="text-muted-foreground hover:text-status-red"
onClick={() => {
setOpen(false);
setTimeout(clearNotificationList, 100);

View file

@ -35,6 +35,7 @@ export default function PageLayout({
onClick={() => {
navigate(backTo);
}}
data-testid="back_page_button"
>
<ForwardedIconComponent
name="ChevronLeft"

View file

@ -13,7 +13,7 @@ export default function RenderIcons({
<span
className={cn(
"flex items-center gap-0.5",
tableRender ? "justify-start" : "justify-center text-[12px]",
tableRender ? "justify-start" : "justify-center text-xs",
)}
>
{shortcutList.map((key, index) => (

View file

@ -5,7 +5,7 @@ import { Tooltip, TooltipContent, TooltipTrigger } from "../../ui/tooltip";
// Extract static styles
const BASE_TOOLTIP_CLASSES =
"z-[99] max-w-96 bg-tooltip text-[12px] text-tooltip-foreground";
"z-[99] max-w-96 bg-tooltip text-xs text-tooltip-foreground";
// Memoize the tooltip content component
const MemoizedTooltipContent = memo(

View file

@ -2,7 +2,6 @@ import { usePostLikeComponent } from "@/controllers/API/queries/store";
import { useState } from "react";
import { getComponent } from "../../../controllers/API";
import useAlertStore from "../../../stores/alertStore";
import useFlowsManagerStore from "../../../stores/flowsManagerStore";
import { useStoreStore } from "../../../stores/storeStore";
import { FlowType } from "../../../types/flow";
import { storeComponent } from "../../../types/store";
@ -42,11 +41,6 @@ export default function StoreCardComponent({
const [downloadsCount, setDownloadsCount] = useState(
data?.downloads_count ?? 0,
);
const setCurrentFlow = useFlowsManagerStore((state) => state.setCurrentFlow);
// const [openPlayground, setOpenPlayground] = useState(false);
const [loadingPlayground, setLoadingPlayground] = useState(false);
const playground =
data.last_tested_version?.includes("1.0.0") && !data.is_component;
const name = data.is_component ? "Component" : "Flow";

View file

@ -1,11 +1,13 @@
import { ForwardedIconComponent } from "@/components/common/genericIconComponent";
import {
DATASTAX_DOCS_URL,
DISCORD_URL,
DOCS_URL,
GITHUB_URL,
TWITTER_URL,
} from "@/constants/constants";
import { useLogout } from "@/controllers/API/queries/auth";
import { ENABLE_DATASTAX_LANGFLOW } from "@/customization/feature-flags";
import { useCustomNavigate } from "@/customization/hooks/use-custom-navigate";
import useAuthStore from "@/stores/authStore";
import { useDarkStore } from "@/stores/darkStore";
@ -45,7 +47,7 @@ export const AccountMenu = () => {
<HeaderMenu>
<HeaderMenuToggle>
<div
className="h-7 w-7 rounded-lg focus-visible:outline-0"
className="h-6 w-6 rounded-lg focus-visible:outline-0"
data-testid="user-profile-settings"
>
<ProfileIcon />
@ -107,7 +109,10 @@ export const AccountMenu = () => {
</HeaderMenuItemButton>
</div>
)}
<HeaderMenuItemLink newPage href={DOCS_URL}>
<HeaderMenuItemLink
newPage
href={ENABLE_DATASTAX_LANGFLOW ? DATASTAX_DOCS_URL : DOCS_URL}
>
<span data-testid="menu_docs_button" id="menu_docs_button">
Docs
</span>

View file

@ -189,7 +189,7 @@ export const MenuBar = ({}: {}): JSX.Element => {
[currentFlowName],
);
const handleNameSubmit = useCallback(() => {
const handleNameSubmit = useCallback(async () => {
if (
flowName.trim() !== "" &&
flowName !== currentFlowName &&
@ -203,9 +203,10 @@ export const MenuBar = ({}: {}): JSX.Element => {
name: flowName,
id: currentFlowId!,
};
setCurrentFlow(newFlow);
saveFlow(newFlow)
.then(() => {
setCurrentFlow(newFlow);
setSuccessData({ title: "Flow name updated successfully" });
})
.catch((error) => {
@ -268,7 +269,7 @@ export const MenuBar = ({}: {}): JSX.Element => {
{currentFolder?.name && (
<div className="hidden truncate md:flex">
<div
className="cursor-pointer truncate pr-1 text-muted-foreground hover:text-primary"
className="cursor-pointer truncate pr-1 text-xs text-muted-foreground hover:text-primary"
onClick={() => {
navigate(
currentFolder?.id
@ -313,7 +314,7 @@ export const MenuBar = ({}: {}): JSX.Element => {
>
<Input
className={cn(
"h-6 w-full shrink-0 cursor-text font-semibold",
"h-6 w-full shrink-0 cursor-text text-xs font-semibold",
"bg-transparent pl-1 pr-0 transition-colors duration-200",
"border-0 outline-none focus:border-0 focus:outline-none focus:ring-0 focus:ring-offset-0",
!editingName && "text-primary hover:opacity-80",
@ -334,7 +335,7 @@ export const MenuBar = ({}: {}): JSX.Element => {
/>
<span
ref={measureRef}
className="invisible absolute left-0 top-0 -z-10 w-fit whitespace-pre font-semibold"
className="invisible absolute left-0 top-0 -z-10 w-fit whitespace-pre text-xs font-semibold"
aria-hidden="true"
data-testid="flow_name"
>

View file

@ -16,13 +16,15 @@ export const HeaderMenu = ({ children }) => (
export const HeaderMenuToggle = ({ children }) => (
<DropdownMenuTrigger
className="group inline-flex w-full items-center justify-center gap-1 rounded-md pr-0"
className="inline-flex w-full items-center justify-center rounded-md pl-4 pr-1"
data-testid="user_menu_button"
id="user_menu_button"
>
<div className="flex items-center gap-1 rounded-lg px-2 py-1.5 group-hover:bg-muted">
{children}
<ChevronsUpDown className="h-4 w-4 text-muted-foreground group-hover:text-foreground" />
<div className="group flex items-center self-center rounded-md">
<div className="flex h-6 w-10 items-center justify-center rounded-full bg-background transition-colors hover:bg-muted group-hover:bg-muted">
<div className="relative right-1 z-10">{children}</div>
<ChevronsUpDown className="relative h-[14px] w-[14px] text-muted-foreground group-hover:text-primary" />
</div>
</div>
</DropdownMenuTrigger>
);

View file

@ -12,7 +12,7 @@ export function ProfileIcon() {
return (
<img
src={profileImageUrl}
className="h-7 w-7 shrink-0 focus-visible:outline-0"
className="h-6 w-6 shrink-0 focus-visible:outline-0"
/>
);
}

View file

@ -8,13 +8,13 @@ export const LangflowCounts = () => {
const discordCount: number = useDarkStore((state) => state.discordCount);
return (
<div className="flex items-center gap-5">
<div className="flex items-center gap-3">
<ShadTooltip
content="Go to GitHub repo"
side="bottom"
styleClasses="z-10"
>
<div className="flex items-center gap-2 text-muted-foreground hover:text-foreground">
<div className="hit-area-hover flex items-center gap-2 rounded-md p-1 text-muted-foreground">
<FaGithub className="h-4 w-4" />
<span className="text-xs font-semibold">{formatNumber(stars)}</span>
</div>
@ -25,7 +25,7 @@ export const LangflowCounts = () => {
side="bottom"
styleClasses="z-10"
>
<div className="flex items-center gap-2 text-muted-foreground hover:text-foreground">
<div className="hit-area-hover flex items-center gap-2 rounded-md p-1 text-muted-foreground">
<FaDiscord className="h-4 w-4" />
<span className="text-xs font-semibold">
{formatNumber(discordCount)}

View file

@ -54,9 +54,16 @@ export default function AppHeader(): JSX.Element {
const isEmpty = flows?.length !== examples?.length || folders?.length > 1;
const getNotificationBadge = () => {
const baseClasses = "absolute h-1 w-1 rounded-full bg-destructive";
return notificationCenter
? `${baseClasses} right-[5.1rem] top-[10px]`
: "hidden";
};
return (
<div
className={`flex h-[44px] w-full items-center justify-between border-b p-6 dark:bg-background ${
className={`flex h-[48px] w-full items-center justify-between border-b px-6 dark:bg-background ${
!isEmpty ? "hidden" : ""
}`}
data-testid="app-header"
@ -75,7 +82,7 @@ export default function AppHeader(): JSX.Element {
{ENABLE_DATASTAX_LANGFLOW ? (
<DataStaxLogo className="fill-black dark:fill-[white]" />
) : (
<LangflowLogo className="h-5 w-6" />
<LangflowLogo className="h-6 w-6" />
)}
</Button>
{ENABLE_DATASTAX_LANGFLOW && (
@ -93,22 +100,20 @@ export default function AppHeader(): JSX.Element {
{/* Right Section */}
<div
className={`relative left-3 z-30 flex items-center gap-2`}
className={`relative left-3 z-30 flex items-center gap-1`}
data-testid="header_right_section_wrapper"
>
{!ENABLE_DATASTAX_LANGFLOW && (
<>
<Button
unstyled
className="hidden items-center whitespace-nowrap pr-2 2xl:inline"
onClick={() =>
window.open("https://github.com/langflow-ai/langflow", "_blank")
}
>
<LangflowCounts />
</Button>
</>
)}
<>
<Button
unstyled
className="hidden items-center whitespace-nowrap pr-2 lg:inline"
onClick={() =>
window.open("https://github.com/langflow-ai/langflow", "_blank")
}
>
<LangflowCounts />
</Button>
</>
<AlertDropdown
notificationRef={notificationContentRef}
onClose={() => setActiveState(null)}
@ -129,19 +134,21 @@ export default function AppHeader(): JSX.Element {
}
data-testid="notification_button"
>
<span
className={
notificationCenter
? `absolute right-[5.3rem] top-[10px] h-1 w-1 rounded-full bg-destructive`
: "hidden"
}
/>
<ForwardedIconComponent
name="Bell"
className="side-bar-button-size ml-1 h-4 w-4 text-muted-foreground"
strokeWidth={2}
/>
<span className="hidden whitespace-nowrap">Notifications</span>
<div className="hit-area-hover group items-center rounded-md px-2 py-1 text-muted-foreground">
<span className={getNotificationBadge()} />
<ForwardedIconComponent
name="Bell"
className={`side-bar-button-size h-4 w-4 ${
activeState === "notifications"
? "text-primary"
: "text-muted-foreground group-hover:text-primary"
}`}
strokeWidth={2}
/>
<span className="hidden whitespace-nowrap">
Notifications
</span>
</div>
</Button>
</AlertDropdown>
</ShadTooltip>
@ -150,50 +157,7 @@ export default function AppHeader(): JSX.Element {
orientation="vertical"
className="my-auto ml-3 h-7 dark:border-zinc-700"
/>
{ENABLE_DATASTAX_LANGFLOW && (
<>
<ShadTooltip content="Docs" side="bottom" styleClasses="z-10">
<Button
variant="ghost"
className="flex text-sm font-medium"
onClick={() =>
window.open(
"https://docs.datastax.com/en/langflow/index.html",
"_blank",
)
}
>
<ForwardedIconComponent
name="book-open-text"
className="side-bar-button-size h-[18px] w-[18px]"
/>
<span className="hidden whitespace-nowrap 2xl:inline">
Docs
</span>
</Button>
</ShadTooltip>
<ShadTooltip content="Settings" side="bottom" styleClasses="z-10">
<Button
data-testid="user-profile-settings"
variant="ghost"
className="flex text-sm font-medium"
onClick={() => navigate("/settings")}
>
<ForwardedIconComponent
name="Settings"
className="side-bar-button-size h-[18px] w-[18px]"
/>
<span className="hidden whitespace-nowrap 2xl:inline">
Settings
</span>
</Button>
</ShadTooltip>
<Separator
orientation="vertical"
className="my-auto h-7 dark:border-zinc-700"
/>
</>
)}
<div className="flex">
<AccountMenu />
</div>

View file

@ -82,9 +82,17 @@ export const GetStartedProgress: FC<{
};
return (
<div className="h-[180px] w-full">
<div className="mt-3 h-[180px] w-full">
<div className="mb-2 flex items-center justify-between">
<span className="font-[14px]">Get started</span>
<span className="text-sm font-semibold">
{percentageGetStarted >= 100 ? (
<>
<span>All Set</span> <span className="pl-1"> 🎉 </span>
</>
) : (
"Get started"
)}
</span>
<button
onClick={() => handleUserTrack("dialog_dismissed")}
className="text-muted-foreground hover:text-foreground"
@ -94,7 +102,7 @@ export const GetStartedProgress: FC<{
</button>
</div>
<div className="mb-1 flex items-center justify-between gap-3">
<div className="mb-1 mt-2 flex items-center justify-between gap-3">
<div className="h-1 w-full rounded-full bg-muted">
<div
className="h-1 w-[33%] rounded-full bg-accent-pink-foreground"
@ -143,7 +151,7 @@ export const GetStartedProgress: FC<{
)}
<span
className={cn(
"text-sm",
"text-xs",
isGithubStarredChild && "text-muted-foreground line-through",
)}
>
@ -185,7 +193,7 @@ export const GetStartedProgress: FC<{
)}
<span
className={cn(
"text-sm",
"text-xs",
isDiscordJoinedChild && "text-muted-foreground line-through",
)}
>
@ -215,7 +223,7 @@ export const GetStartedProgress: FC<{
)}
/>
</span>
<span className={cn("text-sm", hasFlows && "line-through")}>
<span className={cn("text-xs", hasFlows && "line-through")}>
Create a flow
</span>
</div>

View file

@ -41,7 +41,7 @@ export const HeaderButtons = ({
handleDismissDialog={handleDismissDialog}
/>
<div className="-mx-4 mt-4 w-[280px]">
<div className="-mx-4 mt-1 w-[280px]">
<hr className="border-t-1 w-full" />
</div>
</>

View file

@ -18,8 +18,10 @@ import {
import { useGetDownloadFolders } from "@/controllers/API/queries/folders/use-get-download-folders";
import {
ENABLE_CUSTOM_PARAM,
ENABLE_DATASTAX_LANGFLOW,
ENABLE_FILE_MANAGEMENT,
} from "@/customization/feature-flags";
import { useCustomNavigate } from "@/customization/hooks/use-custom-navigate";
import { track } from "@/customization/utils/analytics";
import { createFileUpload } from "@/helpers/create-file-upload";
import { getObjectsFromFilelist } from "@/helpers/get-objects-from-filelist";
@ -56,6 +58,8 @@ const SideBarFoldersButtonsComponent = ({
const loading = !folders;
const refInput = useRef<HTMLInputElement>(null);
const navigate = useCustomNavigate();
const currentFolder = pathname.split("/");
const urlWithoutPath =
pathname.split("/").length < (ENABLE_CUSTOM_PARAM ? 5 : 4);
@ -348,7 +352,7 @@ const SideBarFoldersButtonsComponent = ({
collapsible={isMobile ? "offcanvas" : "none"}
data-testid="folder-sidebar"
>
<SidebarHeader className="p-4">
<SidebarHeader className="px-4 py-1">
<HeaderButtons
handleUploadFlowsToFolder={handleUploadFlowsToFolder}
isUpdatingFolder={isUpdatingFolder}
@ -447,7 +451,24 @@ const SideBarFoldersButtonsComponent = ({
</SidebarContent>
{ENABLE_FILE_MANAGEMENT && (
<SidebarFooter className="border-t">
<div className="flex w-full items-center gap-2 p-2">
<div className="grid w-full items-center gap-2 p-2">
{!ENABLE_DATASTAX_LANGFLOW && (
<div
className="flex w-full items-center"
data-testid="button-store"
>
<SidebarMenuButton
size="md"
className="text-[13px]"
onClick={() => {
window.open("/store", "_blank");
}}
>
<ForwardedIconComponent name="Store" />
Store
</SidebarMenuButton>
</div>
)}
<SidebarMenuButton
isActive={checkPathFiles}
onClick={() => handleFilesClick?.()}

View file

@ -1073,5 +1073,7 @@ export const DISCORD_URL = "https://discord.com/invite/EqksyE2EX9";
export const GITHUB_URL = "https://github.com/langflow-ai/langflow";
export const TWITTER_URL = "https://x.com/langflow_ai";
export const DOCS_URL = "https://docs.langflow.org";
export const DATASTAX_DOCS_URL =
"https://docs.datastax.com/en/langflow/index.html";
export const UUID_PARSING_ERROR = "uuid_parsing";

View file

@ -62,7 +62,7 @@ const ButtonSendWrapper = ({
<Case condition={showStopButton}>
<div className="flex items-center gap-2 rounded-md text-[14px] font-medium">
Stop
<Loading className="h-[16px] w-[16px]" />
<Loading className="h-4 w-4" />
</div>
</Case>

View file

@ -40,7 +40,7 @@ const NoInputView: React.FC<NoInputViewProps> = ({
>
<div className="flex items-center gap-2 rounded-md text-[14px] font-medium">
Stop
<Loading className="h-[16px] w-[16px]" />
<Loading className="h-4 w-4" />
</div>
</Button>
)}

View file

@ -18,7 +18,7 @@ export default function ShortcutDisplay({
{display_name && <span> {display_name} </span>}
<span
className={cn(
"flex h-[16px] w-[16px] items-center justify-center rounded-sm bg-muted text-muted-foreground",
"flex h-4 w-4 items-center justify-center rounded-sm bg-muted text-muted-foreground",
display_name && "ml-3",
)}
>
@ -26,7 +26,7 @@ export default function ShortcutDisplay({
</span>
</div>
) : (
<div className="flex content-center items-center justify-center self-center text-[12px]">
<div className="flex content-center items-center justify-center self-center text-xs">
<span> {display_name} </span>
<span
className={`ml-3 flex items-center rounded-sm bg-primary-hover px-1.5 py-[0.1em] text-muted`}

View file

@ -15,15 +15,9 @@ export default function ToolbarSelectItem({
<div className={`flex ${style}`} data-testid={dataTestId}>
<ForwardedIconComponent
name={icon}
className={`mr-2 ${
icon === "Share3"
? "absolute left-2 top-[0.25em] h-6 w-6"
: "mt-[0.15em] h-4 w-4"
} ${ping && "animate-pulse text-green-500"}`}
className={`mr-2 mt-[0.15em] h-4 w-4 ${ping && "animate-pulse text-green-500"}`}
/>
<span className={`${icon === "Share3" ? "ml-[1.8em]" : " "}`}>
{value}
</span>
<span>{value}</span>
<span
className={`absolute right-2 top-[0.43em] flex items-center rounded-sm bg-muted px-1.5 py-[0.1em] text-muted-foreground`}
>

View file

@ -143,7 +143,7 @@ const GridComponent = ({ flowData }: { flowData: FlowType }) => {
</div>
</div>
<div className="line-clamp-2 h-full pt-5 text-sm text-primary">
<div className="line-clamp-2 h-full pt-3 text-sm text-primary">
{flowData.description}
</div>
</Card>

View file

@ -103,7 +103,7 @@ const HeaderComponent = ({
value={debouncedSearch}
onChange={handleSearch}
/>
<div className="relative mr-2 flex rounded-lg border border-muted bg-muted">
<div className="relative top-[3px] mr-2 flex h-fit rounded-lg border border-muted bg-muted">
{/* Sliding Indicator */}
<div
className={`absolute top-[3px] h-[33px] w-8 transform rounded-lg bg-background shadow-md transition-transform duration-300 ${
@ -129,7 +129,7 @@ const HeaderComponent = ({
<ForwardedIconComponent
name={viewType === "list" ? "Menu" : "LayoutGrid"}
aria-hidden="true"
className="h-4 w-4 group-hover:text-foreground"
className="relative bottom-[1px] h-4 w-4 group-hover:text-foreground"
/>
</Button>
))}

View file

@ -13,7 +13,6 @@ import { useFolderStore } from "@/stores/foldersStore";
import { formatNumber } from "@/utils/utils";
import { ExternalLink } from "lucide-react";
import { FaDiscord, FaGithub } from "react-icons/fa";
import { HiArrowRight } from "react-icons/hi";
import { useShallow } from "zustand/react/shallow";
import useFileDrop from "../hooks/use-on-file-drop";
@ -68,14 +67,14 @@ export const EmptyPageCommunity = ({
onFileDrop={handleFileDrop}
>
<div className="m-0 h-full w-full bg-background p-0">
<div className="z-50 flex h-full w-full flex-col items-center justify-center gap-6">
<div className="z-50 flex flex-col items-center gap-3">
<div className="z-50 flex h-full w-full flex-col items-center justify-center gap-5">
<div className="z-50 flex flex-col items-center gap-2">
<div className="z-50 dark:hidden">
<img
src={logoLightPng}
alt="Langflow Logo Light"
data-testid="empty_page_logo_light"
className=""
className="relative top-3"
/>
</div>
<div className="z-50 hidden dark:block">
@ -83,19 +82,19 @@ export const EmptyPageCommunity = ({
src={logoDarkPng}
alt="Langflow Logo Dark"
data-testid="empty_page_logo_dark"
className=""
className="relative top-3"
/>
</div>
<span
data-testid="mainpage_title"
className="z-50 text-center text-2xl font-semibold text-foreground"
className="z-50 text-center font-chivo text-2xl font-semibold text-foreground"
>
{EMPTY_PAGE_TITLE}
</span>
<span
data-testid="empty_page_description"
className="z-50 text-center text-[14px] text-secondary-foreground"
className="z-50 text-center text-xs text-secondary-foreground"
>
{folders?.length > 1
? EMPTY_PAGE_FOLDER_DESCRIPTION
@ -103,7 +102,7 @@ export const EmptyPageCommunity = ({
</span>
</div>
<div className="flex w-full max-w-[510px] flex-col gap-12 sm:gap-8">
<div className="flex w-full max-w-[510px] flex-col gap-12 sm:gap-[29px]">
<Button
unstyled
className="group mx-3 h-[84px] sm:mx-0"
@ -113,7 +112,7 @@ export const EmptyPageCommunity = ({
}}
data-testid="empty_page_github_button"
>
<div className="relative flex flex-col rounded-lg border-[1px] bg-background p-4 transition-all duration-300 hover:-translate-y-0.5 hover:border-accent-pink-foreground hover:shadow-[0_4px_10px_rgba(198,97,184,0.25)]">
<div className="relative flex flex-col rounded-lg border-[1px] bg-background p-4 transition-all duration-300 hover:border-accent-pink-foreground">
<div className="grid w-full items-center justify-between gap-2">
<div className="flex gap-3">
<FaGithub className="h-6 w-6" />
@ -143,7 +142,7 @@ export const EmptyPageCommunity = ({
}}
data-testid="empty_page_discord_button"
>
<div className="relative flex flex-col rounded-lg border-[1px] bg-background p-4 transition-all duration-300 hover:-translate-y-0.5 hover:border-discord-color hover:shadow-[0_4px_10px_rgba(88,101,242,0.25)]">
<div className="relative flex flex-col rounded-lg border-[1px] bg-background p-4 transition-all duration-300 hover:border-discord-color">
<div className="grid w-full items-center justify-between gap-2">
<div className="flex gap-3">
<FaDiscord className="h-6 w-6 text-discord-color" />
@ -166,7 +165,7 @@ export const EmptyPageCommunity = ({
<Button
variant="default"
className="z-10 m-auto h-10 w-full max-w-[155px] rounded-lg font-bold transition-all duration-300"
className="z-10 m-auto mt-3 h-10 w-full max-w-[155px] rounded-lg font-bold transition-all duration-300"
onClick={() => setOpenModal(true)}
id="new-project-btn"
data-testid="new_project_btn_empty_page"
@ -178,7 +177,7 @@ export const EmptyPageCommunity = ({
</div>
<p
data-testid="empty_page_drag_and_drop_text"
className="absolute bottom-5 left-0 right-0 mt-4 cursor-default text-center text-sm text-muted-foreground"
className="absolute bottom-5 left-0 right-0 mt-4 cursor-default text-center text-xs text-muted-foreground"
>
{EMPTY_PAGE_DRAG_AND_DROP_TEXT}
</p>

View file

@ -148,7 +148,10 @@ export default function StorePage(): JSX.Element {
setLoading(false);
setErrorData({
title: COMPONENTS_ERROR_ALERT,
list: [err["response"]["data"]["detail"]],
list: [
err?.response?.data?.detail ??
"There was an error fetching the components",
],
});
}
});

View file

@ -1257,6 +1257,10 @@
@apply h-7 w-7 rounded-md;
}
.hit-area-hover {
@apply transition-colors duration-200 hover:bg-muted hover:text-foreground;
}
.node-toolbar-buttons {
@apply flex w-max items-center gap-1 rounded-lg text-foreground;
}

View file

@ -80,8 +80,8 @@ withEventDeliveryModes(
const output = await page.getByTestId("div-chat-message").allTextContents();
const outputText = output.join("\n");
expect(outputText.toLowerCase()).toContain("weather");
expect(outputText.toLowerCase()).toContain("budget");
expect(outputText.toLowerCase()).toContain("travel");
expect(outputText.toLowerCase()).toContain("day");
expect(outputText.toLowerCase()).toContain(randomCity.toLowerCase());
expect(outputText.toLowerCase()).toContain(randomCity2.toLowerCase());

View file

@ -37,35 +37,6 @@ withEventDeliveryModes(
await initialGPTsetup(page);
if (process?.env?.ASTRA_DB_API_ENDPOINT?.includes("astra-dev")) {
await page.getByTestId("title-Astra DB").first().click();
await page.getByTestId("code-button-modal").click();
await page.waitForSelector("text=Edit Code", {
timeout: 3000,
});
let cleanCode = await extractAndCleanCode(page);
cleanCode = cleanCode!.replace(
'"pre_delete_collection": self.pre_delete_collection or False,',
'"pre_delete_collection": self.pre_delete_collection or False,\n "environment": "dev",',
);
await page.locator("textarea").last().press(`ControlOrMeta+a`);
await page.keyboard.press("Backspace");
await page.locator("textarea").last().fill(cleanCode);
await page.locator('//*[@id="checkAndSaveBtn"]').click();
await page.waitForSelector('[data-testid="title-Astra DB"]', {
timeout: 3000,
});
await page.getByTestId("title-Astra DB").last().click();
await page.getByTestId("code-button-modal").click();
await page.waitForSelector("text=Edit Code", {
timeout: 3000,
});
await page.locator("textarea").last().press(`ControlOrMeta+a`);
await page.keyboard.press("Backspace");
await page.locator("textarea").last().fill(cleanCode);
await page.locator('//*[@id="checkAndSaveBtn"]').click();
}
await page.waitForSelector('[data-testid="title-Astra DB"]', {
timeout: 3000,
});
@ -263,7 +234,10 @@ withEventDeliveryModes(
path.join(__dirname, "../../assets/test_file.txt"),
);
await page.getByText("test_file.txt").last().isVisible();
await page.waitForTimeout(500);
await page.waitForSelector("text=file uploaded successfully", {
timeout: 10000,
});
await page.waitForTimeout(3000);
await page.getByTestId("select-files-modal-button").click();
await page.getByTestId("button_run_astra db").last().click();
await page.waitForSelector("text=built successfully", {

View file

@ -19,6 +19,8 @@ test(
await page.keyboard.press("Enter");
await page.waitForTimeout(1000);
let flowName = await page.getByTestId("input-flow-name").inputValue();
expect(flowName).toBe(randomName);
@ -44,6 +46,8 @@ test(
await page.keyboard.press("Enter");
await page.waitForTimeout(1000);
flowName = await page.getByTestId("input-flow-name").inputValue();
expect(flowName).toBe(randomName2);
@ -84,6 +88,8 @@ test(
await page.keyboard.press("Enter");
await page.waitForTimeout(1000);
flowName = await page.getByTestId("input-flow-name").inputValue();
expect(flowName).toBe(randomName4);

View file

@ -1,14 +1,15 @@
import { test } from "@playwright/test";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
test("should exists Store", { tag: ["@release"] }, async ({ page }) => {
await page.goto("/");
await awaitBootstrapTest(page, { skipModal: true });
await page.getByTestId("button-store").isVisible();
await page.getByTestId("button-store").isEnabled();
});
test("should not have an API key", { tag: ["@release"] }, async ({ page }) => {
await page.goto("/");
await awaitBootstrapTest(page, { skipModal: true });
await page.getByTestId("button-store").click();

View file

@ -14,7 +14,7 @@ test.skip(
if (!process.env.CI) {
dotenv.config({ path: path.resolve(__dirname, "../../.env") });
}
await awaitBootstrapTest(page);
await awaitBootstrapTest(page, { skipModal: true });
await page.getByText("Close", { exact: true }).click();
await page.waitForTimeout(1000);
@ -96,7 +96,8 @@ test.skip(
dotenv.config({ path: path.resolve(__dirname, "../../.env") });
}
await page.goto("/");
await awaitBootstrapTest(page, { skipModal: true });
await page.waitForTimeout(1000);
await page.getByTestId("button-store").click();

View file

@ -1,11 +1,14 @@
import { expect, test } from "@playwright/test";
import * as dotenv from "dotenv";
import path from "path";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
test(
"should order the visualization (requires store API key)",
{ tag: ["@release"] },
async ({ page }) => {
async ({ page, context }) => {
test.skip();
test.skip(
!process?.env?.STORE_API_KEY,
"STORE_API_KEY required to run this test",
@ -15,45 +18,71 @@ test(
dotenv.config({ path: path.resolve(__dirname, "../../.env") });
}
await page.goto("/");
await awaitBootstrapTest(page, { skipModal: true });
await page.getByTestId("button-store").click();
await page.waitForTimeout(1000);
await page.waitForSelector('[data-testid="button-store"]', {
state: "visible",
timeout: 10000,
});
await page.getByTestId("api-key-button-store").click({
const [newPageStore] = await Promise.all([
context.waitForEvent("page"),
page.getByTestId("button-store").click(),
]);
await newPageStore.waitForTimeout(1000);
await newPageStore.getByTestId("api-key-button-store").click({
timeout: 200000,
});
await page
await newPageStore.getByTestId("sidebar-nav-Langflow Store").click();
await newPageStore
.getByPlaceholder("Insert your API Key")
.fill(process.env.STORE_API_KEY ?? "");
await page.getByTestId("api-key-save-button-store").click();
await newPageStore.getByTestId("api-key-save-button-store").click();
await expect(page.getByText("API key saved successfully")).toBeVisible({
await expect(
newPageStore.getByText("API key saved successfully"),
).toBeVisible({
timeout: 5000,
});
await page.getByTestId("button-store").click();
await newPageStore.getByTestId("back_page_button").click();
await expect(page.getByText("Basic RAG")).toBeVisible({ timeout: 30000 });
await newPageStore.waitForTimeout(1000);
await page.getByTestId("select-order-store").click();
const newPageStore2 = await context.newPage();
await page.getByText("Alphabetical").click();
await newPageStore2.goto("/store");
await page.getByText("Album Cover Builder").isVisible();
await newPageStore2.waitForTimeout(1000);
await page.getByTestId("select-order-store").click();
await page.getByText("Popular").click();
await expect(newPageStore2.getByText("Basic RAG")).toBeVisible({
timeout: 30000,
});
await page.getByText("Basic RAG").isVisible();
await newPageStore2.getByTestId("select-order-store").click();
await newPageStore2.getByText("Alphabetical").click();
await newPageStore2.getByText("Album Cover Builder").isVisible();
await newPageStore2.getByTestId("select-order-store").click();
await newPageStore2.getByText("Popular").click();
await newPageStore2.getByText("Basic RAG").isVisible();
},
);
test(
"should filter by type (requires store API key)",
{ tag: ["@release"] },
async ({ page }) => {
async ({ page, context }) => {
test.skip();
test.skip(
!process?.env?.STORE_API_KEY,
"STORE_API_KEY required to run this test",
@ -61,60 +90,95 @@ test(
if (!process.env.CI) {
dotenv.config({ path: path.resolve(__dirname, "../../.env") });
}
await page.goto("/");
await awaitBootstrapTest(page, { skipModal: true });
await page.waitForSelector('[data-testid="button-store"]', {
state: "visible",
timeout: 10000,
});
await page.getByTestId("button-store").click();
await page.getByTestId("api-key-button-store").click({
const newPageStore = await context.waitForEvent("page", { timeout: 30000 });
await newPageStore.waitForTimeout(1000);
await newPageStore.getByTestId("api-key-button-store").click({
timeout: 200000,
});
await page
await newPageStore.getByTestId("sidebar-nav-Langflow Store").click();
await newPageStore
.getByPlaceholder("Insert your API Key")
.fill(process.env.STORE_API_KEY ?? "");
await page.getByTestId("api-key-save-button-store").click();
await expect(page.getByText("API key saved successfully")).toBeVisible({
await newPageStore.getByTestId("api-key-save-button-store").click();
await expect(
newPageStore.getByText("API key saved successfully"),
).toBeVisible({
timeout: 5000,
});
await page.getByTestId("button-store").click();
await page.waitForSelector('[data-testid="likes-Website Content QA"]', {
await newPageStore.getByTestId("back_page_button").click();
await newPageStore.waitForTimeout(1000);
const newPageStore2 = await context.newPage();
await newPageStore2.goto("/store");
await newPageStore2.waitForTimeout(1000);
await newPageStore2.waitForSelector(
'[data-testid="likes-Website Content QA"]',
{
timeout: 100000,
},
);
await newPageStore2.getByText("Website Content QA").isVisible();
await newPageStore2.waitForSelector('[data-testid="flows-button-store"]', {
timeout: 100000,
});
await page.getByText("Website Content QA").isVisible();
await page.waitForSelector('[data-testid="flows-button-store"]', {
await newPageStore2.getByTestId("flows-button-store").click();
await newPageStore2.waitForSelector('[data-testid="icon-Group"]', {
timeout: 100000,
});
await page.getByTestId("flows-button-store").click();
await page.waitForSelector('[data-testid="icon-Group"]', {
timeout: 100000,
});
let iconGroup = await page.getByTestId("icon-Group")?.count();
let iconGroup = await newPageStore2.getByTestId("icon-Group")?.count();
expect(iconGroup).not.toBe(0);
await page.getByText("icon-ToyBrick").last().isHidden();
await page.waitForSelector('[data-testid="components-button-store"]', {
timeout: 100000,
});
await page.getByTestId("components-button-store").click();
await expect(page.getByTestId("icon-Group").last()).toBeHidden({
await newPageStore2.getByText("icon-ToyBrick").last().isHidden();
await newPageStore2.waitForSelector(
'[data-testid="components-button-store"]',
{
timeout: 100000,
},
);
await newPageStore2.getByTestId("components-button-store").click();
await expect(newPageStore2.getByTestId("icon-Group").last()).toBeHidden({
timeout: 30000,
});
await page.waitForSelector('[data-testid="icon-ToyBrick"]', {
await newPageStore2.waitForSelector('[data-testid="icon-ToyBrick"]', {
timeout: 100000,
});
let toyBrick = await page.getByTestId("icon-ToyBrick")?.count();
let toyBrick = await newPageStore2.getByTestId("icon-ToyBrick")?.count();
expect(toyBrick).not.toBe(0);
await page.waitForSelector('[data-testid="all-button-store"]', {
await newPageStore2.waitForSelector('[data-testid="all-button-store"]', {
timeout: 100000,
});
await page.getByTestId("all-button-store").click();
await page.waitForSelector('[data-testid="icon-Group"]', {
await newPageStore2.getByTestId("all-button-store").click();
await newPageStore2.waitForSelector('[data-testid="icon-Group"]', {
timeout: 100000,
});
await page.waitForSelector('[data-testid="icon-ToyBrick"]', {
await newPageStore2.waitForSelector('[data-testid="icon-ToyBrick"]', {
timeout: 100000,
});
let iconGroupAllCount = await page.getByTestId("icon-Group")?.count();
await page.waitForTimeout(500);
let toyBrickAllCount = await page.getByTestId("icon-ToyBrick")?.count();
await page.waitForTimeout(500);
let iconGroupAllCount = await newPageStore2
.getByTestId("icon-Group")
?.count();
await newPageStore2.waitForTimeout(500);
let toyBrickAllCount = await newPageStore2
.getByTestId("icon-ToyBrick")
?.count();
await newPageStore2.waitForTimeout(500);
if (iconGroupAllCount === 0 || toyBrickAllCount === 0) {
expect(false).toBe(true);
}

View file

@ -1,56 +0,0 @@
# Task: Gradient Border Styling Refinement
**Status**: Completed
## Analysis
- [x] Requirements
- [x] Match the gradient border style of the GitHub card shown in the reference image
- [x] Ensure a consistent purple hue that fades from top to bottom
- [x] Maintain proper dark background for content
- [x] Challenges
- [x] Achieving precise gradient fade matching the reference image
- [x] Balancing border visibility while maintaining subtle effect
- [x] Dependencies
- [x] Existing BackgroundGradient component
## Plan
- [x] Step 1: Review the current implementation
- [x] Analyze existing gradient colors and opacity values
- [x] Compare with reference image for differences
- [x] Step 2: Adjust color and gradient parameters
- [x] Modify gradient colors to match the purple hue
- [x] Fine-tune opacity values for natural fade
- [x] Ensure proper background color for inner content
- [x] Step 3: Optimize hover effects
- [x] Refine blur and opacity transitions on hover
- [x] Test with different content types
## Execution
- [x] Modify gradient parameters
- [x] Update gradient direction and opacity stops
- [x] Simplify to single-color fade for consistency
- [x] Adjust container styles
- [x] Set proper padding for border thickness
- [x] Ensure rounded corners match reference
- [x] Test and refine
- [x] Compare implementation with reference image
- [x] Make final adjustments to achieve exact match
## Summary
- [x] Files modified: `src/frontend/src/components/ui/background-gradient.tsx`
- [x] Dependencies added/changed: None
- [x] Edge cases considered: Different screen sizes, container dimensions
- [x] Known limitations: Exact color reproduction may vary slightly based on monitor calibration
- [x] Future impact points: Component can be reused across the application for consistent styling
### Implementation Details
1. Changed the gradient to a simpler purple fade from top to bottom
2. Used opacity values of 0.7 at top and 0.3 at bottom for subtle fade
3. Reduced border thickness to 2px with `p-[2px]`
4. Made the hover effect more subtle with `opacity-60` instead of `opacity-100`
5. Changed blur on hover from `blur-lg` to `blur-md` for a less intense glow