feat:Update pnpm-lock.yaml to link shared package; modify API routes to utilize new schemas for domain and service management; enhance DNS record handling with CNAME support; refactor service and subdomain routes for improved functionality; implement confirm dialog for domain deletion in the frontend; clean up unused components and improve UI consistency.
Build, Test, and Push CFDM Docker Image / test (push) Failing after 51s
Build, Test, and Push CFDM Docker Image / build-and-push (push) Has been skipped
Build, Test, and Push CFDM Docker Image / update-wiki (push) Has been skipped
Build, Test, and Push CFDM Docker Image / create-release (push) Has been skipped
Build, Test, and Push CFDM Docker Image / test (push) Failing after 51s
Build, Test, and Push CFDM Docker Image / build-and-push (push) Has been skipped
Build, Test, and Push CFDM Docker Image / update-wiki (push) Has been skipped
Build, Test, and Push CFDM Docker Image / create-release (push) Has been skipped
This commit is contained in:
@@ -0,0 +1,193 @@
|
||||
import { useEffect } from 'react'
|
||||
import { useDroppable } from '@dnd-kit/core'
|
||||
import {
|
||||
SortableContext,
|
||||
verticalListSortingStrategy,
|
||||
} from '@dnd-kit/sortable'
|
||||
import { PlusIcon } from 'lucide-react'
|
||||
import { ServiceGroupHeader } from '@/components/services-board/service-group-header'
|
||||
import { ServiceRow } from '@/components/services-board/service-row'
|
||||
import type { BoardColumn } from '@/components/services-board/types'
|
||||
import type { ServiceGroupView, ServiceView } from '@/lib/schemas'
|
||||
import {
|
||||
AccordionContent,
|
||||
AccordionItem,
|
||||
} from '@cfdm/ui/components/accordion'
|
||||
import { Button } from '@cfdm/ui/components/button'
|
||||
import {
|
||||
Empty,
|
||||
EmptyContent,
|
||||
EmptyDescription,
|
||||
EmptyHeader,
|
||||
EmptyTitle,
|
||||
} from '@cfdm/ui/components/empty'
|
||||
import { ItemGroup, ItemSeparator } from '@cfdm/ui/components/item'
|
||||
import { cn } from '@cfdm/ui/lib/utils'
|
||||
|
||||
interface ServiceGroupItemProps {
|
||||
column: BoardColumn
|
||||
isOpen?: boolean
|
||||
isDragging?: boolean
|
||||
onExpandColumn?: (columnId: string) => void
|
||||
onGroupToggle?: (groupId: number, enabled: boolean) => void
|
||||
onEditGroup?: (group: ServiceGroupView) => void
|
||||
onDeleteGroup?: (group: ServiceGroupView) => void
|
||||
onAddService?: (groupId: number | null) => void
|
||||
onServiceToggle: (serviceId: number, enabled: boolean) => void
|
||||
onEditService: (service: ServiceView) => void
|
||||
onDeleteService: (service: ServiceView) => void
|
||||
togglingGroupId?: number | null
|
||||
togglingServiceId?: number | null
|
||||
dragDisabled?: boolean
|
||||
showCheckbox?: boolean
|
||||
isSelected?: (id: number) => boolean
|
||||
onSelectedChange?: (id: number, selected: boolean) => void
|
||||
isAllSelected?: (ids: number[]) => boolean
|
||||
isSomeSelected?: (ids: number[]) => boolean
|
||||
onSelectAllInGroup?: (ids: number[]) => void
|
||||
onDeselectAllInGroup?: (ids: number[]) => void
|
||||
}
|
||||
|
||||
export function ServiceGroupItem({
|
||||
column,
|
||||
isOpen = true,
|
||||
isDragging = false,
|
||||
onExpandColumn,
|
||||
onGroupToggle,
|
||||
onEditGroup,
|
||||
onDeleteGroup,
|
||||
onAddService,
|
||||
onServiceToggle,
|
||||
onEditService,
|
||||
onDeleteService,
|
||||
togglingGroupId = null,
|
||||
togglingServiceId = null,
|
||||
dragDisabled = false,
|
||||
showCheckbox = false,
|
||||
isSelected,
|
||||
onSelectedChange,
|
||||
isAllSelected,
|
||||
isSomeSelected,
|
||||
onSelectAllInGroup,
|
||||
onDeselectAllInGroup,
|
||||
}: ServiceGroupItemProps) {
|
||||
const { setNodeRef, isOver } = useDroppable({
|
||||
id: column.id,
|
||||
disabled: dragDisabled,
|
||||
})
|
||||
const isGroupToggling = column.groupId !== null && togglingGroupId === column.groupId
|
||||
const serviceDisabled = Boolean(column.domain) && !column.enabled
|
||||
const serviceIds = column.items.map((s) => s.id)
|
||||
const groupAllSelected = isAllSelected?.(serviceIds) ?? false
|
||||
const groupSomeSelected = isSomeSelected?.(serviceIds) ?? false
|
||||
|
||||
useEffect(() => {
|
||||
if (isOver && isDragging && !dragDisabled) {
|
||||
onExpandColumn?.(column.id)
|
||||
}
|
||||
}, [isOver, isDragging, dragDisabled, column.id, onExpandColumn])
|
||||
|
||||
return (
|
||||
<AccordionItem
|
||||
value={column.id}
|
||||
className={cn(
|
||||
'not-last:border-b-0 overflow-hidden rounded-lg border border-border transition-colors',
|
||||
!isOpen && 'bg-muted/20',
|
||||
isOpen && 'bg-muted/30',
|
||||
isOver && !dragDisabled && 'ring-2 ring-primary/30',
|
||||
)}
|
||||
>
|
||||
<ServiceGroupHeader
|
||||
column={column}
|
||||
isOpen={isOpen}
|
||||
isDragging={isDragging}
|
||||
dragDisabled={dragDisabled}
|
||||
showCheckbox={showCheckbox}
|
||||
groupAllSelected={groupAllSelected}
|
||||
groupSomeSelected={groupSomeSelected}
|
||||
serviceIds={serviceIds}
|
||||
isGroupToggling={isGroupToggling}
|
||||
onSelectAllInGroup={onSelectAllInGroup}
|
||||
onDeselectAllInGroup={onDeselectAllInGroup}
|
||||
onGroupToggle={onGroupToggle}
|
||||
onEditGroup={onEditGroup}
|
||||
onDeleteGroup={onDeleteGroup}
|
||||
/>
|
||||
|
||||
<AccordionContent className="px-1 pb-2">
|
||||
<div ref={setNodeRef}>
|
||||
<SortableContext
|
||||
items={column.items.map((service) => String(service.id))}
|
||||
strategy={verticalListSortingStrategy}
|
||||
>
|
||||
{column.items.length > 0 ? (
|
||||
<ItemGroup className="gap-0 py-1">
|
||||
{column.items.map((service, index) => (
|
||||
<div key={service.id}>
|
||||
{index > 0 ? <ItemSeparator className="my-0" /> : null}
|
||||
<ServiceRow
|
||||
service={service}
|
||||
onToggle={onServiceToggle}
|
||||
onEdit={onEditService}
|
||||
onDelete={onDeleteService}
|
||||
isToggling={togglingServiceId === service.id}
|
||||
disabled={serviceDisabled}
|
||||
dragDisabled={dragDisabled}
|
||||
showCheckbox={showCheckbox}
|
||||
selected={isSelected?.(service.id) ?? false}
|
||||
onSelectedChange={(selected) =>
|
||||
onSelectedChange?.(service.id, selected)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</ItemGroup>
|
||||
) : (
|
||||
<Empty
|
||||
className={cn(
|
||||
'border border-dashed py-2',
|
||||
isOver && !dragDisabled && 'border-primary bg-primary/5',
|
||||
)}
|
||||
>
|
||||
<EmptyHeader>
|
||||
<EmptyTitle className="text-sm">Нет сервисов в группе</EmptyTitle>
|
||||
<EmptyDescription>
|
||||
{dragDisabled
|
||||
? 'Сервисы не найдены'
|
||||
: 'Перетащите сервис сюда или добавьте новый'}
|
||||
</EmptyDescription>
|
||||
</EmptyHeader>
|
||||
{onAddService ? (
|
||||
<EmptyContent>
|
||||
<Button
|
||||
type="button"
|
||||
variant="link"
|
||||
size="sm"
|
||||
onClick={() => onAddService(column.groupId)}
|
||||
>
|
||||
<PlusIcon data-icon="inline-start" />
|
||||
Добавить сервис
|
||||
</Button>
|
||||
</EmptyContent>
|
||||
) : null}
|
||||
</Empty>
|
||||
)}
|
||||
</SortableContext>
|
||||
|
||||
{column.items.length > 0 && onAddService ? (
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="mt-1 w-full justify-start text-muted-foreground"
|
||||
onClick={() => onAddService(column.groupId)}
|
||||
>
|
||||
<PlusIcon data-icon="inline-start" />
|
||||
Добавить сервис
|
||||
</Button>
|
||||
) : null}
|
||||
</div>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user