chore: linting

This commit is contained in:
DESKTOP-RTLN3BA\$punk 2026-01-15 00:05:53 -08:00
parent 3375aeb9bc
commit 7ae68455b3
20 changed files with 128 additions and 103 deletions

View file

@ -24,9 +24,7 @@ def enum_exists(enum_name: str) -> bool:
"""Check if an enum type exists in the database.""" """Check if an enum type exists in the database."""
conn = op.get_bind() conn = op.get_bind()
result = conn.execute( result = conn.execute(
sa.text( sa.text("SELECT EXISTS (SELECT 1 FROM pg_type WHERE typname = :enum_name)"),
"SELECT EXISTS (SELECT 1 FROM pg_type WHERE typname = :enum_name)"
),
{"enum_name": enum_name}, {"enum_name": enum_name},
) )
return result.scalar() return result.scalar()

View file

@ -22,9 +22,7 @@ def enum_exists(enum_name: str) -> bool:
"""Check if an enum type exists in the database.""" """Check if an enum type exists in the database."""
conn = op.get_bind() conn = op.get_bind()
result = conn.execute( result = conn.execute(
sa.text( sa.text("SELECT EXISTS (SELECT 1 FROM pg_type WHERE typname = :enum_name)"),
"SELECT EXISTS (SELECT 1 FROM pg_type WHERE typname = :enum_name)"
),
{"enum_name": enum_name}, {"enum_name": enum_name},
) )
return result.scalar() return result.scalar()

View file

@ -197,9 +197,7 @@ def enum_exists(enum_name: str) -> bool:
"""Check if an enum type exists in the database.""" """Check if an enum type exists in the database."""
conn = op.get_bind() conn = op.get_bind()
result = conn.execute( result = conn.execute(
sa.text( sa.text("SELECT EXISTS (SELECT 1 FROM pg_type WHERE typname = :enum_name)"),
"SELECT EXISTS (SELECT 1 FROM pg_type WHERE typname = :enum_name)"
),
{"enum_name": enum_name}, {"enum_name": enum_name},
) )
return result.scalar() return result.scalar()

View file

@ -5,13 +5,14 @@ Revises: 61
Create Date: 2026-01-09 15:19:51.827647 Create Date: 2026-01-09 15:19:51.827647
""" """
from collections.abc import Sequence from collections.abc import Sequence
from alembic import op from alembic import op
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.
revision: str = '62' revision: str = "62"
down_revision: str | None = '61' down_revision: str | None = "61"
branch_labels: str | Sequence[str] | None = None branch_labels: str | Sequence[str] | None = None
depends_on: str | Sequence[str] | None = None depends_on: str | Sequence[str] | None = None

View file

