feat(ui): show total file count in FilesModal title (#25873)

This commit is contained in:
G30 2026-06-16 20:55:13 -04:00 committed by GitHub
parent 5cdcdbaeec
commit b4aef82401
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 73 additions and 2 deletions

View file

@ -201,6 +201,18 @@ class FilesTable:
result = await db.execute(select(File))
return [FileModel.model_validate(file) for file in result.scalars().all()]
async def count_files_by_user_id(
self,
user_id: str | None = None,
db: AsyncSession | None = None,
) -> int:
async with get_async_db_context(db) as db:
stmt = select(func.count(File.id))
if user_id:
stmt = stmt.filter_by(user_id=user_id)
result = await db.execute(stmt)
return result.scalar() or 0
async def check_access_by_user_id(self, id, user_id, permission='write', db: AsyncSession | None = None) -> bool:
file = await self.get_file_by_id(id, db=db)
if not file:

View file

@ -463,6 +463,20 @@ async def search_files(
return files
############################
# Count Files
############################
@router.get('/count', response_model=int)
async def count_files(
user=Depends(get_verified_user),
db: AsyncSession = Depends(get_async_session),
):
user_id = None if (user.role == 'admin' and BYPASS_ADMIN_ACCESS_CONTROL) else user.id
return await Files.count_files_by_user_id(user_id=user_id, db=db)
############################
# Delete All Files
############################

View file

@ -214,6 +214,34 @@ export const searchFiles = async (
return res;
};
export const getFileCount = async (token: string = '') => {
let error = null;
const res = await fetch(`${WEBUI_API_BASE_URL}/files/count`, {
method: 'GET',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
authorization: `Bearer ${token}`
}
})
.then(async (res) => {
if (!res.ok) throw await res.json();
return res.json();
})
.catch((err) => {
error = err;
console.error(err);
return null;
});
if (error) {
throw error;
}
return res;
};
export const getFileById = async (token: string, id: string) => {
let error = null;

View file

@ -4,7 +4,7 @@
import type { Writable } from 'svelte/store';
import dayjs from 'dayjs';
import { searchFiles, deleteFileById } from '$lib/apis/files';
import { searchFiles, deleteFileById, getFileCount } from '$lib/apis/files';
import Modal from '$lib/components/common/Modal.svelte';
import Spinner from '$lib/components/common/Spinner.svelte';
import Tooltip from '$lib/components/common/Tooltip.svelte';
@ -21,6 +21,7 @@
export let show = false;
let files: any[] | null = null;
let fileCount: number | null = null;
let query = '';
let orderBy = 'created_at';
let direction = 'desc';
@ -70,6 +71,10 @@
const newFiles = await searchFiles(localStorage.token, pattern, 0, PAGE_SIZE);
files = sortFiles(newFiles);
allFilesLoaded = newFiles.length < PAGE_SIZE;
if (!query) {
fileCount = await getFileCount(localStorage.token);
}
} catch (error) {
// Handle 404 or other errors - show empty state instead of spinner
files = [];
@ -124,6 +129,7 @@
toast.success($i18n.t('File deleted successfully.'));
// Remove from local array instead of re-fetching to allow rapid deletion
files = files?.filter((f) => f.id !== fileId) ?? null;
if (fileCount !== null) fileCount--;
} catch (error) {
toast.error(`${error}`);
}
@ -197,7 +203,18 @@
<Modal size="xl" bind:show>
<div>
<div class="flex justify-between dark:text-gray-300 px-5 pt-4 pb-1">
<div class="text-lg font-medium self-center">{$i18n.t('Files')}</div>
<div class="flex items-center gap-2 text-lg font-medium self-center">
<div>{$i18n.t('Files')}</div>
{#if query && files}
<div class="text-lg font-medium text-gray-500 dark:text-gray-500">
{files.length}
</div>
{:else if fileCount !== null}
<div class="text-lg font-medium text-gray-500 dark:text-gray-500">
{fileCount}
</div>
{/if}
</div>
<button
class="self-center"
on:click={() => {