import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { Button, Checkbox, Combobox, Dialog, DialogActions, DialogBody, DialogContent, DialogSurface, DialogTitle, Field, Input, makeStyles, Option, shorthands, Table, TableBody, TableCell, TableHeader, TableHeaderCell, TableRow, Textarea, Tooltip, } from "@fluentui/react-components"; import { AddRegular, DeleteRegular, EditRegular, LinkDismissRegular, LinkRegular, OpenRegular } from "@fluentui/react-icons"; import { useState } from "react"; import { portalApi } from "../api/portalApi"; import { DataState } from "../components/DataState"; import { PageHeader } from "../components/PageHeader"; import type { Domain, Target } from "../types/portal"; import { Link } from "react-router-dom"; const useStyles = makeStyles({ toolbar: { display: "flex", justifyContent: "flex-start", marginBottom: "18px", }, form: { display: "grid", gap: "14px", }, actions: { display: "flex", gap: "4px", ...shorthands.padding("2px", "0"), }, value: { overflowWrap: "anywhere", }, }); type DialogMode = "add" | "edit" | null; export function TargetsPage() { const styles = useStyles(); const queryClient = useQueryClient(); const [dialogMode, setDialogMode] = useState(null); const [selectedTarget, setSelectedTarget] = useState(null); const [domainID, setDomainID] = useState(undefined); const [linkTargetId, setLinkTargetId] = useState(""); const [linkDomainId, setLinkDomainId] = useState(""); const [unlinkTargetId, setUnlinkTargetId] = useState(""); const [name, setName] = useState(""); const [targetType, setTargetType] = useState("VirtualMachine"); const [providerType, setProviderType] = useState("OnPrem"); const [externalId, setExternalId] = useState(""); const [metadataJson, setMetadataJson] = useState(""); const [selectedIds, setSelectedIds] = useState([]); const { data, error, isLoading } = useQuery({ queryKey: ["targets"], queryFn: ({ signal }) => portalApi.getTargets(signal), }); const { data: domains, error: domainsError, isLoading: domainsLoading } = useQuery({ queryKey: ["domains"], queryFn: ({ signal }) => portalApi.getDomains(signal), }); const closeDialog = () => { setDialogMode(null); setSelectedTarget(null); setDomainID(undefined); setName(""); setTargetType("VirtualMachine"); setProviderType("OnPrem"); setExternalId(""); setMetadataJson(""); }; const openAddDialog = () => { setSelectedTarget(null); setDomainID(undefined); setName(""); setTargetType("VirtualMachine"); setProviderType("OnPrem"); setExternalId(""); setMetadataJson(""); setDialogMode("add"); }; const openTargetDialog = (mode: "edit", target: Target) => { setSelectedTarget(target); setDomainID(target.domainID); setName(target.name); setTargetType(target.targetType ?? "VirtualMachine"); setProviderType(target.providerType ?? "OnPrem"); setExternalId(target.externalId ?? ""); setMetadataJson(target.metadataJson ?? ""); setDialogMode(mode); }; const addTarget = useMutation({ mutationFn: portalApi.addTarget, onSuccess: async () => { closeDialog(); await queryClient.invalidateQueries({ queryKey: ["targets"] }); }, }); const updateTarget = useMutation({ mutationFn: ({ id, target }: { id: string; target: { name: string; targetType: string; providerType: string; externalId?: string; metadataJson?: string } }) => portalApi.updateTarget(id, target), onSuccess: async () => { closeDialog(); await queryClient.invalidateQueries({ queryKey: ["targets"] }); }, }); const deleteTarget = useMutation({ mutationFn: portalApi.deleteTarget, onSuccess: async () => { await queryClient.invalidateQueries({ queryKey: ["targets"] }); }, }); const deleteSelectedTargets = useMutation({ mutationFn: async (ids: string[]) => { await Promise.all(ids.map((id) => portalApi.deleteTarget(id))); }, onSuccess: async () => { setSelectedIds([]); await queryClient.invalidateQueries({ queryKey: ["targets"] }); }, }); const linkTargetToDomain = useMutation({ mutationFn: ({ targetId, targetDomainId }: { targetId: string; targetDomainId: string }) => portalApi.linkTargetToDomain(targetId, targetDomainId), onSuccess: async () => { setLinkTargetId(""); setLinkDomainId(""); await queryClient.invalidateQueries({ queryKey: ["targets"] }); }, }); const unlinkTargetFromDomain = useMutation({ mutationFn: (targetId: string) => portalApi.unlinkTargetFromDomain(targetId), onSuccess: async () => { setUnlinkTargetId(""); await queryClient.invalidateQueries({ queryKey: ["targets"] }); }, }); const submitTarget = (event: React.FormEvent) => { event.preventDefault(); const target = { domainID, name, targetType, providerType, externalId, metadataJson }; if (dialogMode === "edit" && selectedTarget) { updateTarget.mutate({ id: selectedTarget.id, target: { name, targetType, providerType, externalId, metadataJson } }); return; } addTarget.mutate(target); }; const formError = addTarget.error?.message ?? updateTarget.error?.message ?? linkTargetToDomain.error?.message ?? unlinkTargetFromDomain.error?.message; const isSaving = addTarget.isPending || updateTarget.isPending; const domainNameById = new Map((domains ?? []).map((domain: Domain) => [domain.id, domain.name])); const linkedTargets = (data ?? []).filter((target) => Boolean(target.domainID)); const unlinkedTargets = (data ?? []).filter((target) => !target.domainID); const allSelected = Boolean(data?.length) && data!.every((target) => selectedIds.includes(target.id)); const toggleSelected = (id: string, checked: boolean) => { setSelectedIds((current) => (checked ? [...new Set([...current, id])] : current.filter((entry) => entry !== id))); }; return ( <>
!dialogData.open && closeDialog()}>
{dialogMode === "edit" ? "Target aendern" : "Target hinzufuegen"} setDomainID(optionData.optionValue || undefined)} > {(domains ?? []).map((domain) => ( ))} setName(inputData.value)} /> setTargetType(optionData.optionValue ?? "VirtualMachine")} > {["VirtualMachine", "Tenant", "Subscription", "ResourceGroup", "User", "Group", "Site", "PolicyScope"].map((type) => ( ))} setProviderType(optionData.optionValue ?? "OnPrem")} > {["OnPrem", "Azure", "Microsoft365", "ExchangeOnline", "Teams"].map((provider) => ( ))} setExternalId(inputData.value)} />