AiBot Tenantlı ve MenuManager düzenlendi

This commit is contained in:
Sedat ÖZTÜRK 2026-07-13 16:51:13 +03:00
parent 50cb3eaf97
commit b53a4243ab
4 changed files with 108 additions and 12 deletions

View file

@ -6,6 +6,7 @@ using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Repositories;
using System.Threading.Tasks;
using System.Linq;
using System.Collections.Generic;
namespace Sozsoft.Platform.AiBots;
@ -25,16 +26,31 @@ public class AiBotAppService : CrudAppService<
{
await CheckGetListPolicyAsync();
var query = await CreateFilteredQueryAsync(input);
if (CurrentTenant.IsAvailable && !string.IsNullOrWhiteSpace(CurrentTenant.Name))
{
var tenantName = CurrentTenant.Name;
query = query.Where(aiBot =>
string.IsNullOrWhiteSpace(aiBot.Tenants) ||
aiBot.Tenants.Contains($"\"{tenantName}\""));
List<AiBot> tenantEntities;
using (CurrentTenant.Change(null))
{
var hostQuery = await CreateFilteredQueryAsync(input);
tenantEntities = await AsyncExecuter.ToListAsync(hostQuery);
}
var filteredQuery = tenantEntities
.Where(aiBot => IsAvailableForTenant(aiBot.Tenants, tenantName))
.AsQueryable();
var tenantTotalCount = filteredQuery.LongCount();
filteredQuery = ApplySorting(filteredQuery, input);
filteredQuery = ApplyPaging(filteredQuery, input);
var tenantDtos = await MapToGetListOutputDtosAsync(filteredQuery.ToList());
return new PagedResultDto<AiBotDto>(tenantTotalCount, tenantDtos);
}
var query = await CreateFilteredQueryAsync(input);
var totalCount = await AsyncExecuter.CountAsync(query);
query = ApplySorting(query, input);
query = ApplyPaging(query, input);
@ -44,5 +60,17 @@ public class AiBotAppService : CrudAppService<
return new PagedResultDto<AiBotDto>(totalCount, dtos);
}
private static bool IsAvailableForTenant(string tenants, string tenantName)
{
if (string.IsNullOrWhiteSpace(tenants))
{
return true;
}
return tenants
.Split('|', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
.Any(name => string.Equals(name, tenantName, StringComparison.OrdinalIgnoreCase));
}
}

View file

