mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-06-02 03:55:55 +00:00
- Added checks for maximum allowed extensions, bots, and pipelines in the backend services (PluginsRouterGroup, BotService, MCPService, PipelineService). - Updated system configuration to include limitation settings for max_bots, max_pipelines, and max_extensions. - Enhanced frontend components to handle limitations, providing user feedback when limits are reached. - Added internationalization support for limitation messages in English, Japanese, Simplified Chinese, and Traditional Chinese.
196 lines
6.2 KiB
TypeScript
196 lines
6.2 KiB
TypeScript
'use client';
|
|
import { useState, useEffect } from 'react';
|
|
import CreateCardComponent from '@/app/infra/basic-component/create-card-component/CreateCardComponent';
|
|
import { httpClient } from '@/app/infra/http/HttpClient';
|
|
import { PipelineCardVO } from '@/app/home/pipelines/components/pipeline-card/PipelineCardVO';
|
|
import PipelineCard from '@/app/home/pipelines/components/pipeline-card/PipelineCard';
|
|
import styles from './pipelineConfig.module.css';
|
|
import { toast } from 'sonner';
|
|
import { useTranslation } from 'react-i18next';
|
|
import PipelineDialog from './PipelineDetailDialog';
|
|
import {
|
|
Select,
|
|
SelectContent,
|
|
SelectItem,
|
|
SelectTrigger,
|
|
SelectValue,
|
|
} from '@/components/ui/select';
|
|
import { systemInfo } from '@/app/infra/http';
|
|
|
|
export default function PluginConfigPage() {
|
|
const { t } = useTranslation();
|
|
const [dialogOpen, setDialogOpen] = useState<boolean>(false);
|
|
const [isEditForm, setIsEditForm] = useState(false);
|
|
const [pipelineList, setPipelineList] = useState<PipelineCardVO[]>([]);
|
|
const [selectedPipelineId, setSelectedPipelineId] = useState('');
|
|
const [sortByValue, setSortByValue] = useState<string>('created_at');
|
|
const [sortOrderValue, setSortOrderValue] = useState<string>('DESC');
|
|
|
|
useEffect(() => {
|
|
// Load sort preference from localStorage
|
|
const savedSortBy = localStorage.getItem('pipeline_sort_by');
|
|
const savedSortOrder = localStorage.getItem('pipeline_sort_order');
|
|
|
|
if (savedSortBy && savedSortOrder) {
|
|
setSortByValue(savedSortBy);
|
|
setSortOrderValue(savedSortOrder);
|
|
getPipelines(savedSortBy, savedSortOrder);
|
|
} else {
|
|
getPipelines();
|
|
}
|
|
}, []);
|
|
|
|
function getPipelines(
|
|
sortBy: string = sortByValue,
|
|
sortOrder: string = sortOrderValue,
|
|
) {
|
|
httpClient
|
|
.getPipelines(sortBy, sortOrder)
|
|
.then((value) => {
|
|
const currentTime = new Date();
|
|
const pipelineList = value.pipelines.map((pipeline) => {
|
|
const lastUpdatedTimeAgo = Math.floor(
|
|
(currentTime.getTime() -
|
|
new Date(
|
|
pipeline.updated_at ?? currentTime.getTime(),
|
|
).getTime()) /
|
|
1000 /
|
|
60 /
|
|
60 /
|
|
24,
|
|
);
|
|
|
|
const lastUpdatedTimeAgoText =
|
|
lastUpdatedTimeAgo > 0
|
|
? ` ${lastUpdatedTimeAgo} ${t('pipelines.daysAgo')}`
|
|
: t('pipelines.today');
|
|
|
|
return new PipelineCardVO({
|
|
lastUpdatedTimeAgo: lastUpdatedTimeAgoText,
|
|
description: pipeline.description,
|
|
id: pipeline.uuid ?? '',
|
|
name: pipeline.name,
|
|
emoji: pipeline.emoji,
|
|
isDefault: pipeline.is_default ?? false,
|
|
});
|
|
});
|
|
setPipelineList(pipelineList);
|
|
})
|
|
.catch((error) => {
|
|
toast.error(t('pipelines.getPipelineListError') + error.message);
|
|
});
|
|
}
|
|
|
|
const handlePipelineClick = (pipelineId: string) => {
|
|
setSelectedPipelineId(pipelineId);
|
|
setIsEditForm(true);
|
|
setDialogOpen(true);
|
|
};
|
|
|
|
const handleCreateNew = () => {
|
|
const maxPipelines = systemInfo.limitation?.max_pipelines ?? -1;
|
|
if (maxPipelines >= 0 && pipelineList.length >= maxPipelines) {
|
|
toast.error(t('limitation.maxPipelinesReached', { max: maxPipelines }));
|
|
return;
|
|
}
|
|
setIsEditForm(false);
|
|
setSelectedPipelineId('');
|
|
setDialogOpen(true);
|
|
};
|
|
|
|
function handleSortChange(value: string) {
|
|
const [newSortBy, newSortOrder] = value.split(',').map((s) => s.trim());
|
|
setSortByValue(newSortBy);
|
|
setSortOrderValue(newSortOrder);
|
|
|
|
// Save sort preference to localStorage
|
|
localStorage.setItem('pipeline_sort_by', newSortBy);
|
|
localStorage.setItem('pipeline_sort_order', newSortOrder);
|
|
|
|
getPipelines(newSortBy, newSortOrder);
|
|
}
|
|
|
|
return (
|
|
<div className={styles.configPageContainer}>
|
|
<PipelineDialog
|
|
open={dialogOpen}
|
|
onOpenChange={setDialogOpen}
|
|
pipelineId={selectedPipelineId || undefined}
|
|
isEditMode={isEditForm}
|
|
onFinish={() => {
|
|
getPipelines();
|
|
}}
|
|
onNewPipelineCreated={(pipelineId) => {
|
|
getPipelines();
|
|
setSelectedPipelineId(pipelineId);
|
|
setIsEditForm(true);
|
|
setDialogOpen(true);
|
|
}}
|
|
onDeletePipeline={() => {
|
|
getPipelines();
|
|
setDialogOpen(false);
|
|
}}
|
|
onCancel={() => {
|
|
setDialogOpen(false);
|
|
}}
|
|
/>
|
|
|
|
<div className="flex flex-row justify-between items-center mb-4 px-[0.8rem]">
|
|
<Select
|
|
value={`${sortByValue},${sortOrderValue}`}
|
|
onValueChange={handleSortChange}
|
|
>
|
|
<SelectTrigger className="w-[180px] cursor-pointer bg-[#ffffff] dark:bg-[#2a2a2e]">
|
|
<SelectValue placeholder={t('pipelines.sortBy')} />
|
|
</SelectTrigger>
|
|
<SelectContent className="bg-[#ffffff] dark:bg-[#2a2a2e]">
|
|
<SelectItem
|
|
value="created_at,DESC"
|
|
className="text-gray-900 dark:text-gray-100"
|
|
>
|
|
{t('pipelines.newestCreated')}
|
|
</SelectItem>
|
|
<SelectItem
|
|
value="created_at,ASC"
|
|
className="text-gray-900 dark:text-gray-100"
|
|
>
|
|
{t('pipelines.earliestCreated')}
|
|
</SelectItem>
|
|
<SelectItem
|
|
value="updated_at,DESC"
|
|
className="text-gray-900 dark:text-gray-100"
|
|
>
|
|
{t('pipelines.recentlyEdited')}
|
|
</SelectItem>
|
|
<SelectItem
|
|
value="updated_at,ASC"
|
|
className="text-gray-900 dark:text-gray-100"
|
|
>
|
|
{t('pipelines.earliestEdited')}
|
|
</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
<div className={styles.pipelineListContainer}>
|
|
<CreateCardComponent
|
|
width={'100%'}
|
|
height={'10rem'}
|
|
plusSize={'90px'}
|
|
onClick={handleCreateNew}
|
|
/>
|
|
|
|
{pipelineList.map((pipeline) => {
|
|
return (
|
|
<div
|
|
key={pipeline.id}
|
|
onClick={() => handlePipelineClick(pipeline.id)}
|
|
>
|
|
<PipelineCard cardVO={pipeline} />
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|