feat: add pipeline sorting functionality with three sort options (#1582)

* feat: add pipeline sorting functionality with three sort options

Co-Authored-By: Junyan Qin <Chin>, u79E6u9A8Fu8A00 in Chinese, you can call me my english name Rock Chin. <rockchinq@gmail.com>

* perf: ui

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Junyan Qin <Chin>, u79E6u9A8Fu8A00 in Chinese, you can call me my english name Rock Chin. <rockchinq@gmail.com>
This commit is contained in:
devin-ai-integration[bot]
2025-07-20 17:23:30 +08:00
committed by GitHub
parent 28ce986a8c
commit ea51cec57e
7 changed files with 81 additions and 8 deletions

View File

@@ -11,7 +11,9 @@ class PipelinesRouterGroup(group.RouterGroup):
@self.route('', methods=['GET', 'POST'])
async def _() -> str:
if quart.request.method == 'GET':
return self.success(data={'pipelines': await self.ap.pipeline_service.get_pipelines()})
sort_by = quart.request.args.get('sort_by', 'created_at')
sort_order = quart.request.args.get('sort_order', 'DESC')
return self.success(data={'pipelines': await self.ap.pipeline_service.get_pipelines(sort_by, sort_order)})
elif quart.request.method == 'POST':
json_data = await quart.request.json

View File

@@ -38,9 +38,21 @@ class PipelineService:
self.ap.pipeline_config_meta_output.data,
]
async def get_pipelines(self) -> list[dict]:
result = await self.ap.persistence_mgr.execute_async(sqlalchemy.select(persistence_pipeline.LegacyPipeline))
async def get_pipelines(self, sort_by: str = 'created_at', sort_order: str = 'DESC') -> list[dict]:
query = sqlalchemy.select(persistence_pipeline.LegacyPipeline)
if sort_by == 'created_at':
if sort_order == 'DESC':
query = query.order_by(persistence_pipeline.LegacyPipeline.created_at.desc())
else:
query = query.order_by(persistence_pipeline.LegacyPipeline.created_at.asc())
elif sort_by == 'updated_at':
if sort_order == 'DESC':
query = query.order_by(persistence_pipeline.LegacyPipeline.updated_at.desc())
else:
query = query.order_by(persistence_pipeline.LegacyPipeline.updated_at.asc())
result = await self.ap.persistence_mgr.execute_async(query)
pipelines = result.all()
return [
self.ap.persistence_mgr.serialize_model(persistence_pipeline.LegacyPipeline, pipeline)

View File

@@ -9,6 +9,13 @@ 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';
export default function PluginConfigPage() {
const { t } = useTranslation();
@@ -26,14 +33,19 @@ export default function PluginConfigPage() {
});
const [selectedPipelineIsDefault, setSelectedPipelineIsDefault] =
useState(false);
const [sortByValue, setSortByValue] = useState<string>('created_at');
const [sortOrderValue, setSortOrderValue] = useState<string>('DESC');
useEffect(() => {
getPipelines();
}, []);
function getPipelines() {
function getPipelines(
sortBy: string = sortByValue,
sortOrder: string = sortOrderValue,
) {
httpClient
.getPipelines()
.getPipelines(sortBy, sortOrder)
.then((value) => {
const currentTime = new Date();
const pipelineList = value.pipelines.map((pipeline) => {
@@ -106,6 +118,13 @@ export default function PluginConfigPage() {
setDialogOpen(true);
};
function handleSortChange(value: string) {
const [newSortBy, newSortOrder] = value.split(',').map((s) => s.trim());
setSortByValue(newSortBy);
setSortOrderValue(newSortOrder);
getPipelines(newSortBy, newSortOrder);
}
return (
<div className={styles.configPageContainer}>
<PipelineDialog
@@ -134,6 +153,27 @@ export default function PluginConfigPage() {
}}
/>
<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-white dark:bg-gray-800">
<SelectValue placeholder={t('pipelines.sortBy')} />
</SelectTrigger>
<SelectContent>
<SelectItem value="created_at,DESC">
{t('pipelines.newestCreated')}
</SelectItem>
<SelectItem value="updated_at,DESC">
{t('pipelines.recentlyEdited')}
</SelectItem>
<SelectItem value="updated_at,ASC">
{t('pipelines.earliestEdited')}
</SelectItem>
</SelectContent>
</Select>
</div>
<div className={styles.pipelineListContainer}>
<CreateCardComponent
width={'100%'}

View File

@@ -323,8 +323,15 @@ class HttpClient {
return this.get('/api/v1/pipelines/_/metadata');
}
public getPipelines(): Promise<ApiRespPipelines> {
return this.get('/api/v1/pipelines');
public getPipelines(
sortBy?: string,
sortOrder?: string,
): Promise<ApiRespPipelines> {
const params = new URLSearchParams();
if (sortBy) params.append('sort_by', sortBy);
if (sortOrder) params.append('sort_order', sortOrder);
const queryString = params.toString();
return this.get(`/api/v1/pipelines${queryString ? `?${queryString}` : ''}`);
}
public getPipeline(uuid: string): Promise<GetPipelineResponseData> {

View File

@@ -194,6 +194,10 @@ const enUS = {
today: 'Today',
updateTime: 'Updated ',
defaultBadge: 'Default',
sortBy: 'Sort by',
newestCreated: 'Newest Created',
recentlyEdited: 'Recently Edited',
earliestEdited: 'Earliest Edited',
basicInfo: 'Basic',
aiCapabilities: 'AI',
triggerConditions: 'Trigger',

View File

@@ -195,6 +195,10 @@ const jaJP = {
today: '今日',
updateTime: '更新日時',
defaultBadge: 'デフォルト',
sortBy: '並び順',
newestCreated: '最新作成',
recentlyEdited: '最近編集',
earliestEdited: '最古編集',
basicInfo: '基本情報',
aiCapabilities: 'AI機能',
triggerConditions: 'トリガー条件',

View File

@@ -189,6 +189,10 @@ const zhHans = {
today: '今天',
updateTime: '更新于',
defaultBadge: '默认',
sortBy: '排序方式',
newestCreated: '最新创建',
recentlyEdited: '最近编辑',
earliestEdited: '最早编辑',
basicInfo: '基础信息',
aiCapabilities: 'AI 能力',
triggerConditions: '触发条件',