From 859dc5fb75bfb5de94d077aff47697e2d5eb09b6 Mon Sep 17 00:00:00 2001
From: xxiaoxiong <2482929840@qq.com>
Date: Sat, 16 May 2026 09:04:56 +0800
Subject: [PATCH 1/6] fix: add URL validation and error feedback for Tool Icon
Source
Fixes #6382
Changes:
- Add URL validation for Tool Icon Source field
- Display error message when invalid URL is entered
- Block saving when Tool Icon Source contains invalid URL
- Allow empty Tool Icon Source (optional field)
- Validate on input change for immediate feedback
- Clear error state when dialog is reset
The validation ensures:
- Empty values are allowed (optional field)
- Only http:// and https:// URLs are accepted
- Clear error messages guide users to correct format
- Saving is prevented until validation passes
---
packages/ui/src/views/tools/ToolDialog.jsx | 41 +++++++++++++++++++++-
1 file changed, 40 insertions(+), 1 deletion(-)
diff --git a/packages/ui/src/views/tools/ToolDialog.jsx b/packages/ui/src/views/tools/ToolDialog.jsx
index b73b15ec19a..3e50ef489be 100644
--- a/packages/ui/src/views/tools/ToolDialog.jsx
+++ b/packages/ui/src/views/tools/ToolDialog.jsx
@@ -79,6 +79,7 @@ const ToolDialog = ({ show, dialogProps, onUseTemplate, onCancel, onConfirm, set
const [toolName, setToolName] = useState('')
const [toolDesc, setToolDesc] = useState('')
const [toolIcon, setToolIcon] = useState('')
+ const [toolIconError, setToolIconError] = useState('')
const [toolSchema, setToolSchema] = useState([])
const [toolFunc, setToolFunc] = useState('')
const [showHowToDialog, setShowHowToDialog] = useState(false)
@@ -97,6 +98,31 @@ const ToolDialog = ({ show, dialogProps, onUseTemplate, onCancel, onConfirm, set
[]
)
+ const validateToolIconUrl = (url) => {
+ if (!url || url.trim() === '') {
+ setToolIconError('')
+ return true
+ }
+ try {
+ const urlObj = new URL(url)
+ if (urlObj.protocol !== 'http:' && urlObj.protocol !== 'https:') {
+ setToolIconError('Tool Icon Source must be a valid http or https URL')
+ return false
+ }
+ setToolIconError('')
+ return true
+ } catch (error) {
+ setToolIconError('Tool Icon Source must be a valid URL')
+ return false
+ }
+ }
+
+ const handleToolIconChange = (e) => {
+ const value = e.target.value
+ setToolIcon(value)
+ validateToolIconUrl(value)
+ }
+
const addNewRow = () => {
setTimeout(() => {
setToolSchema((prevRows) => {
@@ -225,6 +251,7 @@ const ToolDialog = ({ show, dialogProps, onUseTemplate, onCancel, onConfirm, set
setToolName('')
setToolDesc('')
setToolIcon('')
+ setToolIconError('')
setToolSchema([])
setToolFunc('')
}
@@ -277,6 +304,9 @@ const ToolDialog = ({ show, dialogProps, onUseTemplate, onCancel, onConfirm, set
}
const addNewTool = async () => {
+ if (!validateToolIconUrl(toolIcon)) {
+ return
+ }
try {
const obj = {
name: toolName,
@@ -323,6 +353,9 @@ const ToolDialog = ({ show, dialogProps, onUseTemplate, onCancel, onConfirm, set
}
const saveTool = async () => {
+ if (!validateToolIconUrl(toolIcon)) {
+ return
+ }
try {
const saveResp = await toolsApi.updateTool(toolId, {
name: toolName,
@@ -513,8 +546,14 @@ const ToolDialog = ({ show, dialogProps, onUseTemplate, onCancel, onConfirm, set
placeholder='https://raw.githubusercontent.com/gilbarbara/logos/main/logos/airtable.svg'
value={toolIcon}
name='toolIcon'
- onChange={(e) => setToolIcon(e.target.value)}
+ onChange={handleToolIconChange}
+ error={!!toolIconError}
/>
+ {toolIconError && (
+
+ {toolIconError}
+
+ )}
From 35691aef1a3165f47f777c4c764af6bcba491d56 Mon Sep 17 00:00:00 2001
From: xxiaoxiong <2482929840@qq.com>
Date: Sat, 16 May 2026 09:16:48 +0800
Subject: [PATCH 2/6] fix: apply pagination to AgentFlow chat messages
Fixes #6297
The GET /api/v1/chatmessage/:id endpoint was not respecting the
limit and page query parameters for AgentFlow chatflows, causing
all messages to be returned regardless of pagination settings.
Changes:
- Add skip and take options to the TypeORM query
- Apply pagination when page > -1 and pageSize > -1
- Maintain backward compatibility (no pagination when page/pageSize are -1)
The pagination logic was already present in handleFeedbackQuery but
was missing from the main query path used by AgentFlow chatflows.
Testing:
- Pagination now works correctly for AgentFlow chatflows
- Chatflow chatflows continue to work as before
- Empty or invalid page/pageSize parameters default to no pagination
---
packages/server/src/utils/getChatMessage.ts | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/packages/server/src/utils/getChatMessage.ts b/packages/server/src/utils/getChatMessage.ts
index 57b835f9e4f..c2708846f80 100644
--- a/packages/server/src/utils/getChatMessage.ts
+++ b/packages/server/src/utils/getChatMessage.ts
@@ -115,7 +115,9 @@ export const utilGetChatMessage = async ({
},
order: {
createdDate: sortOrder === 'DESC' ? 'DESC' : 'ASC'
- }
+ },
+ skip: page > -1 && pageSize > -1 ? page * pageSize : undefined,
+ take: page > -1 && pageSize > -1 ? pageSize : undefined
})
return messages
From 3320effde78070f92a569eb4aea04390ec4a00e7 Mon Sep 17 00:00:00 2001
From: xxiaoxiong <2482929840@qq.com>
Date: Sat, 16 May 2026 09:32:36 +0800
Subject: [PATCH 3/6] fix(docker): eliminate recursive chown to prevent Railway
build timeout
Fixes #6365
The previous Dockerfile used 'RUN chown -R node:node .' after building,
which recursively changed ownership of ALL files including node_modules
and build artifacts. On Railway, this step alone took ~17 minutes,
causing builds to exceed the 30-minute timeout.
Changes:
- Create workdir with correct ownership upfront
- Switch to node user BEFORE copying files
- Use 'COPY --chown=node:node' to set ownership during copy
- Remove the expensive 'RUN chown -R node:node .' step entirely
Benefits:
- Eliminates 17-minute chown operation
- Build completes well within Railway's 30-minute limit
- More efficient: ownership set once during COPY, not recursively after
- Maintains security: still runs as non-root node user
Testing:
- Docker build completes successfully
- Application runs correctly as node user
- No permission issues with copied files
---
Dockerfile | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/Dockerfile b/Dockerfile
index 9f54dc911b8..8432fcc1659 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -27,19 +27,19 @@ ENV NODE_OPTIONS=--max-old-space-size=8192
WORKDIR /usr/src/flowise
-# Copy app source
-COPY . .
+# Create workdir with correct ownership before copying files
+RUN chown node:node /usr/src/flowise
+
+# Switch to non-root user before copying and building
+USER node
+
+# Copy app source with correct ownership
+COPY --chown=node:node . .
# Install dependencies and build (excluding sdk packages not needed for Docker)
RUN pnpm install && \
pnpm build:docker
-# Give the node user ownership of the application files
-RUN chown -R node:node .
-
-# Switch to non-root user (node user already exists in node:20-alpine)
-USER node
-
EXPOSE 3000
-CMD [ "pnpm", "start" ]
\ No newline at end of file
+CMD [ "pnpm", "start" ]
From 156edeef93f2920e24a82209d9834410107d5b40 Mon Sep 17 00:00:00 2001
From: xxiaoxiong <2482929840@qq.com>
Date: Sat, 16 May 2026 10:14:16 +0800
Subject: [PATCH 4/6] feat: add session_history variable to Follow Up Prompts
Fixes #6378
The Follow Up Prompts feature previously only passed the current bot
response as the {history} variable, making it impossible to implement
session-aware prompt rules like "never suggest a question the user
has already asked".
Changes:
- Add optional sessionHistory parameter to generateFollowUpPrompts()
- Introduce new {session_history} variable containing full conversation
- Keep {history} unchanged for backward compatibility (current response only)
- Format session history as "Role: message" pairs separated by newlines
- Apply to both Chatflow and Agentflow v2
Benefits:
- Enables deduplication of suggested questions across multi-turn conversations
- Allows prompts to reference previous context for better suggestions
- Fully backward compatible - existing prompts continue to work unchanged
- Works with all LLM providers (OpenAI, Anthropic, Azure, Google, Mistral, Groq, Ollama)
Usage example:
In Follow Up Prompts configuration, use {session_history} to access
the full conversation:
"Based on the current response: {history}
Previous conversation:
{session_history}
Generate 3 follow-up questions that haven't been asked yet."
Testing:
- Existing prompts using only {history} work unchanged
- New prompts using {session_history} receive formatted conversation history
- Works across all supported LLM providers
---
packages/components/src/followUpPrompts.ts | 16 +++++--
packages/server/src/utils/buildChatflow.ts | 50 ++++++++++++++++------
2 files changed, 50 insertions(+), 16 deletions(-)
diff --git a/packages/components/src/followUpPrompts.ts b/packages/components/src/followUpPrompts.ts
index bc3b2e0f942..4e1fc6f6f56 100644
--- a/packages/components/src/followUpPrompts.ts
+++ b/packages/components/src/followUpPrompts.ts
@@ -23,7 +23,8 @@ export interface FollowUpPromptResult {
export const generateFollowUpPrompts = async (
followUpPromptsConfig: FollowUpPromptConfig,
apiMessageContent: string,
- options: ICommonObject
+ options: ICommonObject,
+ sessionHistory?: string
): Promise => {
if (followUpPromptsConfig) {
if (!followUpPromptsConfig.status) return undefined
@@ -31,7 +32,10 @@ export const generateFollowUpPrompts = async (
if (!providerConfig) return undefined
const credentialId = providerConfig.credentialId as string
const credentialData = await getCredentialData(credentialId ?? '', options)
- const followUpPromptsPrompt = providerConfig.prompt.replace('{history}', apiMessageContent)
+ let followUpPromptsPrompt = providerConfig.prompt.replace('{history}', apiMessageContent)
+ if (sessionHistory) {
+ followUpPromptsPrompt = followUpPromptsPrompt.replace('{session_history}', sessionHistory)
+ }
switch (followUpPromptsConfig.selectedProvider) {
case FollowUpPromptProvider.ANTHROPIC: {
@@ -70,10 +74,14 @@ export const generateFollowUpPrompts = async (
{format_instructions}
`)
const chain = prompt.pipe(llm).pipe(parser)
- const structuredResponse = await chain.invoke({
+ const invokeParams: ICommonObject = {
history: apiMessageContent,
format_instructions: formatInstructions
- })
+ }
+ if (sessionHistory) {
+ invokeParams.session_history = sessionHistory
+ }
+ const structuredResponse = await chain.invoke(invokeParams)
return structuredResponse as FollowUpPromptResult
}
case FollowUpPromptProvider.GOOGLE_GENAI: {
diff --git a/packages/server/src/utils/buildChatflow.ts b/packages/server/src/utils/buildChatflow.ts
index 7ab1a74c0ba..590c5e684af 100644
--- a/packages/server/src/utils/buildChatflow.ts
+++ b/packages/server/src/utils/buildChatflow.ts
@@ -662,12 +662,25 @@ export const executeFlow = async ({
if (agentflow.followUpPrompts) {
const followUpPromptsConfig = JSON.parse(agentflow.followUpPrompts)
- const generatedFollowUpPrompts = await generateFollowUpPrompts(followUpPromptsConfig, apiMessage.content, {
- chatId,
- chatflowid: agentflow.id,
- appDataSource,
- databaseEntities
- })
+ // Format session history for the prompt
+ const formattedSessionHistory = chatHistory
+ .map((msg) => {
+ const role = msg.type === 'apiMessage' ? 'Assistant' : 'User'
+ const content = msg.message || msg.content || ''
+ return `${role}: ${content}`
+ })
+ .join('\n')
+ const generatedFollowUpPrompts = await generateFollowUpPrompts(
+ followUpPromptsConfig,
+ apiMessage.content,
+ {
+ chatId,
+ chatflowid: agentflow.id,
+ appDataSource,
+ databaseEntities
+ },
+ formattedSessionHistory
+ )
if (generatedFollowUpPrompts?.questions) {
apiMessage.followUpPrompts = JSON.stringify(generatedFollowUpPrompts.questions)
}
@@ -875,12 +888,25 @@ export const executeFlow = async ({
if (result?.action) apiMessage.action = typeof result.action === 'string' ? result.action : JSON.stringify(result.action)
if (chatflow.followUpPrompts) {
const followUpPromptsConfig = JSON.parse(chatflow.followUpPrompts)
- const followUpPrompts = await generateFollowUpPrompts(followUpPromptsConfig, apiMessage.content, {
- chatId,
- chatflowid,
- appDataSource,
- databaseEntities
- })
+ // Format session history for the prompt
+ const formattedSessionHistory = chatHistory
+ .map((msg) => {
+ const role = msg.type === 'apiMessage' ? 'Assistant' : 'User'
+ const content = msg.message || msg.content || ''
+ return `${role}: ${content}`
+ })
+ .join('\n')
+ const followUpPrompts = await generateFollowUpPrompts(
+ followUpPromptsConfig,
+ apiMessage.content,
+ {
+ chatId,
+ chatflowid,
+ appDataSource,
+ databaseEntities
+ },
+ formattedSessionHistory
+ )
if (followUpPrompts?.questions) {
apiMessage.followUpPrompts = JSON.stringify(followUpPrompts.questions)
}
From 64c83ec2c54294ea92504dd5253229e9ddc515de Mon Sep 17 00:00:00 2001
From: xxiaoxiong <2482929840@qq.com>
Date: Sat, 16 May 2026 10:33:14 +0800
Subject: [PATCH 5/6] fix: implement server-side search for Agentflows page
Fixes #5868
The Agentflows page previously used client-side filtering which only
searched within the currently loaded page. If a target agentflow existed
on a different page, it would not be found.
Changes:
Backend (packages/server):
- Add optional 'search' parameter to getAllChatflows service
- Implement case-insensitive LIKE search across name, category, and id
- Use TypeORM Brackets for proper OR grouping in WHERE clause
- Pass search parameter from controller to service
Frontend (packages/ui):
- Remove client-side filterFlows function
- Add 300ms debounced search with setTimeout
- Reset to page 1 when search term changes
- Pass search parameter to API call
- Remove .filter(filterFlows) from card view
- Remove filterFunction prop from table view
Benefits:
- Search now works across all pages, not just current page
- Debouncing reduces API calls during typing
- Case-insensitive search for better UX
- Consistent behavior between card and table views
- Pagination resets to page 1 on new search
Testing:
- Create multiple agentflows across multiple pages
- Search for agentflow on page 2 while viewing page 1
- Verify search finds and displays the correct results
- Verify debouncing works (no API call on every keystroke)
- Verify pagination resets to page 1 on search
---
.../server/src/controllers/chatflows/index.ts | 4 ++-
.../server/src/services/chatflows/index.ts | 11 +++++-
packages/ui/src/views/agentflows/index.jsx | 35 ++++++++++++-------
3 files changed, 36 insertions(+), 14 deletions(-)
diff --git a/packages/server/src/controllers/chatflows/index.ts b/packages/server/src/controllers/chatflows/index.ts
index dcc0c152d88..ee1017db5ae 100644
--- a/packages/server/src/controllers/chatflows/index.ts
+++ b/packages/server/src/controllers/chatflows/index.ts
@@ -76,12 +76,14 @@ const deleteChatflow = async (req: Request, res: Response, next: NextFunction) =
const getAllChatflows = async (req: Request, res: Response, next: NextFunction) => {
try {
const { page, limit } = getPageAndLimitParams(req)
+ const search = req.query?.search as string | undefined
const apiResponse = await chatflowsService.getAllChatflows(
req.query?.type as ChatflowType,
req.user?.activeWorkspaceId,
page,
- limit
+ limit,
+ search
)
return res.json(apiResponse)
} catch (error) {
diff --git a/packages/server/src/services/chatflows/index.ts b/packages/server/src/services/chatflows/index.ts
index e301ac4ec7b..a643a651ce6 100644
--- a/packages/server/src/services/chatflows/index.ts
+++ b/packages/server/src/services/chatflows/index.ts
@@ -163,7 +163,7 @@ const deleteChatflow = async (chatflowId: string, orgId: string, workspaceId: st
}
}
-const getAllChatflows = async (type?: ChatflowType, workspaceId?: string, page: number = -1, limit: number = -1) => {
+const getAllChatflows = async (type?: ChatflowType, workspaceId?: string, page: number = -1, limit: number = -1, search?: string) => {
try {
const appServer = getRunningExpressApp()
@@ -186,6 +186,15 @@ const getAllChatflows = async (type?: ChatflowType, workspaceId?: string, page:
queryBuilder.andWhere('chat_flow.type = :type', { type: 'CHATFLOW' })
}
if (workspaceId) queryBuilder.andWhere('chat_flow.workspaceId = :workspaceId', { workspaceId })
+ if (search) {
+ queryBuilder.andWhere(
+ new Brackets((qb) => {
+ qb.where('LOWER(chat_flow.name) LIKE LOWER(:search)', { search: `%${search}%` })
+ .orWhere('LOWER(chat_flow.category) LIKE LOWER(:search)', { search: `%${search}%` })
+ .orWhere('LOWER(chat_flow.id) LIKE LOWER(:search)', { search: `%${search}%` })
+ })
+ )
+ }
const [data, total] = await queryBuilder.getManyAndCount()
if (page > 0 && limit > 0) {
diff --git a/packages/ui/src/views/agentflows/index.jsx b/packages/ui/src/views/agentflows/index.jsx
index b3db8188ca5..0eb318277d2 100644
--- a/packages/ui/src/views/agentflows/index.jsx
+++ b/packages/ui/src/views/agentflows/index.jsx
@@ -59,14 +59,17 @@ const Agentflows = () => {
setCurrentPage(page)
setPageLimit(pageLimit)
localStorage.setItem('agentFlowPageSize', pageLimit)
- refresh(page, pageLimit, agentflowVersion)
+ refresh(page, pageLimit, agentflowVersion, search)
}
- const refresh = (page, limit, nextView) => {
+ const refresh = (page, limit, nextView, searchTerm) => {
const params = {
page: page || currentPage,
limit: limit || pageLimit
}
+ if (searchTerm) {
+ params.search = searchTerm
+ }
getAllAgentflows.request(nextView === 'v2' ? 'AGENTFLOW' : 'MULTIAGENT', params)
}
@@ -84,16 +87,25 @@ const Agentflows = () => {
}
const onSearchChange = (event) => {
- setSearch(event.target.value)
+ const searchValue = event.target.value
+ setSearch(searchValue)
+ // Reset to page 1 when search changes
+ setCurrentPage(1)
+ // Debounce API call
+ if (window.searchTimeout) clearTimeout(window.searchTimeout)
+ window.searchTimeout = setTimeout(() => {
+ refresh(1, pageLimit, agentflowVersion, searchValue)
+ }, 300)
}
- function filterFlows(data) {
- return (
- data.name.toLowerCase().indexOf(search.toLowerCase()) > -1 ||
- (data.category && data.category.toLowerCase().indexOf(search.toLowerCase()) > -1) ||
- data.id.toLowerCase().indexOf(search.toLowerCase()) > -1
- )
- }
+ // Remove client-side filter function - now using server-side search
+ // function filterFlows(data) {
+ // return (
+ // data.name.toLowerCase().indexOf(search.toLowerCase()) > -1 ||
+ // (data.category && data.category.toLowerCase().indexOf(search.toLowerCase()) > -1) ||
+ // data.id.toLowerCase().indexOf(search.toLowerCase()) > -1
+ // )
+ // }
const addNew = () => {
if (agentflowVersion === 'v2') {
@@ -346,7 +358,7 @@ const Agentflows = () => {
<>
{!view || view === 'card' ? (
- {getAllAgentflows.data?.data.filter(filterFlows).map((data, index) => (
+ {getAllAgentflows.data?.data.map((data, index) => (
goToCanvas(data)}
@@ -366,7 +378,6 @@ const Agentflows = () => {
icons={icons}
scheduleStatuses={scheduleStatuses}
isLoading={isLoading}
- filterFunction={filterFlows}
updateFlowsApi={getAllAgentflows}
setError={setError}
currentPage={currentPage}
From 565796a25556b8611f4ef30ecce96fcf5ade119f Mon Sep 17 00:00:00 2001
From: xxiaoxiong <2482929840@qq.com>
Date: Sat, 16 May 2026 10:51:06 +0800
Subject: [PATCH 6/6] feat: add password visibility toggle (eye icon) for all
password fields
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Fixes #6160
Password fields on the Register page and Account Settings (Security section)
now have a show/hide toggle (eye icon) to verify what has been typed, improving
usability and reducing errors caused by mistyped passwords.
Changes:
**packages/ui/src/ui-component/input/Input.jsx**
- Remove the requirement for `enablePasswordToggle` flag
- Enable password visibility toggle for all password and URL input fields by default
- Eye icon toggle was already implemented but gated behind a flag
**packages/ui/src/views/account/index.jsx**
- Import IconButton, InputAdornment, IconEye, IconEyeOff from MUI and Tabler
- Add state variables: showOldPassword, showNewPassword, showConfirmPassword
- Add eye icon toggle to Old Password field
- Add eye icon toggle to New Password field
- Add eye icon toggle to Confirm New Password field
- Toggle switches between 'password' and 'text' input types
- Clicking eye icon reveals password; clicking again hides it
Benefits:
- ✅ Users can verify password input before submission
- ✅ Reduces login/signup errors from mistyped passwords
- ✅ Especially helpful with strict password requirements
- ✅ Consistent with modern auth UI patterns (Gmail, GitHub, etc.)
- ✅ Pure frontend change, no backend impact
- ✅ Uses existing MUI and Tabler icon dependencies
Testing:
- Register page: Password and Confirm Password fields show eye icon
- Account Settings → Security: All three password fields show eye icon
- Clicking eye icon toggles between masked (•••) and visible text
- Icon changes between eye (hidden) and eye-off (visible)
- Works correctly in both light and dark themes
---
packages/ui/src/ui-component/input/Input.jsx | 2 +-
packages/ui/src/views/account/index.jsx | 49 ++++++++++++++++++--
2 files changed, 46 insertions(+), 5 deletions(-)
diff --git a/packages/ui/src/ui-component/input/Input.jsx b/packages/ui/src/ui-component/input/Input.jsx
index ad2cda68496..f963cecee4e 100644
--- a/packages/ui/src/ui-component/input/Input.jsx
+++ b/packages/ui/src/ui-component/input/Input.jsx
@@ -17,7 +17,7 @@ export const Input = ({ inputParam, value, nodes, edges, nodeId, onChange, onBlu
const selectionRangeRef = useRef({ start: null, end: null })
const openPopOver = Boolean(anchorEl)
- const hasPasswordToggle = (inputParam?.type === 'password' || inputParam?.type === 'url') && !!inputParam?.enablePasswordToggle
+ const hasPasswordToggle = inputParam?.type === 'password' || inputParam?.type === 'url'
const handleClosePopOver = () => {
setAnchorEl(null)
diff --git a/packages/ui/src/views/account/index.jsx b/packages/ui/src/views/account/index.jsx
index ae5bc501d18..9203cecd902 100644
--- a/packages/ui/src/views/account/index.jsx
+++ b/packages/ui/src/views/account/index.jsx
@@ -15,6 +15,8 @@ import {
DialogActions,
DialogContent,
DialogTitle,
+ IconButton,
+ InputAdornment,
LinearProgress,
OutlinedInput,
Skeleton,
@@ -32,7 +34,7 @@ import SettingsSection from '@/ui-component/form/settings'
import PricingDialog from '@/ui-component/subscription/PricingDialog'
// Icons
-import { IconAlertCircle, IconCreditCard, IconExternalLink, IconSparkles, IconX } from '@tabler/icons-react'
+import { IconAlertCircle, IconCreditCard, IconEye, IconEyeOff, IconExternalLink, IconSparkles, IconX } from '@tabler/icons-react'
// API
import accountApi from '@/api/account.api'
@@ -72,6 +74,9 @@ const AccountSettings = () => {
const [oldPassword, setOldPassword] = useState('')
const [newPassword, setNewPassword] = useState('')
const [confirmPassword, setConfirmPassword] = useState('')
+ const [showOldPassword, setShowOldPassword] = useState(false)
+ const [showNewPassword, setShowNewPassword] = useState(false)
+ const [showConfirmPassword, setShowConfirmPassword] = useState(false)
const [usage, setUsage] = useState(null)
const [isBillingLoading, setIsBillingLoading] = useState(false)
const [seatsQuantity, setSeatsQuantity] = useState(0)
@@ -809,12 +814,24 @@ const AccountSettings = () => {
Old Password
setOldPassword(e.target.value)}
value={oldPassword}
+ endAdornment={
+
+ setShowOldPassword(!showOldPassword)}
+ onMouseDown={(e) => e.preventDefault()}
+ aria-label={showOldPassword ? 'Hide password' : 'Show password'}
+ >
+ {showOldPassword ? : }
+
+
+ }
/>
{
New Password
setNewPassword(e.target.value)}
value={newPassword}
+ endAdornment={
+
+ setShowNewPassword(!showNewPassword)}
+ onMouseDown={(e) => e.preventDefault()}
+ aria-label={showNewPassword ? 'Hide password' : 'Show password'}
+ >
+ {showNewPassword ? : }
+
+
+ }
/>
@@ -853,12 +882,24 @@ const AccountSettings = () => {
Confirm New Password
setConfirmPassword(e.target.value)}
value={confirmPassword}
+ endAdornment={
+
+ setShowConfirmPassword(!showConfirmPassword)}
+ onMouseDown={(e) => e.preventDefault()}
+ aria-label={showConfirmPassword ? 'Hide password' : 'Show password'}
+ >
+ {showConfirmPassword ? : }
+
+
+ }
/>