@ -5,6 +5,7 @@ Revises: 62
Create Date: 2026-01-13 12:23:31.481643 Create Date: 2026-01-13 12:23:31.481643
""" """
from collections.abc import Sequence from collections.abc import Sequence
from sqlalchemy import text from sqlalchemy import text
@ -12,8 +13,8 @@ from sqlalchemy import text
from alembic import op from alembic import op
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.
revision: str = '63' revision: str = "63"
down_revision: str | None = '62' down_revision: str | None = "62"
branch_labels: str | Sequence[str] | None = None branch_labels: str | Sequence[str] | None = None
depends_on: str | Sequence[str] | None = None depends_on: str | Sequence[str] | None = None
@ -21,7 +22,7 @@ depends_on: str | Sequence[str] | None = None
def upgrade() -> None: def upgrade() -> None:
"""Upgrade schema.""" """Upgrade schema."""
connection = op.get_bind() connection = op.get_bind()
# Check if old constraint exists before trying to drop it # Check if old constraint exists before trying to drop it
old_constraint_exists = connection.execute( old_constraint_exists = connection.execute(
text(""" text("""
@ -31,14 +32,14 @@ def upgrade() -> None:
AND constraint_name='uq_searchspace_user_connector_type' AND constraint_name='uq_searchspace_user_connector_type'
""") """)
).scalar() ).scalar()
if old_constraint_exists: if old_constraint_exists:
op.drop_constraint( op.drop_constraint(
'uq_searchspace_user_connector_type', "uq_searchspace_user_connector_type",
'search_source_connectors', "search_source_connectors",
type_='unique' type_="unique",
) )
# Check if new constraint already exists before creating it # Check if new constraint already exists before creating it
new_constraint_exists = connection.execute( new_constraint_exists = connection.execute(
text(""" text("""
@ -48,19 +49,19 @@ def upgrade() -> None:
AND constraint_name='uq_searchspace_user_connector_type_name' AND constraint_name='uq_searchspace_user_connector_type_name'
""") """)
).scalar() ).scalar()
if not new_constraint_exists: if not new_constraint_exists:
op.create_unique_constraint( op.create_unique_constraint(
'uq_searchspace_user_connector_type_name', "uq_searchspace_user_connector_type_name",
'search_source_connectors', "search_source_connectors",
['search_space_id', 'user_id', 'connector_type', 'name'] ["search_space_id", "user_id", "connector_type", "name"],
) )
def downgrade() -> None: def downgrade() -> None:
"""Downgrade schema.""" """Downgrade schema."""
connection = op.get_bind() connection = op.get_bind()
# Check if new constraint exists before trying to drop it # Check if new constraint exists before trying to drop it
new_constraint_exists = connection.execute( new_constraint_exists = connection.execute(
text(""" text("""
@ -70,14 +71,14 @@ def downgrade() -> None:
AND constraint_name='uq_searchspace_user_connector_type_name' AND constraint_name='uq_searchspace_user_connector_type_name'
""") """)
).scalar() ).scalar()
if new_constraint_exists: if new_constraint_exists:
op.drop_constraint( op.drop_constraint(
'uq_searchspace_user_connector_type_name', "uq_searchspace_user_connector_type_name",
'search_source_connectors', "search_source_connectors",
type_='unique' type_="unique",
) )
# Check if old constraint already exists before creating it # Check if old constraint already exists before creating it
old_constraint_exists = connection.execute( old_constraint_exists = connection.execute(
text(""" text("""
@ -87,10 +88,10 @@ def downgrade() -> None:
AND constraint_name='uq_searchspace_user_connector_type' AND constraint_name='uq_searchspace_user_connector_type'
""") """)
).scalar() ).scalar()
if not old_constraint_exists: if not old_constraint_exists:
op.create_unique_constraint( op.create_unique_constraint(
'uq_searchspace_user_connector_type', "uq_searchspace_user_connector_type",
'search_source_connectors', "search_source_connectors",
['search_space_id', 'user_id', 'connector_type'] ["search_space_id", "user_id", "connector_type"],
) )

View file

@ -44,4 +44,3 @@ def downgrade() -> None:
DROP COLUMN IF EXISTS author_id; DROP COLUMN IF EXISTS author_id;
""" """
) )

View file

