fix(web/default): batch fix new UI issues #4880 #4893 #4817 #4877 #4898

- Add singleSelect to status/role filters in API keys, users, and redemptions tables (#4880)
- Fix affiliate link 404 by changing /register to /sign-up (#4893)
- Open FetchModelsDialog in channel creation mode via customFetcher prop (#4817)
- Add TruncatedText component with tooltip for long channel names, token names, and usernames (#4877)
- Elevate forgot-password link z-index to prevent label click interception (#4898)
This commit is contained in:
CaIon
2026-05-16 14:48:49 +08:00
parent 18282e610d
commit 3caa6e467b
10 changed files with 158 additions and 86 deletions
@@ -65,6 +65,8 @@ type FetchModelsDialogProps = {
onModelsSelected?: (models: string[]) => void
redirectModels?: string[]
redirectSourceModels?: string[]
customFetcher?: () => Promise<string[]>
existingModelsOverride?: string[]
}
export function FetchModelsDialog({
@@ -73,6 +75,8 @@ export function FetchModelsDialog({
onModelsSelected,
redirectModels = [],
redirectSourceModels = [],
customFetcher,
existingModelsOverride,
}: FetchModelsDialogProps) {
const { t } = useTranslation()
const { currentRow } = useChannels()
@@ -85,8 +89,10 @@ export function FetchModelsDialog({
// Parse existing models
const existingModels = useMemo(
() => parseModelsString(currentRow?.models || ''),
[currentRow?.models]
() =>
existingModelsOverride ??
parseModelsString(currentRow?.models || ''),
[existingModelsOverride, currentRow?.models]
)
// Categorize models with redirect models
@@ -121,26 +127,33 @@ export function FetchModelsDialog({
}, [fetchedModelSet, redirectSourceKeysSet, searchKeyword, selectedModels])
useEffect(() => {
if (open && currentRow) {
if (open && (currentRow || customFetcher)) {
handleFetchModels()
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [open, currentRow?.id])
}, [open, currentRow?.id, customFetcher])
const handleFetchModels = async () => {
if (!currentRow) return
if (!currentRow && !customFetcher) return
setIsFetching(true)
try {
const response = await fetchUpstreamModels(currentRow.id)
if (response.success) {
const list = Array.isArray(response.data) ? response.data : []
if (customFetcher) {
const list = await customFetcher()
setFetchedModels(list)
setSelectedModels(existingModels)
toast.success(t('Fetched {{count}} models', { count: list.length }))
} else {
toast.error(response.message || t('Failed to fetch models'))
setFetchedModels([])
const response = await fetchUpstreamModels(currentRow!.id)
if (response.success) {
const list = Array.isArray(response.data) ? response.data : []
setFetchedModels(list)
setSelectedModels(existingModels)
toast.success(t('Fetched {{count}} models', { count: list.length }))
} else {
toast.error(response.message || t('Failed to fetch models'))
setFetchedModels([])
}
}
} catch (error: unknown) {
toast.error(
@@ -153,8 +166,6 @@ export function FetchModelsDialog({
}
const handleSave = async () => {
if (!currentRow) return
// If onModelsSelected callback is provided, use it (form filling mode)
if (onModelsSelected) {
onModelsSelected(selectedModels)
@@ -164,6 +175,7 @@ export function FetchModelsDialog({
}
// Otherwise, directly save to API (standalone mode)
if (!currentRow) return
setIsSaving(true)
try {
const modelsString = selectedModels.join(',')
@@ -357,12 +369,16 @@ export function FetchModelsDialog({
<DialogHeader>
<DialogTitle>{t('Fetch Models')}</DialogTitle>
<DialogDescription>
{t('Fetch available models for:')}{' '}
<strong>{currentRow?.name}</strong>
{currentRow
? <>
{t('Fetch available models for:')}{' '}
<strong>{currentRow.name}</strong>
</>
: t('Fetch available models from upstream')}
</DialogDescription>
</DialogHeader>
{!currentRow ? (
{!currentRow && !customFetcher ? (
<div className='text-muted-foreground py-8 text-center'>
{t('No channel selected')}
</div>