AiBot Tenantlı ve MenuManager düzenlendi
This commit is contained in:
parent
50cb3eaf97
commit
b53a4243ab
4 changed files with 108 additions and 12 deletions
|
|
@ -6,6 +6,7 @@ using Volo.Abp.Application.Services;
|
||||||
using Volo.Abp.Domain.Repositories;
|
using Volo.Abp.Domain.Repositories;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Sozsoft.Platform.AiBots;
|
namespace Sozsoft.Platform.AiBots;
|
||||||
|
|
||||||
|
|
@ -25,16 +26,31 @@ public class AiBotAppService : CrudAppService<
|
||||||
{
|
{
|
||||||
await CheckGetListPolicyAsync();
|
await CheckGetListPolicyAsync();
|
||||||
|
|
||||||
var query = await CreateFilteredQueryAsync(input);
|
|
||||||
|
|
||||||
if (CurrentTenant.IsAvailable && !string.IsNullOrWhiteSpace(CurrentTenant.Name))
|
if (CurrentTenant.IsAvailable && !string.IsNullOrWhiteSpace(CurrentTenant.Name))
|
||||||
{
|
{
|
||||||
var tenantName = CurrentTenant.Name;
|
var tenantName = CurrentTenant.Name;
|
||||||
query = query.Where(aiBot =>
|
List<AiBot> tenantEntities;
|
||||||
string.IsNullOrWhiteSpace(aiBot.Tenants) ||
|
|
||||||
aiBot.Tenants.Contains($"\"{tenantName}\""));
|
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);
|
var totalCount = await AsyncExecuter.CountAsync(query);
|
||||||
query = ApplySorting(query, input);
|
query = ApplySorting(query, input);
|
||||||
query = ApplyPaging(query, input);
|
query = ApplyPaging(query, input);
|
||||||
|
|
@ -44,5 +60,17 @@ public class AiBotAppService : CrudAppService<
|
||||||
|
|
||||||
return new PagedResultDto<AiBotDto>(totalCount, dtos);
|
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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useState } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
import { useSortable } from '@dnd-kit/sortable'
|
import { useSortable } from '@dnd-kit/sortable'
|
||||||
import { CSS } from '@dnd-kit/utilities'
|
import { CSS } from '@dnd-kit/utilities'
|
||||||
import { MenuItem } from '@/proxy/menus/menu'
|
import { MenuItem } from '@/proxy/menus/menu'
|
||||||
|
|
@ -19,7 +19,7 @@ import {
|
||||||
import { Field, FieldProps, Form, Formik } from 'formik'
|
import { Field, FieldProps, Form, Formik } from 'formik'
|
||||||
import { SelectBoxOption } from '@/types/shared'
|
import { SelectBoxOption } from '@/types/shared'
|
||||||
import * as Yup from 'yup'
|
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'
|
import { MenuDto } from '@/proxy/menus/models'
|
||||||
|
|
||||||
interface MenuItemComponentProps {
|
interface MenuItemComponentProps {
|
||||||
|
|
@ -30,6 +30,7 @@ interface MenuItemComponentProps {
|
||||||
isDragOverlay?: boolean
|
isDragOverlay?: boolean
|
||||||
refetch: () => void
|
refetch: () => void
|
||||||
permissions: SelectBoxOption[]
|
permissions: SelectBoxOption[]
|
||||||
|
expansionCommand: { expanded: boolean; version: number }
|
||||||
}
|
}
|
||||||
|
|
||||||
export const MenuItemComponent: React.FC<MenuItemComponentProps> = ({
|
export const MenuItemComponent: React.FC<MenuItemComponentProps> = ({
|
||||||
|
|
@ -40,6 +41,7 @@ export const MenuItemComponent: React.FC<MenuItemComponentProps> = ({
|
||||||
isDragOverlay = false,
|
isDragOverlay = false,
|
||||||
refetch,
|
refetch,
|
||||||
permissions,
|
permissions,
|
||||||
|
expansionCommand,
|
||||||
}) => {
|
}) => {
|
||||||
const { translate } = useLocalization()
|
const { translate } = useLocalization()
|
||||||
|
|
||||||
|
|
@ -75,6 +77,10 @@ export const MenuItemComponent: React.FC<MenuItemComponentProps> = ({
|
||||||
const [isModalOpen, setIsModalOpen] = useState(false)
|
const [isModalOpen, setIsModalOpen] = useState(false)
|
||||||
const [modalMode, setModalMode] = useState<'create' | 'edit'>('create')
|
const [modalMode, setModalMode] = useState<'create' | 'edit'>('create')
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setIsExpanded(expansionCommand.expanded)
|
||||||
|
}, [expansionCommand.expanded, expansionCommand.version])
|
||||||
|
|
||||||
const getCreateInitialValues = (): Partial<MenuDto> => ({
|
const getCreateInitialValues = (): Partial<MenuDto> => ({
|
||||||
code: '',
|
code: '',
|
||||||
displayName: '',
|
displayName: '',
|
||||||
|
|
@ -106,8 +112,9 @@ export const MenuItemComponent: React.FC<MenuItemComponentProps> = ({
|
||||||
|
|
||||||
const [formData, setFormData] = useState<Partial<MenuDto>>(getCreateInitialValues())
|
const [formData, setFormData] = useState<Partial<MenuDto>>(getCreateInitialValues())
|
||||||
|
|
||||||
const toggleExpanded = () => {
|
const toggleExpanded = (event?: React.MouseEvent) => {
|
||||||
if (!isDesignMode) setIsExpanded(!isExpanded)
|
event?.stopPropagation()
|
||||||
|
setIsExpanded((current) => !current)
|
||||||
}
|
}
|
||||||
|
|
||||||
const openCreateModal = () => {
|
const openCreateModal = () => {
|
||||||
|
|
@ -146,6 +153,23 @@ export const MenuItemComponent: React.FC<MenuItemComponentProps> = ({
|
||||||
`}
|
`}
|
||||||
{...(isDesignMode ? { ...attributes, ...listeners } : { onClick: toggleExpanded })}
|
{...(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 && (
|
{isDesignMode && (
|
||||||
<div className="flex gap-2 items-center mr-2">
|
<div className="flex gap-2 items-center mr-2">
|
||||||
<Button
|
<Button
|
||||||
|
|
@ -220,7 +244,7 @@ export const MenuItemComponent: React.FC<MenuItemComponentProps> = ({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{children && (!isDesignMode ? isExpanded : true) && <div className="mt-1">{children}</div>}
|
{children && isExpanded && <div className="mt-1">{children}</div>}
|
||||||
|
|
||||||
{isModalOpen && (
|
{isModalOpen && (
|
||||||
<Dialog
|
<Dialog
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,14 @@
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { SortableMenuTree } from './SortableMenuTree'
|
import { SortableMenuTree } from './SortableMenuTree'
|
||||||
import { useMenuData } from '@/utils/hooks/useMenuData'
|
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 { Container } from '@/components/shared'
|
||||||
import { Helmet } from 'react-helmet'
|
import { Helmet } from 'react-helmet'
|
||||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
|
|
@ -12,6 +19,7 @@ export const MenuManager = () => {
|
||||||
const { menuItems, setMenuItems, loading, error, refetch, saveMenuData } = useMenuData()
|
const { menuItems, setMenuItems, loading, error, refetch, saveMenuData } = useMenuData()
|
||||||
const [isDesignMode, setIsDesignMode] = useState(true)
|
const [isDesignMode, setIsDesignMode] = useState(true)
|
||||||
const [isSaving, setIsSaving] = useState(false)
|
const [isSaving, setIsSaving] = useState(false)
|
||||||
|
const [expansionCommand, setExpansionCommand] = useState({ expanded: true, version: 0 })
|
||||||
const { translate } = useLocalization()
|
const { translate } = useLocalization()
|
||||||
|
|
||||||
const handleSave = async () => {
|
const handleSave = async () => {
|
||||||
|
|
@ -33,6 +41,10 @@ export const MenuManager = () => {
|
||||||
setIsDesignMode((current) => !current)
|
setIsDesignMode((current) => !current)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const setAllExpanded = (expanded: boolean) => {
|
||||||
|
setExpansionCommand((current) => ({ expanded, version: current.version + 1 }))
|
||||||
|
}
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-gray-50 dark:bg-gray-900 flex items-center justify-center">
|
<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>
|
</Button>
|
||||||
</div>
|
</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
|
<Button
|
||||||
disabled={!isDesignMode || isSaving}
|
disabled={!isDesignMode || isSaving}
|
||||||
size="sm"
|
size="sm"
|
||||||
|
|
@ -141,6 +174,7 @@ export const MenuManager = () => {
|
||||||
|
|
||||||
{menuItems.length > 0 ? (
|
{menuItems.length > 0 ? (
|
||||||
<SortableMenuTree
|
<SortableMenuTree
|
||||||
|
expansionCommand={expansionCommand}
|
||||||
items={menuItems}
|
items={menuItems}
|
||||||
isDesignMode={isDesignMode}
|
isDesignMode={isDesignMode}
|
||||||
refetch={refetch}
|
refetch={refetch}
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ interface SortableMenuTreeProps {
|
||||||
onItemsChange: (items: MenuItem[]) => void
|
onItemsChange: (items: MenuItem[]) => void
|
||||||
isDesignMode: boolean
|
isDesignMode: boolean
|
||||||
refetch: () => void
|
refetch: () => void
|
||||||
|
expansionCommand: { expanded: boolean; version: number }
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SortableMenuTree: React.FC<SortableMenuTreeProps> = ({
|
export const SortableMenuTree: React.FC<SortableMenuTreeProps> = ({
|
||||||
|
|
@ -33,6 +34,7 @@ export const SortableMenuTree: React.FC<SortableMenuTreeProps> = ({
|
||||||
onItemsChange,
|
onItemsChange,
|
||||||
isDesignMode,
|
isDesignMode,
|
||||||
refetch,
|
refetch,
|
||||||
|
expansionCommand,
|
||||||
}) => {
|
}) => {
|
||||||
const [permissions, setPermissions] = useState<SelectBoxOption[]>([])
|
const [permissions, setPermissions] = useState<SelectBoxOption[]>([])
|
||||||
const [activeItem, setActiveItem] = React.useState<MenuItem | null>(null)
|
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 => {
|
const renderMenuItem = (item: MenuItem, depth: number = 0): React.ReactNode => {
|
||||||
return (
|
return (
|
||||||
<div key={item.id} className="bg-white dark:bg-gray-800 rounded-md">
|
<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 && (
|
{Array.isArray(item.children) && item.children.length > 0 && (
|
||||||
<SortableContext
|
<SortableContext
|
||||||
items={item.children
|
items={item.children
|
||||||
|
|
@ -230,6 +239,7 @@ export const SortableMenuTree: React.FC<SortableMenuTreeProps> = ({
|
||||||
isDragOverlay={true}
|
isDragOverlay={true}
|
||||||
refetch={refetch}
|
refetch={refetch}
|
||||||
permissions={permissions}
|
permissions={permissions}
|
||||||
|
expansionCommand={expansionCommand}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue