mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-06-22 13:34:24 +00:00
fix(web): improve mobile responsiveness of dashboard, plugin detail & models dialog
- monitoring: stack filters full-width, scrollable tab bar, reduce card/content padding on mobile - models dialog: provider form modal no longer overflows viewport on small screens; shared panel body padding shrinks on mobile - plugin logs: reduce horizontal padding on mobile
This commit is contained in:
@@ -646,7 +646,7 @@ export default function ModelsPanel({
|
||||
</PanelBody>
|
||||
|
||||
<Dialog open={providerFormOpen} onOpenChange={setProviderFormOpen}>
|
||||
<DialogContent className="w-[600px] p-6">
|
||||
<DialogContent className="w-full max-w-[calc(100%-2rem)] p-4 sm:max-w-[600px] sm:p-6">
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
{editingProviderId
|
||||
|
||||
@@ -38,7 +38,12 @@ export function PanelBody({
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<div className={cn('min-h-0 flex-1 overflow-auto px-6 py-5', className)}>
|
||||
<div
|
||||
className={cn(
|
||||
'min-h-0 flex-1 overflow-auto px-3 py-4 sm:px-6 sm:py-5',
|
||||
className,
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -109,10 +109,10 @@ export default function MonitoringFilters({
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-wrap items-center gap-6">
|
||||
<div className="flex w-full flex-col gap-3 sm:w-auto sm:flex-row sm:flex-wrap sm:items-center sm:gap-6">
|
||||
{/* Bot Filter */}
|
||||
<div className="flex items-center gap-2">
|
||||
<label className="text-sm font-medium text-foreground whitespace-nowrap">
|
||||
<label className="w-20 shrink-0 text-sm font-medium text-foreground sm:w-auto sm:whitespace-nowrap">
|
||||
{t('monitoring.filters.bot')}
|
||||
</label>
|
||||
<Select
|
||||
@@ -120,7 +120,7 @@ export default function MonitoringFilters({
|
||||
onValueChange={handleBotChange}
|
||||
disabled={loadingBots}
|
||||
>
|
||||
<SelectTrigger className="h-9 w-[140px]">
|
||||
<SelectTrigger className="h-9 w-full sm:w-[140px]">
|
||||
<SelectValue
|
||||
placeholder={
|
||||
loadingBots
|
||||
@@ -144,7 +144,7 @@ export default function MonitoringFilters({
|
||||
|
||||
{/* Pipeline Filter */}
|
||||
<div className="flex items-center gap-2">
|
||||
<label className="text-sm font-medium text-foreground whitespace-nowrap">
|
||||
<label className="w-20 shrink-0 text-sm font-medium text-foreground sm:w-auto sm:whitespace-nowrap">
|
||||
{t('monitoring.filters.pipeline')}
|
||||
</label>
|
||||
<Select
|
||||
@@ -152,7 +152,7 @@ export default function MonitoringFilters({
|
||||
onValueChange={handlePipelineChange}
|
||||
disabled={loadingPipelines}
|
||||
>
|
||||
<SelectTrigger className="h-9 w-[140px]">
|
||||
<SelectTrigger className="h-9 w-full sm:w-[140px]">
|
||||
<SelectValue
|
||||
placeholder={
|
||||
loadingPipelines
|
||||
@@ -176,11 +176,11 @@ export default function MonitoringFilters({
|
||||
|
||||
{/* Time Range Filter */}
|
||||
<div className="flex items-center gap-2">
|
||||
<label className="text-sm font-medium text-foreground whitespace-nowrap">
|
||||
<label className="w-20 shrink-0 text-sm font-medium text-foreground sm:w-auto sm:whitespace-nowrap">
|
||||
{t('monitoring.filters.timeRange')}
|
||||
</label>
|
||||
<Select value={timeRange} onValueChange={handleTimeRangeChange}>
|
||||
<SelectTrigger className="h-9 w-[150px]">
|
||||
<SelectTrigger className="h-9 w-full sm:w-[150px]">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
|
||||
@@ -270,7 +270,7 @@ function MonitoringPageContent() {
|
||||
{/* Filters and Refresh Button - Sticky */}
|
||||
<div className="sticky top-0 z-10 -mt-1 pb-5 pt-1 bg-background">
|
||||
<div>
|
||||
<div className="flex flex-wrap items-center justify-between gap-4 p-4 bg-card rounded-xl border">
|
||||
<div className="flex flex-col gap-3 p-3 bg-card rounded-xl border sm:flex-row sm:flex-wrap sm:items-center sm:justify-between sm:gap-4 sm:p-4">
|
||||
<MonitoringFilters
|
||||
selectedBots={filterState.selectedBots}
|
||||
selectedPipelines={filterState.selectedPipelines}
|
||||
@@ -285,7 +285,7 @@ function MonitoringPageContent() {
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={handleRefresh}
|
||||
className="shadow-sm flex-shrink-0"
|
||||
className="flex-1 shadow-sm sm:flex-shrink-0 sm:flex-none"
|
||||
>
|
||||
<RefreshCw className="w-4 h-4 mr-2" />
|
||||
{t('monitoring.refreshData')}
|
||||
@@ -312,27 +312,27 @@ function MonitoringPageContent() {
|
||||
onValueChange={setActiveTab}
|
||||
className="w-full"
|
||||
>
|
||||
<div className="px-6 pt-4">
|
||||
<TabsList className="h-12 p-1">
|
||||
<TabsTrigger value="messages" className="px-6 py-2">
|
||||
<div className="px-3 pt-4 sm:px-6">
|
||||
<TabsList className="h-12 w-full justify-start gap-1 overflow-x-auto p-1 sm:w-auto">
|
||||
<TabsTrigger value="messages" className="px-3 py-2 sm:px-6">
|
||||
{t('monitoring.tabs.messages')}
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="modelCalls" className="px-6 py-2">
|
||||
<TabsTrigger value="modelCalls" className="px-3 py-2 sm:px-6">
|
||||
{t('monitoring.tabs.modelCalls')}
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="tokens" className="px-6 py-2">
|
||||
<TabsTrigger value="tokens" className="px-3 py-2 sm:px-6">
|
||||
{t('monitoring.tabs.tokens')}
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="feedback" className="px-6 py-2">
|
||||
<TabsTrigger value="feedback" className="px-3 py-2 sm:px-6">
|
||||
{t('monitoring.tabs.feedback')}
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="errors" className="px-6 py-2">
|
||||
<TabsTrigger value="errors" className="px-3 py-2 sm:px-6">
|
||||
{t('monitoring.tabs.errors')}
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
</div>
|
||||
|
||||
<TabsContent value="messages" className="p-6 m-0">
|
||||
<TabsContent value="messages" className="p-3 m-0 sm:p-6">
|
||||
<div>
|
||||
{loading && (
|
||||
<div className="py-12 flex justify-center">
|
||||
@@ -362,7 +362,7 @@ function MonitoringPageContent() {
|
||||
>
|
||||
{/* Message Header - Always Visible */}
|
||||
<div
|
||||
className="p-5 cursor-pointer hover:bg-accent transition-colors"
|
||||
className="p-3 cursor-pointer hover:bg-accent transition-colors sm:p-5"
|
||||
onClick={() => toggleMessageExpand(msg.id)}
|
||||
>
|
||||
<div className="flex items-start justify-between">
|
||||
@@ -466,7 +466,7 @@ function MonitoringPageContent() {
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="modelCalls" className="p-6 m-0">
|
||||
<TabsContent value="modelCalls" className="p-3 m-0 sm:p-6">
|
||||
<div>
|
||||
{loading && (
|
||||
<div className="py-12 flex justify-center">
|
||||
@@ -482,7 +482,7 @@ function MonitoringPageContent() {
|
||||
{data.modelCalls.map((call) => (
|
||||
<div
|
||||
key={call.id}
|
||||
className="border rounded-xl p-5 transition-all duration-200"
|
||||
className="border rounded-xl p-3 transition-all duration-200 sm:p-5"
|
||||
>
|
||||
<div className="flex justify-between items-start mb-3">
|
||||
<div className="flex-1">
|
||||
@@ -672,7 +672,7 @@ function MonitoringPageContent() {
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="tokens" className="p-6 m-0">
|
||||
<TabsContent value="tokens" className="p-3 m-0 sm:p-6">
|
||||
<TokenMonitoring
|
||||
botIds={
|
||||
filterState.selectedBots.length > 0
|
||||
@@ -690,7 +690,7 @@ function MonitoringPageContent() {
|
||||
/>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="feedback" className="p-6 m-0">
|
||||
<TabsContent value="feedback" className="p-3 m-0 sm:p-6">
|
||||
<div>
|
||||
{loading && (
|
||||
<div className="py-12 flex justify-center">
|
||||
@@ -722,7 +722,7 @@ function MonitoringPageContent() {
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="errors" className="p-6 m-0">
|
||||
<TabsContent value="errors" className="p-3 m-0 sm:p-6">
|
||||
<div>
|
||||
{loading && (
|
||||
<div className="py-12 flex justify-center">
|
||||
@@ -739,7 +739,7 @@ function MonitoringPageContent() {
|
||||
>
|
||||
{/* Error Header - Always Visible */}
|
||||
<div
|
||||
className="p-5 cursor-pointer hover:bg-red-50 dark:hover:bg-red-950/50 transition-colors bg-red-50/50 dark:bg-red-950/30"
|
||||
className="p-3 cursor-pointer hover:bg-red-50 dark:hover:bg-red-950/50 transition-colors bg-red-50/50 dark:bg-red-950/30 sm:p-5"
|
||||
onClick={() => toggleErrorExpand(error.id)}
|
||||
>
|
||||
<div className="flex items-start justify-between">
|
||||
|
||||
@@ -90,7 +90,7 @@ export default function PluginLogs({
|
||||
|
||||
return (
|
||||
<div className="flex h-full flex-col">
|
||||
<div className="flex shrink-0 flex-wrap items-center gap-2 px-6 pb-3">
|
||||
<div className="flex shrink-0 flex-wrap items-center gap-2 px-1 pb-3 sm:px-6">
|
||||
<Select value={level} onValueChange={setLevel}>
|
||||
<SelectTrigger className="h-8 w-[130px]">
|
||||
<SelectValue />
|
||||
@@ -132,7 +132,7 @@ export default function PluginLogs({
|
||||
<div
|
||||
ref={scrollRef}
|
||||
onScroll={handleScroll}
|
||||
className="min-h-0 flex-1 overflow-auto bg-gray-50 px-6 py-3 font-mono text-xs leading-relaxed dark:bg-gray-900/40"
|
||||
className="min-h-0 flex-1 overflow-auto bg-gray-50 px-3 py-3 font-mono text-xs leading-relaxed dark:bg-gray-900/40 sm:px-6"
|
||||
>
|
||||
{logs.length === 0 ? (
|
||||
<div className="py-8 text-center text-sm text-gray-500 dark:text-gray-400">
|
||||
|
||||
Reference in New Issue
Block a user