@ -18,7 +18,9 @@ logger = logging.getLogger(__name__)
class MCPClient: class MCPClient:
"""Client for communicating with an MCP server.""" """Client for communicating with an MCP server."""
def __init__(self, command: str, args: list[str], env: dict[str, str] | None = None): def __init__(
self, command: str, args: list[str], env: dict[str, str] | None = None
):
"""Initialize MCP client. """Initialize MCP client.
Args: Args:
@ -44,18 +46,16 @@ class MCPClient:
# Merge env vars with current environment # Merge env vars with current environment
server_env = os.environ.copy() server_env = os.environ.copy()
server_env.update(self.env) server_env.update(self.env)
# Create server parameters with env # Create server parameters with env
server_params = StdioServerParameters( server_params = StdioServerParameters(
command=self.command, command=self.command, args=self.args, env=server_env
args=self.args,
env=server_env
) )
# Spawn server process and create session # Spawn server process and create session
# Note: Cannot combine these context managers because ClientSession # Note: Cannot combine these context managers because ClientSession
# needs the read/write streams from stdio_client # needs the read/write streams from stdio_client
async with stdio_client(server=server_params) as (read, write): async with stdio_client(server=server_params) as (read, write): # noqa: SIM117
async with ClientSession(read, write) as session: async with ClientSession(read, write) as session:
# Initialize the connection # Initialize the connection
await session.initialize() await session.initialize()
@ -85,7 +85,9 @@ class MCPClient:
""" """
if not self.session: if not self.session:
raise RuntimeError("Not connected to MCP server. Use 'async with client.connect():'") raise RuntimeError(
"Not connected to MCP server. Use 'async with client.connect():'"
)
try: try:
# Call tools/list RPC method # Call tools/list RPC method
@ -93,11 +95,15 @@ class MCPClient:
tools = [] tools = []
for tool in response.tools: for tool in response.tools:
tools.append({ tools.append(
"name": tool.name, {
"description": tool.description or "", "name": tool.name,
"input_schema": tool.inputSchema if hasattr(tool, "inputSchema") else {}, "description": tool.description or "",
}) "input_schema": tool.inputSchema
if hasattr(tool, "inputSchema")
else {},
}
)
logger.info("Listed %d tools from MCP server", len(tools)) logger.info("Listed %d tools from MCP server", len(tools))
return tools return tools
@ -121,10 +127,14 @@ class MCPClient:
""" """
if not self.session: if not self.session:
raise RuntimeError("Not connected to MCP server. Use 'async with client.connect():'") raise RuntimeError(
"Not connected to MCP server. Use 'async with client.connect():'"
)
try: try:
logger.info("Calling MCP tool '%s' with arguments: %s", tool_name, arguments) logger.info(
"Calling MCP tool '%s' with arguments: %s", tool_name, arguments
)
# Call tools/call RPC method # Call tools/call RPC method
response = await self.session.call_tool(tool_name, arguments=arguments) response = await self.session.call_tool(tool_name, arguments=arguments)
@ -147,12 +157,17 @@ class MCPClient:
# Handle validation errors from MCP server responses # Handle validation errors from MCP server responses
# Some MCP servers (like server-memory) return extra fields not in their schema # Some MCP servers (like server-memory) return extra fields not in their schema
if "Invalid structured content" in str(e): if "Invalid structured content" in str(e):
logger.warning("MCP server returned data not matching its schema, but continuing: %s", e) logger.warning(
"MCP server returned data not matching its schema, but continuing: %s",
e,
)
# Try to extract result from error message or return a success message # Try to extract result from error message or return a success message
return "Operation completed (server returned unexpected format)" return "Operation completed (server returned unexpected format)"
raise raise
except (ValueError, TypeError, AttributeError, KeyError) as e: except (ValueError, TypeError, AttributeError, KeyError) as e:
logger.error("Failed to call MCP tool '%s': %s", tool_name, e, exc_info=True) logger.error(
"Failed to call MCP tool '%s': %s", tool_name, e, exc_info=True
)
return f"Error calling tool: {e!s}" return f"Error calling tool: {e!s}"

View file

@ -21,7 +21,8 @@ logger = logging.getLogger(__name__)
def _create_dynamic_input_model_from_schema( def _create_dynamic_input_model_from_schema(
tool_name: str, input_schema: dict[str, Any], tool_name: str,
input_schema: dict[str, Any],
) -> type[BaseModel]: ) -> type[BaseModel]:
"""Create a Pydantic model from MCP tool's JSON schema. """Create a Pydantic model from MCP tool's JSON schema.
@ -41,15 +42,18 @@ def _create_dynamic_input_model_from_schema(
for param_name, param_schema in properties.items(): for param_name, param_schema in properties.items():
param_description = param_schema.get("description", "") param_description = param_schema.get("description", "")
is_required = param_name in required_fields is_required = param_name in required_fields
# Use Any type for complex schemas to preserve structure # Use Any type for complex schemas to preserve structure
# This allows the MCP server to do its own validation # This allows the MCP server to do its own validation
from typing import Any as AnyType from typing import Any as AnyType
from pydantic import Field from pydantic import Field
if is_required: if is_required:
field_definitions[param_name] = (AnyType, Field(..., description=param_description)) field_definitions[param_name] = (
AnyType,
Field(..., description=param_description),
)
else: else:
field_definitions[param_name] = ( field_definitions[param_name] = (
AnyType | None, AnyType | None,
@ -88,7 +92,7 @@ async def _create_mcp_tool_from_definition(
async def mcp_tool_call(**kwargs) -> str: async def mcp_tool_call(**kwargs) -> str:
"""Execute the MCP tool call via the client.""" """Execute the MCP tool call via the client."""
logger.info(f"MCP tool '{tool_name}' called with params: {kwargs}") logger.info(f"MCP tool '{tool_name}' called with params: {kwargs}")
try: try:
# Connect to server and call tool # Connect to server and call tool
async with mcp_client.connect(): async with mcp_client.connect():
@ -114,7 +118,8 @@ async def _create_mcp_tool_from_definition(
async def load_mcp_tools( async def load_mcp_tools(
session: AsyncSession, search_space_id: int, session: AsyncSession,
search_space_id: int,
) -> list[StructuredTool]: ) -> list[StructuredTool]:
"""Load all MCP tools from user's active MCP server connectors. """Load all MCP tools from user's active MCP server connectors.
@ -150,7 +155,9 @@ async def load_mcp_tools(
env = server_config.get("env", {}) env = server_config.get("env", {})
if not command: if not command:
logger.warning(f"MCP connector {connector.id} missing command, skipping") logger.warning(
f"MCP connector {connector.id} missing command, skipping"
)
continue continue
# Create MCP client # Create MCP client
@ -168,7 +175,9 @@ async def load_mcp_tools(
# Create LangChain tools from definitions # Create LangChain tools from definitions
for tool_def in tool_definitions: for tool_def in tool_definitions:
try: try:
tool = await _create_mcp_tool_from_definition(tool_def, mcp_client) tool = await _create_mcp_tool_from_definition(
tool_def, mcp_client
)
tools.append(tool) tools.append(tool)
except Exception as e: except Exception as e:
logger.exception( logger.exception(

View file

@ -283,7 +283,8 @@ async def build_tools_async(
): ):
try: try:
mcp_tools = await load_mcp_tools( mcp_tools = await load_mcp_tools(
dependencies["db_session"], dependencies["search_space_id"], dependencies["db_session"],
dependencies["search_space_id"],
) )
tools.extend(mcp_tools) tools.extend(mcp_tools)
logging.info( logging.info(

View file

@ -23,7 +23,9 @@ class SearchSourceConnectorBase(BaseModel):
@field_validator("config") @field_validator("config")
@classmethod @classmethod
def validate_config_for_connector_type( def validate_config_for_connector_type(
cls, config: dict[str, Any], values: dict[str, Any], cls,
config: dict[str, Any],
values: dict[str, Any],
) -> dict[str, Any]: ) -> dict[str, Any]:
connector_type = values.data.get("connector_type") connector_type = values.data.get("connector_type")
return validate_connector_config(connector_type, config) return validate_connector_config(connector_type, config)

View file

@ -34,7 +34,6 @@ from .base import (
) )
from .markdown_processor import add_received_markdown_file_document from .markdown_processor import add_received_markdown_file_document
# Constants for LlamaCloud retry configuration # Constants for LlamaCloud retry configuration
LLAMACLOUD_MAX_RETRIES = 3 LLAMACLOUD_MAX_RETRIES = 3
LLAMACLOUD_BASE_DELAY = 5 # Base delay in seconds for exponential backoff LLAMACLOUD_BASE_DELAY = 5 # Base delay in seconds for exponential backoff

View file

@ -120,4 +120,3 @@ export function ApiKeyContent({ onMenuClick }: ApiKeyContentProps) {
</motion.div> </motion.div>
); );
} }

View file

@ -6,8 +6,8 @@ import { AnimatePresence, motion } from "motion/react";
import { useTranslations } from "next-intl"; import { useTranslations } from "next-intl";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { toast } from "sonner"; import { toast } from "sonner";
import { currentUserAtom } from "@/atoms/user/user-query.atoms";
import { updateUserMutationAtom } from "@/atoms/user/user-mutation.atoms"; import { updateUserMutationAtom } from "@/atoms/user/user-mutation.atoms";
import { currentUserAtom } from "@/atoms/user/user-query.atoms";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";

View file

@ -1,7 +1,7 @@
"use client"; "use client";
import { ArrowLeft, ChevronRight, X } from "lucide-react";
import type { LucideIcon } from "lucide-react"; import type { LucideIcon } from "lucide-react";
import { ArrowLeft, ChevronRight, X } from "lucide-react";
import { AnimatePresence, motion } from "motion/react"; import { AnimatePresence, motion } from "motion/react";
import { useTranslations } from "next-intl"; import { useTranslations } from "next-intl";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
@ -152,4 +152,3 @@ export function UserSettingsSidebar({
</> </>
); );
} }

View file

@ -7,7 +7,7 @@ import { useTranslations } from "next-intl";
import { useCallback, useState } from "react"; import { useCallback, useState } from "react";
import { ApiKeyContent } from "./components/ApiKeyContent"; import { ApiKeyContent } from "./components/ApiKeyContent";
import { ProfileContent } from "./components/ProfileContent"; import { ProfileContent } from "./components/ProfileContent";
import { UserSettingsSidebar, type SettingsNavItem } from "./components/UserSettingsSidebar"; import { type SettingsNavItem, UserSettingsSidebar } from "./components/UserSettingsSidebar";
export default function UserSettingsPage() { export default function UserSettingsPage() {
const t = useTranslations("userSettings"); const t = useTranslations("userSettings");

View file

@ -16,4 +16,3 @@ export const updateUserMutationAtom = atomWithMutation((get) => {
}, },
}; };
}); });