@ -1,4 +1,4 @@
import React, { useState } from 'react'
import React, { useEffect, useState } from 'react'
import { useSortable } from '@dnd-kit/sortable'
import { CSS } from '@dnd-kit/utilities'
import { MenuItem } from '@/proxy/menus/menu'
@ -19,7 +19,7 @@ import {
import { Field, FieldProps, Form, Formik } from 'formik'
import { SelectBoxOption } from '@/types/shared'
import * as Yup from 'yup'
import { FaQuestionCircle, FaPlus, FaTrashAlt } from 'react-icons/fa'
import { FaChevronDown, FaChevronRight, FaQuestionCircle, FaPlus, FaTrashAlt } from 'react-icons/fa'
import { MenuDto } from '@/proxy/menus/models'
interface MenuItemComponentProps {
@ -30,6 +30,7 @@ interface MenuItemComponentProps {
isDragOverlay?: boolean
refetch: () => void
permissions: SelectBoxOption[]
expansionCommand: { expanded: boolean; version: number }
}
export const MenuItemComponent: React.FC<MenuItemComponentProps> = ({
@ -40,6 +41,7 @@ export const MenuItemComponent: React.FC<MenuItemComponentProps> = ({
isDragOverlay = false,
refetch,
permissions,
expansionCommand,
}) => {
const { translate } = useLocalization()
@ -75,6 +77,10 @@ export const MenuItemComponent: React.FC<MenuItemComponentProps> = ({
const [isModalOpen, setIsModalOpen] = useState(false)
const [modalMode, setModalMode] = useState<'create' | 'edit'>('create')
useEffect(() => {
setIsExpanded(expansionCommand.expanded)
}, [expansionCommand.expanded, expansionCommand.version])
const getCreateInitialValues = (): Partial<MenuDto> => ({
code: '',
displayName: '',
@ -106,8 +112,9 @@ export const MenuItemComponent: React.FC<MenuItemComponentProps> = ({
const [formData, setFormData] = useState<Partial<MenuDto>>(getCreateInitialValues())
const toggleExpanded = () => {
if (!isDesignMode) setIsExpanded(!isExpanded)
const toggleExpanded = (event?: React.MouseEvent) => {
event?.stopPropagation()
setIsExpanded((current) => !current)
}
const openCreateModal = () => {
@ -146,6 +153,23 @@ export const MenuItemComponent: React.FC<MenuItemComponentProps> = ({
`}
{...(isDesignMode ? { ...attributes, ...listeners } : { onClick: toggleExpanded })}
>
{isDesignMode &&
(item.children && item.children.length > 0 ? (
<Button
aria-label={isExpanded ? 'Collapse menu item' : 'Expand menu item'}
className="!h-6 !w-6 !px-0"
icon={isExpanded ? <FaChevronDown size={12} /> : <FaChevronRight size={12} />}
shape="circle"
size="xs"
title={isExpanded ? 'Collapse' : 'Expand'}
variant="plain"
onPointerDown={(event) => event.stopPropagation()}
onClick={toggleExpanded}
/>
) : (
<span aria-hidden="true" className="h-6 w-6 flex-shrink-0" />
))}
{isDesignMode && (
<div className="flex gap-2 items-center mr-2">
<Button
@ -220,7 +244,7 @@ export const MenuItemComponent: React.FC<MenuItemComponentProps> = ({
</div>
</div>
{children && (!isDesignMode ? isExpanded : true) && <div className="mt-1">{children}</div>}
{children && isExpanded && <div className="mt-1">{children}</div>}
{isModalOpen && (
<Dialog

View file

@ -1,7 +1,14 @@
import { useState } from 'react'
import { SortableMenuTree } from './SortableMenuTree'
import { useMenuData } from '@/utils/hooks/useMenuData'
import { FaRegBell, FaSpinner, FaBars, FaRegSave } from 'react-icons/fa'
import {
FaRegBell,
FaSpinner,
FaBars,
FaRegSave,
FaChevronRight,
FaChevronDown,
} from 'react-icons/fa'
import { Container } from '@/components/shared'
import { Helmet } from 'react-helmet'
import { useLocalization } from '@/utils/hooks/useLocalization'
@ -12,6 +19,7 @@ export const MenuManager = () => {
const { menuItems, setMenuItems, loading, error, refetch, saveMenuData } = useMenuData()
const [isDesignMode, setIsDesignMode] = useState(true)
const [isSaving, setIsSaving] = useState(false)
const [expansionCommand, setExpansionCommand] = useState({ expanded: true, version: 0 })
const { translate } = useLocalization()
const handleSave = async () => {
@ -33,6 +41,10 @@ export const MenuManager = () => {
setIsDesignMode((current) => !current)
}
const setAllExpanded = (expanded: boolean) => {
setExpansionCommand((current) => ({ expanded, version: current.version + 1 }))
}
if (loading) {
return (
<div className="min-h-screen bg-gray-50 dark:bg-gray-900 flex items-center justify-center">
@ -113,6 +125,27 @@ export const MenuManager = () => {
</Button>
</div>
<div className="flex items-center gap-1">
<Button
size="sm"
variant="plain"
className="!h-auto !rounded-lg !px-2 !py-1"
icon={<FaChevronRight size={10} />}
onClick={() => setAllExpanded(true)}
>
{translate('::ListForms.ListFormEdit.ExpandAll')}
</Button>
<Button
size="sm"
variant="plain"
className="!h-auto !rounded-lg !px-2 !py-1"
icon={<FaChevronDown size={10} />}
onClick={() => setAllExpanded(false)}
>
{translate('::ListForms.ListFormEdit.CollapseAll')}
</Button>
</div>
<Button
disabled={!isDesignMode || isSaving}
size="sm"
@ -141,6 +174,7 @@ export const MenuManager = () => {
{menuItems.length > 0 ? (
<SortableMenuTree
expansionCommand={expansionCommand}
items={menuItems}
isDesignMode={isDesignMode}
refetch={refetch}

View file

@ -26,6 +26,7 @@ interface SortableMenuTreeProps {
onItemsChange: (items: MenuItem[]) => void
isDesignMode: boolean
refetch: () => void
expansionCommand: { expanded: boolean; version: number }
}
export const SortableMenuTree: React.FC<SortableMenuTreeProps> = ({
@ -33,6 +34,7 @@ export const SortableMenuTree: React.FC<SortableMenuTreeProps> = ({
onItemsChange,
isDesignMode,
refetch,
expansionCommand,
}) => {
const [permissions, setPermissions] = useState<SelectBoxOption[]>([])
const [activeItem, setActiveItem] = React.useState<MenuItem | null>(null)
@ -193,7 +195,14 @@ export const SortableMenuTree: React.FC<SortableMenuTreeProps> = ({
const renderMenuItem = (item: MenuItem, depth: number = 0): React.ReactNode => {
return (
<div key={item.id} className="bg-white dark:bg-gray-800 rounded-md">
<MenuItemComponent item={item} isDesignMode={isDesignMode} depth={depth} refetch={refetch} permissions={permissions}>
<MenuItemComponent
item={item}
isDesignMode={isDesignMode}
depth={depth}
refetch={refetch}
permissions={permissions}
expansionCommand={expansionCommand}
>
{Array.isArray(item.children) && item.children.length > 0 && (
<SortableContext
items={item.children
@ -230,6 +239,7 @@ export const SortableMenuTree: React.FC<SortableMenuTreeProps> = ({
isDragOverlay={true}
refetch={refetch}
permissions={permissions}
expansionCommand={expansionCommand}
/>
</div>
) : null}