View file

@ -109,7 +109,9 @@ const DocumentUploadPopupContent: FC<{
<Upload className="size-4 sm:size-7 text-primary" /> <Upload className="size-4 sm:size-7 text-primary" />
</div> </div>
<div className="flex-1 min-w-0 pr-8 sm:pr-0"> <div className="flex-1 min-w-0 pr-8 sm:pr-0">
<h2 className="text-base sm:text-2xl font-semibold tracking-tight">Upload Documents</h2> <h2 className="text-base sm:text-2xl font-semibold tracking-tight">
Upload Documents
</h2>
<p className="text-xs sm:text-base text-muted-foreground mt-0.5 sm:mt-1 line-clamp-1 sm:line-clamp-none"> <p className="text-xs sm:text-base text-muted-foreground mt-0.5 sm:mt-1 line-clamp-1 sm:line-clamp-none">
Upload and sync your documents to your search space Upload and sync your documents to your search space
</p> </p>

View file

@ -36,11 +36,7 @@ import {
newLLMConfigsAtom, newLLMConfigsAtom,
} from "@/atoms/new-llm-config/new-llm-config-query.atoms"; } from "@/atoms/new-llm-config/new-llm-config-query.atoms";
import { currentUserAtom } from "@/atoms/user/user-query.atoms"; import { currentUserAtom } from "@/atoms/user/user-query.atoms";
import { import { ComposerAddAttachment, ComposerAttachments } from "@/components/assistant-ui/attachment";
ComposerAddAttachment,
ComposerAttachments,
} from "@/components/assistant-ui/attachment";
import { UserMessage } from "@/components/assistant-ui/user-message";
import { ConnectorIndicator } from "@/components/assistant-ui/connector-popup"; import { ConnectorIndicator } from "@/components/assistant-ui/connector-popup";
import { import {
InlineMentionEditor, InlineMentionEditor,
@ -53,6 +49,7 @@ import {
} from "@/components/assistant-ui/thinking-steps"; } from "@/components/assistant-ui/thinking-steps";
import { ToolFallback } from "@/components/assistant-ui/tool-fallback"; import { ToolFallback } from "@/components/assistant-ui/tool-fallback";
import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button"; import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
import { UserMessage } from "@/components/assistant-ui/user-message";
import { import {
DocumentMentionPicker, DocumentMentionPicker,
type DocumentMentionPickerRef, type DocumentMentionPickerRef,
@ -636,7 +633,6 @@ const AssistantActionBar: FC = () => {
); );
}; };
const EditComposer: FC = () => { const EditComposer: FC = () => {
return ( return (
<MessagePrimitive.Root className="aui-edit-composer-wrapper mx-auto flex w-full max-w-(--thread-max-width) flex-col px-2 py-3"> <MessagePrimitive.Root className="aui-edit-composer-wrapper mx-auto flex w-full max-w-(--thread-max-width) flex-col px-2 py-3">

View file

@ -139,30 +139,33 @@ export function DocumentUploadTab({
[acceptedFileTypes] [acceptedFileTypes]
); );
const onDrop = useCallback((acceptedFiles: File[]) => { const onDrop = useCallback(
setFiles((prev) => { (acceptedFiles: File[]) => {
const newFiles = [...prev, ...acceptedFiles]; setFiles((prev) => {
const newFiles = [...prev, ...acceptedFiles];
// Check file count limit
if (newFiles.length > MAX_FILES) { // Check file count limit
toast.error(t("max_files_exceeded"), { if (newFiles.length > MAX_FILES) {
description: t("max_files_exceeded_desc", { max: MAX_FILES }), toast.error(t("max_files_exceeded"), {
}); description: t("max_files_exceeded_desc", { max: MAX_FILES }),
return prev; });
} return prev;
}
// Check total size limit
const newTotalSize = newFiles.reduce((sum, file) => sum + file.size, 0); // Check total size limit
if (newTotalSize > MAX_TOTAL_SIZE_BYTES) { const newTotalSize = newFiles.reduce((sum, file) => sum + file.size, 0);
toast.error(t("max_size_exceeded"), { if (newTotalSize > MAX_TOTAL_SIZE_BYTES) {
description: t("max_size_exceeded_desc", { max: MAX_TOTAL_SIZE_MB }), toast.error(t("max_size_exceeded"), {
}); description: t("max_size_exceeded_desc", { max: MAX_TOTAL_SIZE_MB }),
return prev; });
} return prev;
}
return newFiles;
}); return newFiles;
}, [t]); });
},
[t]
);
const { getRootProps, getInputProps, isDragActive } = useDropzone({ const { getRootProps, getInputProps, isDragActive } = useDropzone({
onDrop, onDrop,
@ -191,7 +194,10 @@ export function DocumentUploadTab({
const isFileCountLimitReached = files.length >= MAX_FILES; const isFileCountLimitReached = files.length >= MAX_FILES;
const isSizeLimitReached = totalFileSize >= MAX_TOTAL_SIZE_BYTES; const isSizeLimitReached = totalFileSize >= MAX_TOTAL_SIZE_BYTES;
const remainingFiles = MAX_FILES - files.length; const remainingFiles = MAX_FILES - files.length;
const remainingSizeMB = Math.max(0, (MAX_TOTAL_SIZE_BYTES - totalFileSize) / (1024 * 1024)).toFixed(1); const remainingSizeMB = Math.max(
0,
(MAX_TOTAL_SIZE_BYTES - totalFileSize) / (1024 * 1024)
).toFixed(1);
// Track accordion state changes // Track accordion state changes
const handleAccordionChange = useCallback( const handleAccordionChange = useCallback(
@ -243,7 +249,8 @@ export function DocumentUploadTab({
<Alert className="border border-border bg-slate-400/5 dark:bg-white/5 flex items-start gap-3 [&>svg]:relative [&>svg]:left-0 [&>svg]:top-0 [&>svg~*]:pl-0"> <Alert className="border border-border bg-slate-400/5 dark:bg-white/5 flex items-start gap-3 [&>svg]:relative [&>svg]:left-0 [&>svg]:top-0 [&>svg~*]:pl-0">
<Info className="h-4 w-4 shrink-0 mt-0.5" /> <Info className="h-4 w-4 shrink-0 mt-0.5" />
<AlertDescription className="text-xs sm:text-sm leading-relaxed pt-0.5"> <AlertDescription className="text-xs sm:text-sm leading-relaxed pt-0.5">
{t("file_size_limit")} {t("upload_limits", { maxFiles: MAX_FILES, maxSizeMB: MAX_TOTAL_SIZE_MB })} {t("file_size_limit")}{" "}
{t("upload_limits", { maxFiles: MAX_FILES, maxSizeMB: MAX_TOTAL_SIZE_MB })}
</AlertDescription> </AlertDescription>
</Alert> </Alert>
@ -270,7 +277,9 @@ export function DocumentUploadTab({
<div className="flex flex-col items-center gap-2 sm:gap-4 text-center px-4"> <div className="flex flex-col items-center gap-2 sm:gap-4 text-center px-4">
<Upload className="h-8 w-8 sm:h-12 sm:w-12 text-destructive/70" /> <Upload className="h-8 w-8 sm:h-12 sm:w-12 text-destructive/70" />
<div> <div>
<p className="text-sm sm:text-lg font-medium text-destructive">{t("file_limit_reached")}</p> <p className="text-sm sm:text-lg font-medium text-destructive">
{t("file_limit_reached")}
</p>
<p className="text-xs sm:text-sm text-muted-foreground mt-1"> <p className="text-xs sm:text-sm text-muted-foreground mt-1">
{t("file_limit_reached_desc", { max: MAX_FILES })} {t("file_limit_reached_desc", { max: MAX_FILES })}
</p> </p>

View file

@ -1,7 +1,7 @@
import { import {
getMeResponse, getMeResponse,
updateUserResponse,
type UpdateUserRequest, type UpdateUserRequest,
updateUserResponse,
} from "@/contracts/types/user.types"; } from "@/contracts/types/user.types";
import { baseApiService } from "./base-api.service"; import { baseApiService } from "./base-api.service";