chore(ui): Merge UI repository

This commit is contained in:
Pedro De Castro committed 2024-11-25 13:15:14 +01:00
commit 725fcf80aa
274 files changed
+33204

No files matched your search

+82
View File
@@ -0,0 +1,82 @@
"use client";
import { SwitchProps, useSwitch } from "@nextui-org/react";
import { useIsSSR } from "@react-aria/ssr";
import { VisuallyHidden } from "@react-aria/visually-hidden";
import clsx from "clsx";
import { useTheme } from "next-themes";
import { FC } from "react";
import React from "react";
import { MoonFilledIcon, SunFilledIcon } from "./icons";
export interface ThemeSwitchProps {
className?: string;
classNames?: SwitchProps["classNames"];
}
export const ThemeSwitch: FC<ThemeSwitchProps> = ({
className,
classNames,
}) => {
const { theme, setTheme } = useTheme();
const isSSR = useIsSSR();
const onChange = () => {
theme === "light" ? setTheme("dark") : setTheme("light");
};
const {
Component,
slots,
isSelected,
getBaseProps,
getInputProps,
getWrapperProps,
} = useSwitch({
isSelected: theme === "light" || isSSR,
"aria-label": `Switch to ${theme === "light" || isSSR ? "dark" : "light"} mode`,
onChange,
});
return (
<Component
{...getBaseProps({
className: clsx(
"px-px transition-opacity hover:opacity-80 cursor-pointer",
className,
classNames?.base,
),
})}
>
<VisuallyHidden>
<input {...getInputProps()} />
</VisuallyHidden>
<div
{...getWrapperProps()}
className={slots.wrapper({
class: clsx(
[
"h-auto w-auto",
"bg-transparent",
"rounded-lg",
"flex items-center justify-center",
"group-data-[selected=true]:bg-transparent",
"!text-default-500",
"pt-px",
"px-0",
"mx-0",
],
classNames?.wrapper,
),
})}
>
{!isSelected || isSSR ? (
<SunFilledIcon size={22} />
) : (
<MoonFilledIcon size={22} />
)}
</div>
</Component>
);
};
+318
View File
@@ -0,0 +1,318 @@
"use client";
import { zodResolver } from "@hookform/resolvers/zod";
import { Icon } from "@iconify/react";
import { Button, Checkbox, Divider, Link } from "@nextui-org/react";
import { useRouter } from "next/navigation";
import { useForm } from "react-hook-form";
import { z } from "zod";
import { authenticate, createNewUser } from "@/actions/auth";
import { NotificationIcon, ProwlerExtended } from "@/components/icons";
import { ThemeSwitch } from "@/components/ThemeSwitch";
import { useToast } from "@/components/ui";
import { CustomButton, CustomInput } from "@/components/ui/custom";
import {
Form,
FormControl,
FormField,
FormMessage,
} from "@/components/ui/form";
import { ApiError, authFormSchema } from "@/types";
export const AuthForm = ({
type,
invitationToken,
}: {
type: string;
invitationToken?: string | null;
}) => {
const formSchema = authFormSchema(type);
const router = useRouter();
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
email: "",
password: "",
...(type === "sign-up" && {
name: "",
company: "",
termsAndConditions: false,
confirmPassword: "",
...(invitationToken && { invitationToken }),
}),
},
});
const isLoading = form.formState.isSubmitting;
const { toast } = useToast();
const onSubmit = async (data: z.infer<typeof formSchema>) => {
if (type === "sign-in") {
const result = await authenticate(null, {
email: data.email.toLowerCase(),
password: data.password,
});
if (result?.message === "Success") {
router.push("/");
} else if (result?.errors && "credentials" in result.errors) {
form.setError("email", {
type: "server",
message: result.errors.credentials ?? "Incorrect email or password",
});
} else {
toast({
variant: "destructive",
title: "Oops! Something went wrong",
description: "An unexpected error occurred. Please try again.",
});
}
}
if (type === "sign-up") {
const newUser = await createNewUser(data);
if (!newUser.errors) {
toast({
title: "Success!",
description: "The user was registered successfully.",
});
form.reset();
router.push("/sign-in");
} else {
newUser.errors.forEach((error: ApiError) => {
const errorMessage = error.detail;
switch (error.source.pointer) {
case "/data/attributes/name":
form.setError("name", { type: "server", message: errorMessage });
break;
case "/data/attributes/email":
form.setError("email", { type: "server", message: errorMessage });
break;
case "/data/attributes/company_name":
form.setError("company", {
type: "server",
message: errorMessage,
});
break;
case "/data/attributes/password":
form.setError("password", {
type: "server",
message: errorMessage,
});
break;
case "/data":
form.setError("invitationToken", {
type: "server",
message: errorMessage,
});
break;
default:
toast({
variant: "destructive",
title: "Oops! Something went wrong",
description: errorMessage,
});
}
});
}
}
};
return (
<div className="relative flex h-screen w-screen">
{/* Auth Form */}
<div className="relative flex w-full items-center justify-center lg:w-full">
{/* Background Pattern */}
<div className="absolute h-full w-full bg-[radial-gradient(#6af400_1px,transparent_1px)] [background-size:16px_16px] [mask-image:radial-gradient(ellipse_50%_50%_at_50%_50%,#000_10%,transparent_80%)]"></div>
<div className="relative z-10 flex w-full max-w-sm flex-col gap-4 rounded-large border-1 border-divider bg-white/90 px-8 py-10 shadow-small dark:bg-background/85 md:max-w-md">
{/* Prowler Logo */}
<div className="absolute -top-[100px] left-1/2 z-10 flex h-fit w-fit -translate-x-1/2">
<ProwlerExtended width={300} />
</div>
<div className="flex items-center justify-between">
<p className="pb-2 text-xl font-medium">
{type === "sign-in" ? "Sign In" : "Sign Up"}
</p>
<ThemeSwitch aria-label="Toggle theme" />
</div>
<Form {...form}>
<form
className="flex flex-col gap-3"
onSubmit={form.handleSubmit(onSubmit)}
>
{type === "sign-up" && (
<>
<CustomInput
control={form.control}
name="name"
type="text"
label="Name"
placeholder="Enter your name"
isInvalid={!!form.formState.errors.name}
/>
<CustomInput
control={form.control}
name="company"
type="text"
label="Company Name"
placeholder="Enter your company name"
isRequired={false}
isInvalid={!!form.formState.errors.company}
/>
</>
)}
<CustomInput
control={form.control}
name="email"
type="email"
label="Email"
placeholder="Enter your email"
isInvalid={!!form.formState.errors.email}
/>
<CustomInput
control={form.control}
name="password"
password
isInvalid={!!form.formState.errors.password}
/>
{type === "sign-in" && (
<div className="flex items-center justify-between px-1 py-2">
<Checkbox name="remember" size="sm">
Remember me
</Checkbox>
<Link className="text-default-500" href="#">
Forgot password?
</Link>
</div>
)}
{type === "sign-up" && (
<>
<CustomInput
control={form.control}
name="confirmPassword"
confirmPassword
/>
{invitationToken && (
<CustomInput
control={form.control}
name="invitationToken"
type="text"
label="Invitation Token"
placeholder={invitationToken}
defaultValue={invitationToken}
isRequired={false}
isInvalid={!!form.formState.errors.invitationToken}
/>
)}
<FormField
control={form.control}
name="termsAndConditions"
render={({ field }) => (
<>
<FormControl>
<Checkbox
isRequired
className="py-4"
size="sm"
checked={field.value}
onChange={(e) => field.onChange(e.target.checked)}
>
I agree with the&nbsp;
<Link href="#" size="sm">
Terms
</Link>
&nbsp; and&nbsp;
<Link href="#" size="sm">
Privacy Policy
</Link>
</Checkbox>
</FormControl>
<FormMessage className="text-system-error dark:text-system-error" />
</>
)}
/>
</>
)}
{form.formState.errors?.email && (
<div className="flex flex-row items-center gap-2 text-system-error">
<NotificationIcon size={16} />
<p className="text-s">No user found</p>
</div>
)}
<CustomButton
type="submit"
ariaLabel={type === "sign-in" ? "Log In" : "Sign Up"}
ariaDisabled={isLoading}
className="w-full"
variant="solid"
color="action"
size="md"
radius="md"
isLoading={isLoading}
isDisabled={isLoading}
>
{isLoading ? (
<span>Loading</span>
) : (
<span>{type === "sign-in" ? "Log In" : "Sign Up"}</span>
)}
</CustomButton>
</form>
</Form>
{type === "sign-in" && (
<>
<div className="flex items-center gap-4 py-2">
<Divider className="flex-1" />
<p className="shrink-0 text-tiny text-default-500">OR</p>
<Divider className="flex-1" />
</div>
<div className="flex flex-col gap-2">
<Button
startContent={
<Icon icon="flat-color-icons:google" width={24} />
}
variant="bordered"
>
Continue with Google
</Button>
<Button
startContent={
<Icon
className="text-default-500"
icon="fe:github"
width={24}
/>
}
variant="bordered"
>
Continue with Github
</Button>
</div>
</>
)}
{type === "sign-in" ? (
<p className="text-center text-small">
Need to create an account?&nbsp;
<Link href="/sign-up">Sign Up</Link>
</p>
) : (
<p className="text-center text-small">
Already have an account?&nbsp;
<Link href="/sign-in">Log In</Link>
</p>
)}
</div>
</div>
</div>
);
};
+1
View File
@@ -0,0 +1 @@
export * from "./auth-form";
+76
View File
@@ -0,0 +1,76 @@
"use client";
import { Bar, BarChart, LabelList, XAxis, YAxis } from "recharts";
import {
ChartConfig,
ChartContainer,
ChartTooltip,
ChartTooltipContent,
} from "@/components/ui/chart/Chart";
const chartData = [
{ severity: "critical", findings: 32, fill: "var(--color-critical)" },
{ severity: "high", findings: 78, fill: "var(--color-high)" },
{ severity: "medium", findings: 117, fill: "var(--color-medium)" },
{ severity: "low", findings: 39, fill: "var(--color-low)" },
];
const chartConfig = {
findings: {
label: "Findings",
},
critical: {
label: "Critical",
color: "hsl(var(--chart-critical))",
},
high: {
label: "High",
color: "hsl(var(--chart-fail))",
},
medium: {
label: "Medium",
color: "hsl(var(--chart-medium))",
},
low: {
label: "Low",
color: "hsl(var(--chart-low))",
},
} satisfies ChartConfig;
export const SeverityChart = () => {
return (
<div className="my-auto">
<ChartContainer config={chartConfig}>
<BarChart accessibilityLayer data={chartData} layout="vertical">
<YAxis
dataKey="severity"
type="category"
tickLine={false}
tickMargin={10}
axisLine={false}
tickFormatter={(value) =>
chartConfig[value as keyof typeof chartConfig]?.label
}
/>
<XAxis dataKey="findings" type="number" hide>
<LabelList position="insideTop" offset={12} fontSize={12} />
</XAxis>
<ChartTooltip
cursor={false}
content={<ChartTooltipContent indicator="line" />}
/>
<Bar dataKey="findings" layout="vertical" radius={12}>
<LabelList
position="insideRight"
offset={10}
className="fill-foreground font-bold"
fontSize={12}
/>
</Bar>
</BarChart>
</ChartContainer>
</div>
);
};
+148
View File
@@ -0,0 +1,148 @@
"use client";
import { Chip, Divider, Spacer } from "@nextui-org/react";
import { TrendingUp } from "lucide-react";
import * as React from "react";
import { Label, Pie, PieChart } from "recharts";
import { NotificationIcon, SuccessIcon } from "../icons";
import {
ChartConfig,
ChartContainer,
ChartTooltip,
ChartTooltipContent,
} from "../ui";
const calculatePercent = (
chartData: { findings: string; number: number; fill: string }[],
) => {
const total = chartData.reduce((sum, item) => sum + item.number, 0);
return chartData.map((item) => ({
...item,
percent: Math.round((item.number / total) * 100) + "%",
}));
};
const chartData = [
{
findings: "Success",
number: 436,
fill: "var(--color-success)",
},
{ findings: "Fail", number: 293, fill: "var(--color-fail)" },
];
const updatedChartData = calculatePercent(chartData);
const chartConfig = {
number: {
label: "Findings",
},
success: {
label: "Success",
color: "hsl(var(--chart-success))",
},
fail: {
label: "Fail",
color: "hsl(var(--chart-fail))",
},
} satisfies ChartConfig;
export function StatusChart() {
const totalVisitors = React.useMemo(() => {
return chartData.reduce((acc, curr) => acc + curr.number, 0);
}, []);
return (
<div className="my-auto flex items-center justify-center self-center">
<ChartContainer
config={chartConfig}
className="mx-auto aspect-square min-w-[200px] md:min-h-[250px]"
>
<PieChart>
<ChartTooltip cursor={false} content={<ChartTooltipContent />} />
<Pie
data={chartData}
dataKey="number"
nameKey="findings"
innerRadius={60}
strokeWidth={50}
>
<Label
content={({ viewBox }) => {
if (viewBox && "cx" in viewBox && "cy" in viewBox) {
return (
<text
x={viewBox.cx}
y={viewBox.cy}
radius={70}
textAnchor="middle"
dominantBaseline="middle"
>
<tspan
x={viewBox.cx}
y={viewBox.cy}
className="fill-foreground text-3xl font-bold"
>
{totalVisitors.toLocaleString()}
</tspan>
<tspan
x={viewBox.cx}
y={(viewBox.cy || 0) + 24}
className="fill-foreground"
>
Findings
</tspan>
</text>
);
}
}}
/>
</Pie>
</PieChart>
</ChartContainer>
<div className="mx-6 flex flex-col justify-center gap-2 text-small">
<div className="flex space-x-4">
<Chip
className="h-5"
variant="flat"
startContent={<SuccessIcon size={18} />}
color="success"
radius="lg"
size="md"
>
{chartData[0].number}
</Chip>
<Divider orientation="vertical" />
<span>{updatedChartData[0].percent}</span>
<Divider orientation="vertical" />
</div>
<div className="flex items-center font-light leading-none">
No change from last scan
</div>
<Spacer y={4} />
<div className="text-muted-foreground flex flex-col gap-2 leading-none">
<div className="flex space-x-4">
<Chip
className="h-5"
variant="flat"
startContent={<NotificationIcon size={18} />}
color="danger"
radius="lg"
size="md"
>
{chartData[1].number}
</Chip>
<Divider orientation="vertical" />
<span>{updatedChartData[1].percent}</span>
<Divider orientation="vertical" />
</div>
<div className="flex items-center gap-1 font-medium leading-none">
+2 findings from last scan <TrendingUp className="h-4 w-4" />
</div>
</div>
</div>
</div>
);
}
+2
View File
@@ -0,0 +1,2 @@
export * from "./SeverityChart";
export * from "./StatusChart";
@@ -0,0 +1,83 @@
import { Card, CardBody, Progress } from "@nextui-org/react";
import Image from "next/image";
import React from "react";
import { getComplianceIcon } from "../icons";
interface ComplianceCardProps {
title: string;
passingRequirements: number;
totalRequirements: number;
prevPassingRequirements: number;
prevTotalRequirements: number;
}
export const ComplianceCard: React.FC<ComplianceCardProps> = ({
title,
passingRequirements,
totalRequirements,
}) => {
const ratingPercentage = Math.floor(
(passingRequirements / totalRequirements) * 100,
);
// const prevRatingPercentage = Math.floor(
// (prevPassingRequirements / prevTotalRequirements) * 100,
// );
// const getScanChange = () => {
// const scanDifference = ratingPercentage - prevRatingPercentage;
// if (scanDifference < 0 && scanDifference <= -1) {
// return `${scanDifference}% from last scan`;
// }
// if (scanDifference > 0 && scanDifference >= 1) {
// return `+${scanDifference}% from last scan`;
// }
// return "No changes from last scan";
// };
const getRatingColor = (ratingPercentage: number) => {
if (ratingPercentage <= 10) {
return "danger";
}
if (ratingPercentage <= 40) {
return "warning";
}
return "success";
};
return (
<Card fullWidth isPressable isHoverable shadow="sm">
<CardBody className="flex flex-row items-center justify-between space-x-4 dark:bg-prowler-blue-800">
<div className="flex w-full items-center space-x-4">
<Image
src={getComplianceIcon(title)}
alt={`${title} logo`}
className="h-10 w-10 min-w-10 rounded-md border-1 border-gray-300 bg-white object-contain p-1"
/>
<div className="flex w-full flex-col">
<h4 className="text-md font-bold leading-5 3xl:text-lg">{title}</h4>
<Progress
label="Your Rating:"
size="sm"
aria-label="Your Rating"
value={ratingPercentage}
showValueLabel={true}
className="mt-2 font-semibold"
color={getRatingColor(ratingPercentage)}
/>
<div className="mt-2 flex justify-between">
<small>
<span className="mr-1 font-semibold">
{passingRequirements} / {totalRequirements}
</span>
Passing Requirements
</small>
{/* <small>{getScanChange()}</small> */}
</div>
</div>
</div>
</CardBody>
</Card>
);
};
@@ -0,0 +1,18 @@
import { Card, Skeleton } from "@nextui-org/react";
import React from "react";
export const ComplianceSkeletonGrid = () => {
return (
<Card className="h-fit w-full p-4">
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-3 3xl:grid-cols-4">
{[...Array(28)].map((_, index) => (
<div key={index} className="flex flex-col space-y-4">
<Skeleton className="h-28 rounded-lg">
<div className="h-full bg-default-300"></div>
</Skeleton>
</div>
))}
</div>
</Card>
);
};
@@ -0,0 +1,90 @@
"use client";
import { useRouter, useSearchParams } from "next/navigation";
import { useCallback, useEffect, useState } from "react";
import { SelectScanComplianceData } from "@/components/compliance/data-compliance";
import { CrossIcon } from "@/components/icons";
import { CustomButton, CustomDropdownFilter } from "@/components/ui/custom";
interface DataComplianceProps {
scans: { id: string; name: string; state: string; progress: number }[];
regions: string[];
}
export const DataCompliance = ({ scans, regions }: DataComplianceProps) => {
const router = useRouter();
const searchParams = useSearchParams();
const [showClearButton, setShowClearButton] = useState(false);
const scanIdParam = searchParams.get("scanId");
const selectedScanId = scanIdParam || scans[0]?.id;
useEffect(() => {
const hasFilters = Array.from(searchParams.keys()).some(
(key) => key.startsWith("filter[") || key === "sort",
);
setShowClearButton(hasFilters);
}, [searchParams]);
const handleScanChange = (selectedKey: string) => {
const params = new URLSearchParams(searchParams);
params.set("scanId", selectedKey);
router.push(`?${params.toString()}`);
};
const pushDropdownFilter = useCallback(
(key: string, values: string[]) => {
const params = new URLSearchParams(searchParams);
const filterKey = `filter[${key}]`;
if (values.length === 0) {
params.delete(filterKey);
} else {
params.set(filterKey, values.join(","));
}
router.push(`?${params.toString()}`);
},
[router, searchParams],
);
const clearAllFilters = useCallback(() => {
const params = new URLSearchParams(searchParams.toString());
Array.from(params.keys()).forEach((key) => {
if (key.startsWith("filter[") || key === "sort") {
params.delete(key);
}
});
router.push(`?${params.toString()}`, { scroll: false });
}, [router, searchParams]);
return (
<div className="flex flex-col gap-4">
<div className="grid grid-cols-1 items-center gap-x-4 gap-y-4 md:grid-cols-2 xl:grid-cols-4">
<SelectScanComplianceData
scans={scans}
selectedScanId={selectedScanId}
onSelectionChange={handleScanChange}
/>
<CustomDropdownFilter
filter={{
key: "region__in",
values: regions,
labelCheckboxGroup: "Regions",
}}
onFilterChange={pushDropdownFilter}
/>
{showClearButton && (
<CustomButton
ariaLabel="Reset"
className="w-full md:w-fit"
onPress={clearAllFilters}
variant="dashed"
size="sm"
endContent={<CrossIcon size={24} />}
radius="sm"
>
Reset
</CustomButton>
)}
</div>
</div>
);
};
@@ -0,0 +1,2 @@
export * from "./data-compliance";
export * from "./select-scan-compliance-data";
@@ -0,0 +1,50 @@
import { Select, SelectItem } from "@nextui-org/react";
interface SelectScanComplianceDataProps {
scans: { id: string; name: string; state: string; progress: number }[];
selectedScanId: string;
onSelectionChange: (selectedKey: string) => void;
}
export const SelectScanComplianceData = ({
scans,
selectedScanId,
onSelectionChange,
}: SelectScanComplianceDataProps) => {
return (
<Select
aria-label="Select a Scan"
placeholder="Select a scan"
labelPlacement="outside"
size="md"
selectedKeys={new Set([selectedScanId])}
onSelectionChange={(keys) =>
onSelectionChange(Array.from(keys)[0] as string)
}
renderValue={() => {
const selectedItem = scans.find((item) => item.id === selectedScanId);
return selectedItem ? (
<div className="flex flex-col">
<span className="font-bold">{selectedItem.name}</span>
<span className="text-sm text-gray-500">
State: {selectedItem.state}, Progress: {selectedItem.progress}%
</span>
</div>
) : (
"Select a scan"
);
}}
>
{scans.map((scan) => (
<SelectItem key={scan.id} textValue={scan.name}>
<div className="flex flex-col">
<span className="font-bold">{scan.name}</span>
<span className="text-sm text-gray-500">
State: {scan.state}, Progress: {scan.progress}%
</span>
</div>
</SelectItem>
))}
</Select>
);
};
+2
View File
@@ -0,0 +1,2 @@
export * from "./compliance-card";
export * from "./compliance-skeleton-grid";
@@ -0,0 +1,32 @@
"use client";
import { Select, SelectItem } from "@nextui-org/react";
const accounts = [
{ key: "audit-test-1", label: "740350143844" },
{ key: "audit-test-2", label: "890837126756" },
{ key: "audit-test-3", label: "563829104923" },
{ key: "audit-test-4", label: "678943217543" },
{ key: "audit-test-5", label: "932187465320" },
{ key: "audit-test-6", label: "492837106587" },
{ key: "audit-test-7", label: "812736459201" },
{ key: "audit-test-8", label: "374829106524" },
{ key: "audit-test-9", label: "926481053298" },
{ key: "audit-test-10", label: "748192364579" },
{ key: "audit-test-11", label: "501374829106" },
];
export const CustomAccountSelection = () => {
return (
<Select
label="Account"
aria-label="Select an Account"
placeholder="Select an account"
selectionMode="multiple"
className="w-full"
size="sm"
>
{accounts.map((acc) => (
<SelectItem key={acc.key}>{acc.label}</SelectItem>
))}
</Select>
);
};
@@ -0,0 +1,15 @@
import { Checkbox } from "@nextui-org/react";
import React from "react";
export const CustomCheckboxMutedFindings = () => {
return (
<Checkbox
className="xl:-mt-8"
size="md"
color="danger"
aria-label="Include Muted Findings"
>
Include Muted Findings
</Checkbox>
);
};
@@ -0,0 +1,97 @@
"use client";
import {
getLocalTimeZone,
startOfMonth,
startOfWeek,
today,
} from "@internationalized/date";
import { Button, ButtonGroup, DatePicker } from "@nextui-org/react";
import { useLocale } from "@react-aria/i18n";
import { useRouter, useSearchParams } from "next/navigation";
import React, { useCallback, useEffect, useRef } from "react";
export const CustomDatePicker = () => {
const router = useRouter();
const searchParams = useSearchParams();
const defaultDate = today(getLocalTimeZone());
const [value, setValue] = React.useState(defaultDate);
const { locale } = useLocale();
const now = today(getLocalTimeZone());
const nextWeek = startOfWeek(now.add({ weeks: 1 }), locale);
const nextMonth = startOfMonth(now.add({ months: 1 }));
const applyDateFilter = useCallback(
(date: any) => {
const params = new URLSearchParams(searchParams.toString());
if (date) {
params.set("filter[updated_at]", date.toString());
} else {
params.delete("filter[updated_at]");
}
router.push(`?${params.toString()}`, { scroll: false });
},
[router, searchParams],
);
const initialRender = useRef(true);
useEffect(() => {
if (initialRender.current) {
initialRender.current = false;
return;
}
const params = new URLSearchParams(searchParams.toString());
if (params.size === 0) {
// If all params are cleared, reset to default date
setValue(defaultDate);
}
}, [searchParams]);
const handleDateChange = (newValue: any) => {
setValue(newValue);
applyDateFilter(newValue);
};
return (
<div className="flex w-full flex-col md:gap-2">
<DatePicker
aria-label="Select a Date"
CalendarTopContent={
<ButtonGroup
fullWidth
className="bg-content1 px-3 pb-2 pt-3 dark:bg-prowler-blue-400 [&>button]:border-default-200/60 [&>button]:text-default-500"
radius="full"
size="sm"
variant="bordered"
>
<Button onPress={() => handleDateChange(now)}>Today</Button>
<Button onPress={() => handleDateChange(nextWeek)}>
Next week
</Button>
<Button onPress={() => handleDateChange(nextMonth)}>
Next month
</Button>
</ButtonGroup>
}
calendarProps={{
focusedValue: value,
onFocusChange: setValue,
nextButtonProps: {
variant: "bordered",
},
prevButtonProps: {
variant: "bordered",
},
}}
value={value}
onChange={handleDateChange}
size="sm"
variant="flat"
/>
</div>
);
};
@@ -0,0 +1,44 @@
import React from "react";
import {
AWSProviderBadge,
AzureProviderBadge,
GCPProviderBadge,
KS8ProviderBadge,
} from "../icons/providers-badge";
export const CustomProviderInputAWS = () => {
return (
<div className="flex items-center gap-x-2">
<AWSProviderBadge width={25} height={25} />
<p className="text-sm">Amazon Web Services</p>
</div>
);
};
export const CustomProviderInputAzure = () => {
return (
<div className="flex items-center gap-x-2">
<AzureProviderBadge width={25} height={25} />
<p className="text-sm">Azure</p>
</div>
);
};
export const CustomProviderInputGCP = () => {
return (
<div className="flex items-center gap-x-2">
<GCPProviderBadge width={25} height={25} />
<p className="text-sm">Google Cloud Platform</p>
</div>
);
};
export const CustomProviderInputKubernetes = () => {
return (
<div className="flex items-center gap-x-2">
<KS8ProviderBadge width={25} height={25} />
<p className="text-sm">Kubernetes</p>
</div>
);
};
@@ -0,0 +1,51 @@
"use client";
import { Select, SelectItem } from "@nextui-org/react";
const regions = [
{ key: "af-south-1", label: "AF South 1" },
{ key: "ap-east-1", label: "AP East 1" },
{ key: "ap-northeast-1", label: "AP Northeast 1" },
{ key: "ap-northeast-2", label: "AP Northeast 2" },
{ key: "ap-northeast-3", label: "AP Northeast 3" },
{ key: "ap-south-1", label: "AP South 1" },
{ key: "ap-south-2", label: "AP South 2" },
{ key: "ap-southeast-1", label: "AP Southeast 1" },
{ key: "ap-southeast-2", label: "AP Southeast 2" },
{ key: "ap-southeast-3", label: "AP Southeast 3" },
{ key: "ap-southeast-4", label: "AP Southeast 4" },
{ key: "ca-central-1", label: "CA Central 1" },
{ key: "ca-west-1", label: "CA West 1" },
{ key: "eu-central-1", label: "EU Central 1" },
{ key: "eu-central-2", label: "EU Central 2" },
{ key: "eu-north-1", label: "EU North 1" },
{ key: "eu-south-1", label: "EU South 1" },
{ key: "eu-south-2", label: "EU South 2" },
{ key: "eu-west-1", label: "EU West 1" },
{ key: "eu-west-2", label: "EU West 2" },
{ key: "eu-west-3", label: "EU West 3" },
{ key: "il-central-1", label: "IL Central 1" },
{ key: "me-central-1", label: "ME Central 1" },
{ key: "me-south-1", label: "ME South 1" },
{ key: "sa-east-1", label: "SA East 1" },
{ key: "us-east-1", label: "US East 1" },
{ key: "us-east-2", label: "US East 2" },
{ key: "us-west-1", label: "US West 1" },
{ key: "us-west-2", label: "US West 2" },
];
export const CustomRegionSelection = () => {
return (
<Select
label="Region"
aria-label="Select a Region"
placeholder="Select a region"
selectionMode="multiple"
className="w-full"
size="sm"
>
{regions.map((acc) => (
<SelectItem key={acc.key}>{acc.label}</SelectItem>
))}
</Select>
);
};
@@ -0,0 +1,62 @@
import { Input } from "@nextui-org/react";
import debounce from "lodash.debounce";
import { SearchIcon, XCircle } from "lucide-react";
import { useRouter, useSearchParams } from "next/navigation";
import React, { useCallback, useEffect, useState } from "react";
export const CustomSearchInput: React.FC = () => {
const router = useRouter();
const searchParams = useSearchParams();
const [searchQuery, setSearchQuery] = useState("");
const applySearch = useCallback(
(query: string) => {
const params = new URLSearchParams(searchParams.toString());
if (query) {
params.set("filter[search]", query);
} else {
params.delete("filter[search]");
}
router.push(`?${params.toString()}`, { scroll: false });
},
[router, searchParams],
);
const debouncedChangeHandler = useCallback(debounce(applySearch, 300), []);
const clearIconSearch = () => {
setSearchQuery("");
applySearch("");
};
useEffect(() => {
const searchFromUrl = searchParams.get("filter[search]") || "";
setSearchQuery(searchFromUrl);
}, [searchParams]);
return (
<Input
variant="flat"
aria-label="Search"
placeholder="Search..."
labelPlacement="outside"
value={searchQuery}
startContent={<SearchIcon className="text-default-400" width={16} />}
onChange={(e) => {
const value = e.target.value;
setSearchQuery(value);
debouncedChangeHandler(value);
}}
endContent={
searchQuery && (
<button onClick={clearIconSearch} className="focus:outline-none">
<XCircle className="h-4 w-4 text-default-400" />
</button>
)
}
radius="sm"
size="sm"
/>
);
};
@@ -0,0 +1,91 @@
"use client";
import { Select, SelectItem } from "@nextui-org/react";
import { useRouter, useSearchParams } from "next/navigation";
import React, { useCallback, useMemo } from "react";
import {
CustomProviderInputAWS,
CustomProviderInputAzure,
CustomProviderInputGCP,
CustomProviderInputKubernetes,
} from "./custom-provider-inputs";
const dataInputsProvider = [
{
key: "aws",
label: "Amazon Web Services",
value: <CustomProviderInputAWS />,
},
{
key: "gcp",
label: "Google Cloud Platform",
value: <CustomProviderInputGCP />,
},
{
key: "azure",
label: "Microsoft Azure",
value: <CustomProviderInputAzure />,
},
{
key: "kubernetes",
label: "Kubernetes",
value: <CustomProviderInputKubernetes />,
},
];
export const CustomSelectProvider: React.FC = () => {
const router = useRouter();
const searchParams = useSearchParams();
const applyProviderFilter = useCallback(
(value: string) => {
const params = new URLSearchParams(searchParams.toString());
if (value) {
params.set("filter[provider_type]", value);
} else {
params.delete("filter[provider_type]");
}
router.push(`?${params.toString()}`, { scroll: false });
},
[router, searchParams],
);
const currentProvider = searchParams.get("filter[provider_type]") || "";
const selectedKeys = useMemo(() => {
return dataInputsProvider.some(
(provider) => provider.key === currentProvider,
)
? [currentProvider]
: [];
}, [currentProvider]);
return (
<Select
items={dataInputsProvider}
aria-label="Select a Provider"
placeholder="Select a provider"
labelPlacement="outside"
size="sm"
onChange={(e) => {
const value = e.target.value;
applyProviderFilter(value);
}}
selectedKeys={selectedKeys}
renderValue={(items) => {
return items.map((item) => (
<div key={item.key} className="flex items-center gap-2">
{item.data?.value}
</div>
));
}}
>
{(item) => (
<SelectItem key={item.key} textValue={item.key} aria-label={item.label}>
<div className="flex items-center gap-2">{item.value}</div>
</SelectItem>
)}
</Select>
);
};
+74
View File
@@ -0,0 +1,74 @@
export const filterProviders = [
{
key: "connected",
labelCheckboxGroup: "Connection",
values: ["false", "true"],
},
// Add more filter categories as needed
];
export const filterScans = [
{
key: "provider_type__in",
labelCheckboxGroup: "Provider",
values: ["aws", "azure", "gcp", "kubernetes"],
},
{
key: "state",
labelCheckboxGroup: "State",
values: [
"available",
"scheduled",
"executing",
"completed",
"failed",
"cancelled",
],
},
{
key: "trigger",
labelCheckboxGroup: "Schedule",
values: ["scheduled", "manual"],
},
// Add more filter categories as needed
];
export const filterFindings = [
{
key: "severity__in",
labelCheckboxGroup: "Severity",
values: ["critical", "high", "medium", "low", "informational"],
},
{
key: "status__in",
labelCheckboxGroup: "Status",
values: ["PASS", "FAIL", "MANUAL", "MUTED"],
},
{
key: "delta__in",
labelCheckboxGroup: "Delta",
values: ["new", "changed"],
},
{
key: "provider_type__in",
labelCheckboxGroup: "Provider",
values: ["aws", "azure", "gcp", "kubernetes"],
},
// Add more filter categories as needed
];
export const filterUsers = [
{
key: "is_active",
labelCheckboxGroup: "Status",
values: ["true", "false"],
},
];
export const filterInvitations = [
{
key: "state",
labelCheckboxGroup: "State",
values: ["pending", "accepted", "expired", "revoked"],
},
];
+75
View File
@@ -0,0 +1,75 @@
"use client";
import { useRouter, useSearchParams } from "next/navigation";
import React, { useCallback, useEffect, useState } from "react";
import { FilterControlsProps } from "@/types";
import { CrossIcon } from "../icons";
import { CustomButton } from "../ui/custom";
import { DataTableFilterCustom } from "../ui/table";
import { CustomAccountSelection } from "./custom-account-selection";
import { CustomCheckboxMutedFindings } from "./custom-checkbox-muted-findings";
import { CustomDatePicker } from "./custom-date-picker";
import { CustomRegionSelection } from "./custom-region-selection";
import { CustomSearchInput } from "./custom-search-input";
import { CustomSelectProvider } from "./custom-select-provider";
export const FilterControls: React.FC<FilterControlsProps> = ({
search = false,
providers = false,
date = false,
regions = false,
accounts = false,
mutedFindings = false,
customFilters,
}) => {
const router = useRouter();
const searchParams = useSearchParams();
const [showClearButton, setShowClearButton] = useState(false);
useEffect(() => {
const hasFilters = Array.from(searchParams.keys()).some(
(key) => key.startsWith("filter[") || key === "sort",
);
setShowClearButton(hasFilters);
}, [searchParams]);
const clearAllFilters = useCallback(() => {
const params = new URLSearchParams(searchParams.toString());
Array.from(params.keys()).forEach((key) => {
if (key.startsWith("filter[") || key === "sort") {
params.delete(key);
}
});
router.push(`?${params.toString()}`, { scroll: false });
}, [router, searchParams]);
return (
<div className="flex flex-col gap-4">
<div className="grid grid-cols-1 items-center gap-x-4 gap-y-4 md:grid-cols-2 xl:grid-cols-4">
{search && <CustomSearchInput />}
{providers && <CustomSelectProvider />}
{date && <CustomDatePicker />}
{regions && <CustomRegionSelection />}
{accounts && <CustomAccountSelection />}
{mutedFindings && <CustomCheckboxMutedFindings />}
{showClearButton && (
<CustomButton
ariaLabel="Reset"
className="w-full md:w-fit"
onPress={clearAllFilters}
variant="dashed"
size="sm"
endContent={<CrossIcon size={24} />}
radius="sm"
>
Reset
</CustomButton>
)}
</div>
{customFilters && <DataTableFilterCustom filters={customFilters} />}
</div>
);
};
+8
View File
@@ -0,0 +1,8 @@
export * from "./custom-account-selection";
export * from "./custom-checkbox-muted-findings";
export * from "./custom-date-picker";
export * from "./custom-provider-inputs";
export * from "./custom-region-selection";
export * from "./custom-select-provider";
export * from "./data-filters";
export * from "./filter-controls";
@@ -0,0 +1,177 @@
"use client";
import { ColumnDef } from "@tanstack/react-table";
import { useSearchParams } from "next/navigation";
import { DataTableRowDetails } from "@/components/findings/table";
import { InfoIcon } from "@/components/icons";
import { TriggerSheet } from "@/components/ui/sheet";
import {
DataTableColumnHeader,
SeverityBadge,
StatusFindingBadge,
} from "@/components/ui/table";
import { FindingProps } from "@/types";
import { DataTableRowActions } from "./data-table-row-actions";
const getFindingsData = (row: { original: FindingProps }) => {
return row.original;
};
const getFindingsMetadata = (row: { original: FindingProps }) => {
return row.original.attributes.check_metadata;
};
const getResourceData = (
row: { original: FindingProps },
field: keyof FindingProps["relationships"]["resource"]["attributes"],
) => {
return (
row.original.relationships?.resource?.attributes?.[field] ||
`No ${field} found in resource`
);
};
const getProviderData = (
row: { original: FindingProps },
field: keyof FindingProps["relationships"]["provider"]["attributes"],
) => {
return (
row.original.relationships?.provider?.attributes?.[field] ||
`No ${field} found in provider`
);
};
const getScanData = (
row: { original: FindingProps },
field: keyof FindingProps["relationships"]["scan"]["attributes"],
) => {
return (
row.original.relationships?.scan?.attributes?.[field] ||
`No ${field} found in scan`
);
};
export const ColumnFindings: ColumnDef<FindingProps>[] = [
{
accessorKey: "check",
header: ({ column }) => (
<DataTableColumnHeader column={column} title={"Check"} param="check_id" />
),
cell: ({ row }) => {
const { checktitle } = getFindingsMetadata(row);
return <p className="max-w-96 truncate text-small">{checktitle}</p>;
},
},
{
accessorKey: "severity",
header: ({ column }) => (
<DataTableColumnHeader
column={column}
title={"Severity"}
param="severity"
/>
),
cell: ({ row }) => {
const {
attributes: { severity },
} = getFindingsData(row);
return <SeverityBadge severity={severity} />;
},
},
{
accessorKey: "status",
header: ({ column }) => (
<DataTableColumnHeader column={column} title={"Status"} param="status" />
),
cell: ({ row }) => {
const {
attributes: { status },
} = getFindingsData(row);
return <StatusFindingBadge size="sm" status={status} />;
},
},
{
accessorKey: "scanName",
header: "Scan Name",
cell: ({ row }) => {
const name = getScanData(row, "name");
return (
<p className="text-small">
{typeof name === "string" || typeof name === "number"
? name
: "Invalid data"}
</p>
);
},
},
{
accessorKey: "region",
header: "Region",
cell: ({ row }) => {
const region = getResourceData(row, "region");
return (
<>
<div>{typeof region === "string" ? region : "Invalid region"}</div>
</>
);
},
},
{
accessorKey: "service",
header: "Service",
cell: ({ row }) => {
const { servicename } = getFindingsMetadata(row);
return <p className="max-w-96 truncate text-small">{servicename}</p>;
},
},
{
accessorKey: "account",
header: "Account",
cell: ({ row }) => {
const account = getProviderData(row, "uid");
return (
<>
<p className="max-w-96 truncate text-small">
{typeof account === "string" ? account : "Invalid account"}
</p>
</>
);
},
},
{
id: "moreInfo",
header: "Details",
cell: ({ row }) => {
const searchParams = useSearchParams();
const findingId = searchParams.get("id");
const isOpen = findingId === row.original.id;
return (
<div className="flex justify-center">
<TriggerSheet
triggerComponent={<InfoIcon className="text-primary" size={16} />}
title="Finding Details"
description="View the finding details"
defaultOpen={isOpen}
>
<DataTableRowDetails
entityId={row.original.id}
findingDetails={row.original}
/>
</TriggerSheet>
</div>
);
},
},
{
id: "actions",
cell: ({ row }) => {
return <DataTableRowActions row={row} />;
},
},
];
@@ -0,0 +1,99 @@
"use client";
import {
Button,
Dropdown,
DropdownItem,
DropdownMenu,
DropdownSection,
DropdownTrigger,
} from "@nextui-org/react";
import {
// AddNoteBulkIcon,
EditDocumentBulkIcon,
} from "@nextui-org/shared-icons";
import { Row } from "@tanstack/react-table";
// import { useState } from "react";
import { VerticalDotsIcon } from "@/components/icons";
// import { CustomAlertModal } from "@/components/ui/custom";
// import { EditForm } from "../forms";
// import { DeleteForm } from "../forms/delete-form";
interface DataTableRowActionsProps<FindingProps> {
row: Row<FindingProps>;
}
const iconClasses =
"text-2xl text-default-500 pointer-events-none flex-shrink-0";
export function DataTableRowActions<FindingProps>({
row,
}: DataTableRowActionsProps<FindingProps>) {
const findingId = (row.original as { id: string }).id;
return (
<>
{/* <CustomAlertModal
isOpen={isEditOpen}
onOpenChange={setIsEditOpen}
title="Edit Provider"
description={"Edit the provider details"}
>
<EditForm
providerId={providerId}
providerAlias={providerAlias}
setIsOpen={setIsEditOpen}
/>
</CustomAlertModal>
<CustomAlertModal
isOpen={isDeleteOpen}
onOpenChange={setIsDeleteOpen}
title="Are you absolutely sure?"
description="This action cannot be undone. This will permanently delete your provider account and remove your data from the server."
>
<DeleteForm providerId={providerId} setIsOpen={setIsDeleteOpen} />
</CustomAlertModal> */}
<div className="relative flex items-center justify-end gap-2">
<Dropdown
className="shadow-xl dark:bg-prowler-blue-800"
placement="bottom"
>
<DropdownTrigger>
<Button isIconOnly radius="full" size="sm" variant="light">
<VerticalDotsIcon className="text-default-400" />
</Button>
</DropdownTrigger>
<DropdownMenu
closeOnSelect
aria-label="Actions"
color="default"
variant="flat"
>
<DropdownSection title="Actions">
<DropdownItem
key="jira"
description="Allows you to send the finding to Jira"
textValue="Send to Jira"
startContent={<EditDocumentBulkIcon className={iconClasses} />}
// onClick={() => setIsEditOpen(true)}
>
<span className="hidden text-sm">{findingId}</span>
Send to Jira
</DropdownItem>
<DropdownItem
key="slack"
description="Allows you to send the finding to Slack"
textValue="Send to Slack"
startContent={<EditDocumentBulkIcon className={iconClasses} />}
// onClick={() => setIsEditOpen(true)}
>
Send to Slack
</DropdownItem>
</DropdownSection>
</DropdownMenu>
</Dropdown>
</div>
</>
);
}
@@ -0,0 +1,40 @@
"use client";
// import { usePathname, useRouter, useSearchParams } from "next/navigation";
// import { useEffect } from "react";
import { FindingProps } from "@/types/components";
import { FindingDetail } from "./finding-detail";
export const DataTableRowDetails = ({
// entityId,
findingDetails,
}: {
entityId: string;
findingDetails: FindingProps;
}) => {
// const router = useRouter();
// const pathname = usePathname();
// const searchParams = useSearchParams();
// useEffect(() => {
// if (entityId) {
// const params = new URLSearchParams(searchParams.toString());
// params.set("id", entityId);
// router.push(`${pathname}?${params.toString()}`, { scroll: false });
// }
// return () => {
// if (entityId) {
// const cleanupParams = new URLSearchParams(searchParams.toString());
// cleanupParams.delete("id");
// router.push(`${pathname}?${cleanupParams.toString()}`, {
// scroll: false,
// });
// }
// };
// }, [entityId, pathname, router, searchParams]);
return <FindingDetail findingDetails={findingDetails} />;
};
@@ -0,0 +1,219 @@
"use client";
import { Snippet } from "@nextui-org/react";
import Link from "next/link";
import { SnippetId } from "@/components/ui/entities";
import { DateWithTime } from "@/components/ui/entities/date-with-time";
import { SeverityBadge } from "@/components/ui/table/severity-badge";
import { FindingProps } from "@/types";
export const FindingDetail = ({
findingDetails,
}: {
findingDetails: FindingProps;
}) => {
const finding = findingDetails;
const attributes = finding.attributes;
const resource = finding.relationships.resource.attributes;
const remediation = attributes.check_metadata.remediation;
return (
<div className="flex flex-col gap-6 rounded-lg">
{/* Header */}
<div className="flex items-center justify-between">
<div>
<h2 className="line-clamp-2 text-xl font-bold leading-tight text-gray-800 dark:text-prowler-theme-pale/90">
{attributes.check_metadata.checktitle}
</h2>
<p className="text-sm text-gray-500 dark:text-prowler-theme-pale/70">
{resource.service}
</p>
</div>
<div
className={`rounded-lg px-3 py-1 text-sm font-semibold ${
attributes.status === "PASS"
? "bg-green-100 text-green-600"
: attributes.status === "MANUAL"
? "bg-gray-100 text-gray-600"
: "bg-red-100 text-red-600"
}`}
>
{attributes.status}
</div>
</div>
{/* Check Metadata */}
<div className="flex flex-col gap-4 rounded-lg p-4 shadow dark:bg-prowler-blue-400">
<div className="flex items-center justify-between">
<h3 className="text-lg font-bold text-gray-800 dark:text-prowler-theme-pale/90">
Check Metadata
</h3>
<SeverityBadge severity={attributes.severity} />
</div>
{attributes.status === "FAIL" && (
<Snippet
className="max-w-full py-4"
color="danger"
hideCopyButton
hideSymbol
>
<p className="text-sm font-semibold dark:text-prowler-theme-pale">
Risk
</p>
<p className="whitespace-pre-line text-gray-800 dark:text-prowler-theme-pale/90">
{attributes.check_metadata.risk}
</p>
</Snippet>
)}
<div className="flex flex-col gap-2">
<p className="text-sm font-semibold dark:text-prowler-theme-pale">
Description
</p>
<p className="text-gray-800 dark:text-prowler-theme-pale/90">
{attributes.check_metadata.description}
</p>
</div>
<div className="flex flex-col gap-2">
<h3 className="text-sm font-semibold dark:text-prowler-theme-pale">
Remediation
</h3>
<div className="text-gray-800 dark:text-prowler-theme-pale/90">
{remediation.recommendation && (
<>
<p className="text-sm font-semibold">Recommendation:</p>
<p>{remediation.recommendation.text}</p>
<Link
target="_blank"
href={remediation.recommendation.url}
className="mt-2 inline-block text-sm text-blue-500 underline"
>
Learn more
</Link>
</>
)}
{remediation.code &&
Object.values(remediation.code).some(Boolean) && (
<div className="flex flex-col gap-2">
<p className="mt-4 text-sm font-semibold">
Check these links:
</p>
<div className="flex flex-col gap-2">
{remediation.code.cli && (
<div>
<p className="text-sm font-semibold">CLI Command:</p>
<Snippet hideSymbol size="sm" className="max-w-full">
<p className="whitespace-pre-line">
{remediation.code.cli}
</p>
</Snippet>
</div>
)}
<div className="flex flex-row gap-4">
{Object.entries(remediation.code)
.filter(([key]) => key !== "cli")
.map(([key, value]) =>
value ? (
<Link
key={key}
href={value}
target="_blank"
className="text-sm font-medium text-blue-500"
>
{key === "other"
? "External doc"
: key.charAt(0).toUpperCase() +
key.slice(1).toLowerCase()}
</Link>
) : null,
)}
</div>
</div>
</div>
)}
</div>
</div>
</div>
{/* Resources Section */}
<div className="flex flex-col gap-4 rounded-lg p-4 shadow dark:bg-prowler-blue-400">
<h3 className="text-lg font-bold text-gray-800 dark:text-prowler-theme-pale/90">
Resource Details
</h3>
<div className="grid grid-cols-2 gap-6">
<div className="col-span-2">
<p className="text-sm font-semibold dark:text-prowler-theme-pale">
Resource ID
</p>
<Snippet size="sm" hideSymbol className="max-w-full">
<p className="whitespace-pre-line">{resource.uid}</p>
</Snippet>
</div>
<div>
<p className="text-sm font-semibold dark:text-prowler-theme-pale">
Resource Name
</p>
<p className="text-gray-800 dark:text-prowler-theme-pale/90">
{resource.name}
</p>
</div>
<div>
<p className="text-sm font-semibold dark:text-prowler-theme-pale">
Region
</p>
<p className="text-gray-800 dark:text-prowler-theme-pale/90">
{resource.region}
</p>
</div>
<div>
<p className="text-sm font-semibold dark:text-prowler-theme-pale">
Resource Type
</p>
<p className="text-gray-800 dark:text-prowler-theme-pale/90">
{resource.type}
</p>
</div>
<div>
<p className="text-sm font-semibold dark:text-prowler-theme-pale">
Severity
</p>
<SeverityBadge severity={attributes.severity} />
</div>
{resource.tags &&
Object.entries(resource.tags).map(([key, value]) => (
<div key={key}>
<p className="text-sm font-semibold dark:text-prowler-theme-pale">
Tag: {key}
</p>
<SnippetId
entityId={value}
hideSymbol
size="sm"
className="max-w-full"
>
<p className="whitespace-pre-line">{value}</p>
</SnippetId>
</div>
))}
<div className="col-span-2 grid grid-cols-2 gap-6">
<div>
<p className="text-sm font-semibold dark:text-prowler-theme-pale">
Inserted At
</p>
<DateWithTime inline dateTime={resource.inserted_at} />
</div>
<div>
<p className="text-sm font-semibold dark:text-prowler-theme-pale">
Updated At
</p>
<DateWithTime inline dateTime={resource.updated_at} />
</div>
</div>
</div>
</div>
</div>
);
};
+5
View File
@@ -0,0 +1,5 @@
export * from "./column-findings";
export * from "./data-table-row-actions";
export * from "./data-table-row-details";
export * from "./finding-detail";
export * from "./skeleton-table-findings";
@@ -0,0 +1,65 @@
import { Card, Skeleton } from "@nextui-org/react";
import React from "react";
export const SkeletonTableFindings = () => {
return (
<Card className="h-full w-full space-y-5 p-4" radius="sm">
{/* Table headers */}
<div className="hidden justify-between md:flex">
<Skeleton className="w-1/12 rounded-lg">
<div className="h-8 bg-default-200"></div>
</Skeleton>
<Skeleton className="w-2/12 rounded-lg">
<div className="h-8 bg-default-200"></div>
</Skeleton>
<Skeleton className="w-2/12 rounded-lg">
<div className="h-8 bg-default-200"></div>
</Skeleton>
<Skeleton className="w-2/12 rounded-lg">
<div className="h-8 bg-default-200"></div>
</Skeleton>
<Skeleton className="w-2/12 rounded-lg">
<div className="h-8 bg-default-200"></div>
</Skeleton>
<Skeleton className="w-1/12 rounded-lg">
<div className="h-8 bg-default-200"></div>
</Skeleton>
<Skeleton className="w-1/12 rounded-lg">
<div className="h-8 bg-default-200"></div>
</Skeleton>
</div>
{/* Table body */}
<div className="space-y-3">
{[...Array(3)].map((_, index) => (
<div
key={index}
className="flex flex-col items-center justify-between space-x-0 md:flex-row md:space-x-4"
>
<Skeleton className="mb-2 w-full rounded-lg md:mb-0 md:w-1/12">
<div className="h-12 bg-default-300"></div>
</Skeleton>
<Skeleton className="mb-2 w-full rounded-lg md:mb-0 md:w-2/12">
<div className="h-12 bg-default-300"></div>
</Skeleton>
<Skeleton className="mb-2 hidden rounded-lg sm:flex md:mb-0 md:w-2/12">
<div className="h-12 bg-default-300"></div>
</Skeleton>
<Skeleton className="mb-2 hidden rounded-lg sm:flex md:mb-0 md:w-2/12">
<div className="h-12 bg-default-300"></div>
</Skeleton>
<Skeleton className="mb-2 hidden rounded-lg sm:flex md:mb-0 md:w-2/12">
<div className="h-12 bg-default-300"></div>
</Skeleton>
<Skeleton className="mb-2 hidden rounded-lg sm:flex md:mb-0 md:w-1/12">
<div className="h-12 bg-default-300"></div>
</Skeleton>
<Skeleton className="mb-2 hidden rounded-lg sm:flex md:mb-0 md:w-1/12">
<div className="h-12 bg-default-300"></div>
</Skeleton>
</div>
))}
</div>
</Card>
);
};
+810
View File
@@ -0,0 +1,810 @@
import * as React from "react";
import { IconSvgProps } from "@/types";
export const TwitterIcon: React.FC<IconSvgProps> = ({
size = 24,
width,
height,
...props
}) => {
return (
<svg
height={size || height}
viewBox="0 0 24 24"
width={size || width}
{...props}
>
<path
d="M19.633 7.997c.013.175.013.349.013.523 0 5.325-4.053 11.461-11.46 11.461-2.282 0-4.402-.661-6.186-1.809.324.037.636.05.973.05a8.07 8.07 0 0 0 5.001-1.721 4.036 4.036 0 0 1-3.767-2.793c.249.037.499.062.761.062.361 0 .724-.05 1.061-.137a4.027 4.027 0 0 1-3.23-3.953v-.05c.537.299 1.16.486 1.82.511a4.022 4.022 0 0 1-1.796-3.354c0-.748.199-1.434.548-2.032a11.457 11.457 0 0 0 8.306 4.215c-.062-.3-.1-.611-.1-.923a4.026 4.026 0 0 1 4.028-4.028c1.16 0 2.207.486 2.943 1.272a7.957 7.957 0 0 0 2.556-.973 4.02 4.02 0 0 1-1.771 2.22 8.073 8.073 0 0 0 2.319-.624 8.645 8.645 0 0 1-2.019 2.083z"
fill="currentColor"
/>
</svg>
);
};
export const GithubIcon: React.FC<IconSvgProps> = ({
size = 24,
width,
height,
...props
}) => {
return (
<svg
height={size || height}
viewBox="0 0 24 24"
width={size || width}
{...props}
>
<path
clipRule="evenodd"
d="M12.026 2c-5.509 0-9.974 4.465-9.974 9.974 0 4.406 2.857 8.145 6.821 9.465.499.09.679-.217.679-.481 0-.237-.008-.865-.011-1.696-2.775.602-3.361-1.338-3.361-1.338-.452-1.152-1.107-1.459-1.107-1.459-.905-.619.069-.605.069-.605 1.002.07 1.527 1.028 1.527 1.028.89 1.524 2.336 1.084 2.902.829.091-.645.351-1.085.635-1.334-2.214-.251-4.542-1.107-4.542-4.93 0-1.087.389-1.979 1.024-2.675-.101-.253-.446-1.268.099-2.64 0 0 .837-.269 2.742 1.021a9.582 9.582 0 0 1 2.496-.336 9.554 9.554 0 0 1 2.496.336c1.906-1.291 2.742-1.021 2.742-1.021.545 1.372.203 2.387.099 2.64.64.696 1.024 1.587 1.024 2.675 0 3.833-2.33 4.675-4.552 4.922.355.308.675.916.675 1.846 0 1.334-.012 2.41-.012 2.737 0 .267.178.577.687.479C19.146 20.115 22 16.379 22 11.974 22 6.465 17.535 2 12.026 2z"
fill="currentColor"
fillRule="evenodd"
/>
</svg>
);
};
export const MoonFilledIcon: React.FC<IconSvgProps> = ({
size = 24,
width,
height,
...props
}) => (
<svg
aria-hidden="true"
focusable="false"
height={size || height}
role="presentation"
viewBox="0 0 24 24"
width={size || width}
{...props}
>
<path
d="M21.53 15.93c-.16-.27-.61-.69-1.73-.49a8.46 8.46 0 01-1.88.13 8.409 8.409 0 01-5.91-2.82 8.068 8.068 0 01-1.44-8.66c.44-1.01.13-1.54-.09-1.76s-.77-.55-1.83-.11a10.318 10.318 0 00-6.32 10.21 10.475 10.475 0 007.04 8.99 10 10 0 002.89.55c.16.01.32.02.48.02a10.5 10.5 0 008.47-4.27c.67-.93.49-1.519.32-1.79z"
fill="currentColor"
/>
</svg>
);
export const SunFilledIcon: React.FC<IconSvgProps> = ({
size = 24,
width,
height,
...props
}) => (
<svg
aria-hidden="true"
focusable="false"
height={size || height}
role="presentation"
viewBox="0 0 24 24"
width={size || width}
{...props}
>
<g fill="currentColor">
<path d="M19 12a7 7 0 11-7-7 7 7 0 017 7z" />
<path d="M12 22.96a.969.969 0 01-1-.96v-.08a1 1 0 012 0 1.038 1.038 0 01-1 1.04zm7.14-2.82a1.024 1.024 0 01-.71-.29l-.13-.13a1 1 0 011.41-1.41l.13.13a1 1 0 010 1.41.984.984 0 01-.7.29zm-14.28 0a1.024 1.024 0 01-.71-.29 1 1 0 010-1.41l.13-.13a1 1 0 011.41 1.41l-.13.13a1 1 0 01-.7.29zM22 13h-.08a1 1 0 010-2 1.038 1.038 0 011.04 1 .969.969 0 01-.96 1zM2.08 13H2a1 1 0 010-2 1.038 1.038 0 011.04 1 .969.969 0 01-.96 1zm16.93-7.01a1.024 1.024 0 01-.71-.29 1 1 0 010-1.41l.13-.13a1 1 0 011.41 1.41l-.13.13a.984.984 0 01-.7.29zm-14.02 0a1.024 1.024 0 01-.71-.29l-.13-.14a1 1 0 011.41-1.41l.13.13a1 1 0 010 1.41.97.97 0 01-.7.3zM12 3.04a.969.969 0 01-1-.96V2a1 1 0 012 0 1.038 1.038 0 01-1 1.04z" />
</g>
</svg>
);
export const SearchIcon: React.FC<IconSvgProps> = ({
size = 24,
width,
height,
...props
}) => (
<svg
aria-hidden="true"
fill="none"
focusable="false"
height={size || height}
role="presentation"
viewBox="0 0 24 24"
width={size || width}
{...props}
>
<path
d="M11.5 21C16.7467 21 21 16.7467 21 11.5C21 6.25329 16.7467 2 11.5 2C6.25329 2 2 6.25329 2 11.5C2 16.7467 6.25329 21 11.5 21Z"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
/>
<path
d="M22 22L20 20"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
/>
</svg>
);
export const ChevronDownIcon: React.FC<IconSvgProps> = ({
size = 24,
width,
height,
strokeWidth = 1.5,
...props
}) => (
<svg
aria-hidden="true"
fill="none"
focusable="false"
height={size || height}
role="presentation"
viewBox="0 0 24 24"
width={size || width}
{...props}
>
<path
d="M19.92 8.95l-6.52 6.52c-.77.77-2.03.77-2.8 0L4.08 8.95"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeMiterlimit={10}
strokeWidth={strokeWidth}
/>
</svg>
);
export const PlusIcon: React.FC<IconSvgProps> = ({
size = 24,
width,
height,
...props
}) => (
<svg
aria-hidden="true"
fill="none"
focusable="false"
height={size || height}
role="presentation"
viewBox="0 0 24 24"
width={size || width}
{...props}
>
<g
fill="none"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={1.5}
>
<path d="M6 12h12" />
<path d="M12 18V6" />
</g>
</svg>
);
export const VerticalDotsIcon: React.FC<IconSvgProps> = ({
size = 24,
width,
height,
...props
}) => (
<svg
aria-hidden="true"
fill="none"
focusable="false"
height={size || height}
role="presentation"
viewBox="0 0 24 24"
width={size || width}
{...props}
>
<g fill="currentColor">
<path d="M12 10c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zM12 4c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zM12 16c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z" />
</g>
</svg>
);
export const DeleteIcon: React.FC<IconSvgProps> = ({
size,
height,
width,
...props
}) => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
height={size || height || 48}
viewBox="0 0 24 24"
width={size || width || 48}
aria-hidden="true"
{...props}
>
<g fill="none">
<path d="m12.593 23.258-.011.002-.071.035-.02.004-.014-.004-.071-.035q-.016-.005-.024.005l-.004.01-.017.428.005.02.01.013.104.074.015.004.012-.004.104-.074.012-.016.004-.017-.017-.427q-.004-.016-.017-.018m.265-.113-.013.002-.185.093-.01.01-.003.011.018.43.005.012.008.007.201.093q.019.005.029-.008l.004-.014-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014-.034.614q.001.018.017.024l.015-.002.201-.093.01-.008.004-.011.017-.43-.003-.012-.01-.01z" />
<path
fill="currentColor"
d="M14.28 2a2 2 0 0 1 1.897 1.368L16.72 5H20a1 1 0 1 1 0 2l-.003.071-.867 12.143A3 3 0 0 1 16.138 22H7.862a3 3 0 0 1-2.992-2.786L4.003 7.07 4 7a1 1 0 0 1 0-2h3.28l.543-1.632A2 2 0 0 1 9.721 2zm3.717 5H6.003l.862 12.071a1 1 0 0 0 .997.929h8.276a1 1 0 0 0 .997-.929zM10 10a1 1 0 0 1 .993.883L11 11v5a1 1 0 0 1-1.993.117L9 16v-5a1 1 0 0 1 1-1m4 0a1 1 0 0 1 1 1v5a1 1 0 1 1-2 0v-5a1 1 0 0 1 1-1m.28-6H9.72l-.333 1h5.226z"
/>
</g>
</svg>
);
};
export const CheckIcon: React.FC<IconSvgProps> = ({
size = 24,
width,
height,
...props
}) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width={size || width}
height={size || height}
viewBox="0 0 2048 2048"
{...props}
>
<path
fill="currentColor"
d="M2048 1024q0 142-36 272t-103 245t-160 207t-208 160t-245 103t-272 37q-142 0-272-36t-245-103t-207-160t-160-208t-103-245t-37-272q0-141 36-272t103-245t160-207t208-160T752 37t272-37q141 0 272 36t245 103t207 160t160 208t103 245t37 272m-1024 896q123 0 237-32t214-90t182-141t140-181t91-214t32-238q0-123-32-237t-90-214t-141-182t-181-140t-214-91t-238-32q-124 0-238 32t-213 90t-182 141t-140 181t-91 214t-32 238q0 124 32 238t90 213t141 182t181 140t214 91t238 32m0-512q55 0 107-15t98-45t81-69t61-91l116 56q-32 67-80 121t-109 92t-130 58t-144 21q-110 0-210-45t-174-128v173H512v-384h384v128H738q54 60 129 94t157 34m384-723V512h128v384h-384V768h158q-54-60-129-94t-157-34q-55 0-107 15t-98 45t-81 69t-61 91l-116-56q32-67 80-121t109-92t130-58t144-21q110 0 210 45t174 128"
/>
</svg>
);
export const CrossIcon: React.FC<IconSvgProps> = ({
size = 24,
width,
height,
...props
}) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width={size || width}
height={size || height}
viewBox="0 0 24 24"
{...props}
>
<path
fill="currentColor"
d="M16.066 8.995a.75.75 0 1 0-1.06-1.061L12 10.939L8.995 7.934a.75.75 0 1 0-1.06 1.06L10.938 12l-3.005 3.005a.75.75 0 0 0 1.06 1.06L12 13.06l3.005 3.006a.75.75 0 0 0 1.06-1.06L13.062 12z"
/>
</svg>
);
export const PassIcon: React.FC<IconSvgProps> = ({
size = 24,
width,
height,
...props
}) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width={size || width}
height={size || height}
viewBox="0 0 16 16"
{...props}
>
<g fill="currentColor">
<path d="M6.27 10.87h.71l4.56-4.56l-.71-.71l-4.2 4.21l-1.92-1.92L4 8.6z" />
<path
fillRule="evenodd"
d="M8.6 1c1.6.1 3.1.9 4.2 2c1.3 1.4 2 3.1 2 5.1c0 1.6-.6 3.1-1.6 4.4c-1 1.2-2.4 2.1-4 2.4s-3.2.1-4.6-.7s-2.5-2-3.1-3.5S.8 7.5 1.3 6c.5-1.6 1.4-2.9 2.8-3.8C5.4 1.3 7 .9 8.6 1m.5 12.9c1.3-.3 2.5-1 3.4-2.1c.8-1.1 1.3-2.4 1.2-3.8c0-1.6-.6-3.2-1.7-4.3c-1-1-2.2-1.6-3.6-1.7c-1.3-.1-2.7.2-3.8 1S2.7 4.9 2.3 6.3c-.4 1.3-.4 2.7.2 4q.9 1.95 2.7 3c1.2.7 2.6.9 3.9.6"
clipRule="evenodd"
/>
</g>
</svg>
);
export const RocketIcon: React.FC<IconSvgProps> = ({
size = 24,
width,
height,
...props
}) => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width={size || width}
height={size || height}
viewBox="0 0 24 24"
{...props}
>
<path
fill="currentColor"
d="m5.65 10.025l1.95.825q.35-.7.725-1.35t.825-1.3l-1.4-.275zM9.2 12.1l2.85 2.825q1.05-.4 2.25-1.225t2.25-1.875q1.75-1.75 2.738-3.887T20.15 4q-1.8-.125-3.95.863T12.3 7.6q-1.05 1.05-1.875 2.25T9.2 12.1m4.45-1.625q-.575-.575-.575-1.412t.575-1.413t1.425-.575t1.425.575t.575 1.413t-.575 1.412t-1.425.575t-1.425-.575m.475 8.025l2.1-2.1l-.275-1.4q-.65.45-1.3.812t-1.35.713zM21.95 2.175q.475 3.025-.587 5.888T17.7 13.525L18.2 16q.1.5-.05.975t-.5.825l-4.2 4.2l-2.1-4.925L7.075 12.8L2.15 10.7l4.175-4.2q.35-.35.838-.5t.987-.05l2.475.5q2.6-2.6 5.45-3.675t5.875-.6m-18.025 13.8q.875-.875 2.138-.887t2.137.862t.863 2.138t-.888 2.137q-.625.625-2.087 1.075t-4.038.8q.35-2.575.8-4.038t1.075-2.087m1.425 1.4q-.25.25-.5.913t-.35 1.337q.675-.1 1.338-.337t.912-.488q.3-.3.325-.725T6.8 17.35t-.725-.288t-.725.313"
/>
</svg>
);
};
export const AlertIcon: React.FC<IconSvgProps> = ({
size = 24,
width,
height,
...props
}) => (
<svg
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
fill="none"
focusable="false"
height={size || height}
role="presentation"
viewBox="0 0 24 24"
width={size || width}
{...props}
>
<g fill="none">
<path d="M24 0v24H0V0zM12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.019-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z" />
<path
fill="currentColor"
d="m13.299 3.148l8.634 14.954a1.5 1.5 0 0 1-1.299 2.25H3.366a1.5 1.5 0 0 1-1.299-2.25l8.634-14.954c.577-1 2.02-1 2.598 0M12 4.898L4.232 18.352h15.536zM12 15a1 1 0 1 1 0 2a1 1 0 0 1 0-2m0-7a1 1 0 0 1 1 1v4a1 1 0 1 1-2 0V9a1 1 0 0 1 1-1"
/>
</g>
</svg>
);
export const NotificationIcon: React.FC<IconSvgProps> = ({
size = 24,
width,
height,
...props
}) => (
<svg
fill="none"
height={size || height || 24}
viewBox="0 0 24 24"
width={size || width || 24}
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<path
clipRule="evenodd"
d="M18.707 8.796c0 1.256.332 1.997 1.063 2.85.553.628.73 1.435.73 2.31 0 .874-.287 1.704-.863 2.378a4.537 4.537 0 01-2.9 1.413c-1.571.134-3.143.247-4.736.247-1.595 0-3.166-.068-4.737-.247a4.532 4.532 0 01-2.9-1.413 3.616 3.616 0 01-.864-2.378c0-.875.178-1.682.73-2.31.754-.854 1.064-1.594 1.064-2.85V8.37c0-1.682.42-2.781 1.283-3.858C7.861 2.942 9.919 2 11.956 2h.09c2.08 0 4.204.987 5.466 2.625.82 1.054 1.195 2.108 1.195 3.745v.426zM9.074 20.061c0-.504.462-.734.89-.833.5-.106 3.545-.106 4.045 0 .428.099.89.33.89.833-.025.48-.306.904-.695 1.174a3.635 3.635 0 01-1.713.731 3.795 3.795 0 01-1.008 0 3.618 3.618 0 01-1.714-.732c-.39-.269-.67-.694-.695-1.173z"
fill="currentColor"
fillRule="evenodd"
/>
</svg>
);
export const IdIcon: React.FC<IconSvgProps> = ({
size = 24,
width,
height,
...props
}) => (
<svg
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
height={size || height || 24}
viewBox="0 0 24 24"
width={size || width || 24}
{...props}
>
<path d="M18 4v16H6V8.8L10.8 4zm0-2h-8L4 8v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2M9.5 19h-2v-2h2zm7 0h-2v-2h2zm-7-4h-2v-4h2zm3.5 4h-2v-4h2zm0-6h-2v-2h2zm3.5 2h-2v-4h2z" />
</svg>
);
export const DoneIcon: React.FC<IconSvgProps> = ({
size,
height,
width,
...props
}) => {
return (
<svg
width={size || width || 24}
height={size || height || 24}
viewBox="0 0 24 24"
fill="currentColor"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<path d="m2.394 13.742 4.743 3.62 7.616-8.704-1.506-1.316-6.384 7.296-3.257-2.486zm19.359-5.084-1.506-1.316-6.369 7.279-.753-.602-1.25 1.562 2.247 1.798z" />
</svg>
);
};
export const CopyIcon: React.FC<IconSvgProps> = ({
size,
height,
width,
...props
}) => {
return (
<svg
fill="none"
height={size || height || 20}
shapeRendering="geometricPrecision"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="1.5"
viewBox="0 0 24 24"
width={size || width || 20}
{...props}
>
<path d="M6 17C4.89543 17 4 16.1046 4 15V5C4 3.89543 4.89543 3 6 3H13C13.7403 3 14.3866 3.4022 14.7324 4M11 21H18C19.1046 21 20 20.1046 20 19V9C20 7.89543 19.1046 7 18 7H11C9.89543 7 9 7.89543 9 9V19C9 20.1046 9.89543 21 11 21Z" />
</svg>
);
};
export const FlowIcon: React.FC<IconSvgProps> = ({
size,
height,
width,
...props
}) => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
height={size || height || 20}
viewBox="0 0 20 20"
width={size || width || 20}
{...props}
>
<path
fill="currentColor"
d="M16.4 4a2.4 2.4 0 1 0-4.8 0c0 .961.568 1.784 1.384 2.167c-.082 1.584-1.27 2.122-3.335 2.896c-.87.327-1.829.689-2.649 1.234V6.176A2.396 2.396 0 0 0 6 1.6a2.397 2.397 0 0 0-1 4.576v7.649A2.39 2.39 0 0 0 3.6 16a2.4 2.4 0 1 0 4.8 0c0-.961-.568-1.784-1.384-2.167c.082-1.583 1.271-2.122 3.335-2.896c2.03-.762 4.541-1.711 4.64-4.756A2.4 2.4 0 0 0 16.4 4M6 2.615a1.384 1.384 0 1 1 0 2.768a1.384 1.384 0 0 1 0-2.768m0 14.77a1.385 1.385 0 1 1 0-2.77a1.385 1.385 0 0 1 0 2.77m8-12a1.385 1.385 0 1 1 0-2.77a1.385 1.385 0 0 1 0 2.77"
/>
</svg>
);
};
export const ConnectionIcon: React.FC<IconSvgProps> = ({
size,
height,
width,
...props
}) => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
height={size || height || 20}
viewBox="0 0 20 20"
width={size || width || 20}
{...props}
>
<path
fill="currentColor"
d="M18 14.824V12.5A3.5 3.5 0 0 0 14.5 9h-2A1.5 1.5 0 0 1 11 7.5V5.176A2.4 2.4 0 0 0 12.4 3a2.4 2.4 0 1 0-4.8 0c0 .967.576 1.796 1.4 2.176V7.5A1.5 1.5 0 0 1 7.5 9h-2A3.5 3.5 0 0 0 2 12.5v2.324A2.396 2.396 0 0 0 3 19.4a2.397 2.397 0 0 0 1-4.576V12.5A1.5 1.5 0 0 1 5.5 11h2c.539 0 1.044-.132 1.5-.35v4.174a2.396 2.396 0 0 0 1 4.576a2.397 2.397 0 0 0 1-4.576V10.65c.456.218.961.35 1.5.35h2a1.5 1.5 0 0 1 1.5 1.5v2.324A2.4 2.4 0 0 0 14.6 17a2.4 2.4 0 1 0 4.8 0c0-.967-.575-1.796-1.4-2.176M10 1.615a1.384 1.384 0 1 1 0 2.768a1.384 1.384 0 0 1 0-2.768m-7 16.77a1.385 1.385 0 1 1 0-2.77a1.385 1.385 0 0 1 0 2.77m7 0a1.385 1.385 0 1 1 0-2.77a1.385 1.385 0 0 1 0 2.77m7 0a1.385 1.385 0 1 1 0-2.77a1.385 1.385 0 0 1 0 2.77"
/>
</svg>
);
};
export const ConnectionTrue: React.FC<IconSvgProps> = ({
size = 24,
width,
height,
...props
}) => (
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="1.5"
height={size || height || 24}
viewBox="0 0 24 24"
width={size || width || 24}
{...props}
>
<path
d="M12 20h.012M8.25 17c2-2 5.5-2 7.5 0m2.75-3c-3.768-3.333-9-3.333-13 0M2 11c3.158-2.667 6.579-4 10-4m3 .5s1 0 2 2c0 0 2.477-3.9 5-5.5"
color="currentColor"
/>
</svg>
);
export const ConnectionFalse: React.FC<IconSvgProps> = ({
size = 24,
width,
height,
...props
}) => (
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="1.5"
height={size || height || 24}
viewBox="0 0 24 24"
width={size || width || 24}
{...props}
>
<path
d="M12 18h.012M8.25 15c2-2 5.5-2 7.5 0m2.75-3a11 11 0 0 0-.231-.199M5.5 12c2.564-2.136 5.634-2.904 8.5-2.301M2 9c3.466-2.927 7.248-4.247 11-3.962M22 5l-6 6m6 0-6-6"
color="currentColor"
/>
</svg>
);
export const ConnectionPending: React.FC<IconSvgProps> = ({
size = 24,
width,
height,
...props
}) => (
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
stroke="currentColor"
strokeWidth="1.05"
height={size || height || 24}
viewBox="0 0 24 24"
width={size || width || 24}
{...props}
>
<g fill="none" stroke="currentColor" strokeWidth="1.05">
<circle cx="12" cy="18" r="2" />
<path strokeOpacity=".2" d="M7.757 13.757a6 6 0 0 1 8.486 0" />
<path
strokeOpacity=".2"
d="M4.929 10.93c3.905-3.905 10.237-3.905 14.142 0"
opacity=".8"
/>
<path
strokeOpacity=".2"
d="M2.101 8.1c5.467-5.468 14.331-5.468 19.798 0"
opacity=".8"
/>
</g>
</svg>
);
export const SuccessIcon: React.FC<IconSvgProps> = ({
size = 24,
width,
height,
...props
}) => (
<svg
width={size || width || 24}
height={size || height || 24}
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<path
d="M12 2C6.49 2 2 6.49 2 12C2 17.51 6.49 22 12 22C17.51 22 22 17.51 22 12C22 6.49 17.51 2 12 2ZM16.78 9.7L11.11 15.37C10.97 15.51 10.78 15.59 10.58 15.59C10.38 15.59 10.19 15.51 10.05 15.37L7.22 12.54C6.93 12.25 6.93 11.77 7.22 11.48C7.51 11.19 7.99 11.19 8.28 11.48L10.58 13.78L15.72 8.64C16.01 8.35 16.49 8.35 16.78 8.64C17.07 8.93 17.07 9.4 16.78 9.7Z"
fill="currentColor"
/>
</svg>
);
export const ArrowUpIcon: React.FC<IconSvgProps> = ({
size,
height,
width,
...props
}) => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
height={size || height || "1em"}
viewBox="0 0 12 12"
width={size || width || "1em"}
aria-hidden="true"
focusable="false"
role="presentation"
{...props}
>
<path
d="M3 7.5L6 4.5L9 7.5"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="1.5"
/>
</svg>
);
};
export const ArrowDownIcon: React.FC<IconSvgProps> = ({
size,
height,
width,
...props
}) => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
height={size || height || "1em"}
viewBox="0 0 12 12"
width={size || width || "1em"}
aria-hidden="true"
focusable="false"
role="presentation"
{...props}
>
<path
d="M3 4.5L6 7.5L9 4.5"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="1.5"
/>
</svg>
);
};
export const ChevronsLeftRightIcon: React.FC<IconSvgProps> = ({
size,
height,
width,
...props
}) => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
height={size || height || 24}
viewBox="0 0 24 24"
width={size || width || 24}
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className="lucide lucide-chevrons-left-right ml-2 h-4 w-4 rotate-90"
{...props}
>
<path d="m9 7-5 5 5 5M15 7l5 5-5 5" />
</svg>
);
};
export const PlusCircleIcon: React.FC<IconSvgProps> = ({
size,
height,
width,
...props
}) => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
height={size || height || 15}
viewBox="0 0 15 15"
width={size || width || 15}
className="mr-2 size-4"
{...props}
>
<path
d="M7.5.877a6.623 6.623 0 1 0 0 13.246A6.623 6.623 0 0 0 7.5.877ZM1.827 7.5a5.673 5.673 0 1 1 11.346 0 5.673 5.673 0 0 1-11.346 0ZM7.5 4a.5.5 0 0 1 .5.5V7h2.5a.5.5 0 1 1 0 1H8v2.5a.5.5 0 0 1-1 0V8H4.5a.5.5 0 0 1 0-1H7V4.5a.5.5 0 0 1 .5-.5Z"
fill="currentColor"
fillRule="evenodd"
clipRule="evenodd"
/>
</svg>
);
};
export const CustomFilterIcon: React.FC<IconSvgProps> = ({
size,
height,
width,
...props
}) => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
height={size || height || 16}
width={size || width || 16}
viewBox="0 0 24 24"
{...props}
>
<g fill="none" stroke="currentColor" strokeWidth="1.5">
<path d="M9.5 14a3 3 0 1 1 0 6 3 3 0 0 1 0-6Zm5-10a3 3 0 1 0 0 6 3 3 0 0 0 0-6Z" />
<path strokeLinecap="round" d="M15 16.959h7m-13-10H2m0 10h2m18-10h-2" />
</g>
</svg>
);
};
export const SaveIcon: React.FC<IconSvgProps> = ({
size,
height,
width,
...props
}) => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
height={size || height || 48}
viewBox="0 0 24 24"
width={size || width || 48}
aria-hidden="true"
{...props}
>
<path
d="m20.71 9.29l-6-6a1 1 0 0 0-.32-.21A1.1 1.1 0 0 0 14 3H6a3 3 0 0 0-3 3v12a3 3 0 0 0 3 3h12a3 3 0 0 0 3-3v-8a1 1 0 0 0-.29-.71M9 5h4v2H9Zm6 14H9v-3a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1Zm4-1a1 1 0 0 1-1 1h-1v-3a3 3 0 0 0-3-3h-4a3 3 0 0 0-3 3v3H6a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1h1v3a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V6.41l4 4Z"
fill="currentColor"
/>
</svg>
);
};
export const AddIcon: React.FC<IconSvgProps> = ({
size,
height,
width,
...props
}) => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
height={size || height || 20}
viewBox="0 0 24 24"
width={size || width || 20}
aria-hidden="true"
{...props}
>
<path
fill="currentColor"
fillRule="evenodd"
d="M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2 2 6.477 2 12s4.477 10 10 10m.75-13a.75.75 0 0 0-1.5 0v2.25H9a.75.75 0 0 0 0 1.5h2.25V15a.75.75 0 0 0 1.5 0v-2.25H15a.75.75 0 0 0 0-1.5h-2.25z"
clipRule="evenodd"
/>
</svg>
);
};
export const ScheduleIcon: React.FC<IconSvgProps> = ({
size,
height,
width,
...props
}) => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width={size || width || 24}
height={size || height || 24}
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
aria-hidden="true"
{...props}
>
<path d="M21 7.5V6a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h3.5" />
<path d="M16 2v4M8 2v4M3 10h5" />
<path d="M17.5 17.5L16 16.3V14" />
<circle cx="16" cy="16" r="6" />
</svg>
);
};
export const InfoIcon: React.FC<IconSvgProps> = ({
size = 24,
width,
height,
...props
}) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width={size || width}
height={size || height}
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
{...props}
>
<circle cx="12" cy="12" r="10" />
<path d="M12 16v-4M12 8h.01" />
</svg>
);
@@ -0,0 +1,63 @@
import AWSLogo from "./aws.svg";
import CISLogo from "./cis.svg";
import CISALogo from "./cisa.svg";
import ENSLogo from "./ens.png";
import FedRAMPLogo from "./fedramp.svg";
import FFIECLogo from "./ffiec.svg";
import GDPRLogo from "./gdpr.svg";
import GxPLogo from "./gxp-aws.svg";
import HIPAALogo from "./hipaa.svg";
import ISOLogo from "./iso-27001.svg";
import MITRELogo from "./mitre-attack.svg";
import NISTLogo from "./nist.svg";
import PCILogo from "./pci-dss.svg";
import RBILogo from "./rbi.svg";
import SOC2Logo from "./soc2.svg";
export const getComplianceIcon = (complianceTitle: string) => {
if (complianceTitle.toLowerCase().includes("aws")) {
return AWSLogo;
}
if (complianceTitle.toLowerCase().includes("cisa")) {
return CISALogo;
}
if (complianceTitle.toLowerCase().includes("cis")) {
return CISLogo;
}
if (complianceTitle.toLowerCase().includes("ens")) {
return ENSLogo;
}
if (complianceTitle.toLowerCase().includes("ffiec")) {
return FFIECLogo;
}
if (complianceTitle.toLowerCase().includes("fedramp")) {
return FedRAMPLogo;
}
if (complianceTitle.toLowerCase().includes("gdpr")) {
return GDPRLogo;
}
if (complianceTitle.toLowerCase().includes("gxp")) {
return GxPLogo;
}
if (complianceTitle.toLowerCase().includes("hipaa")) {
return HIPAALogo;
}
if (complianceTitle.toLowerCase().includes("iso")) {
return ISOLogo;
}
if (complianceTitle.toLowerCase().includes("mitre")) {
return MITRELogo;
}
if (complianceTitle.toLowerCase().includes("nist")) {
return NISTLogo;
}
if (complianceTitle.toLowerCase().includes("pci")) {
return PCILogo;
}
if (complianceTitle.toLowerCase().includes("rbi")) {
return RBILogo;
}
if (complianceTitle.toLowerCase().includes("soc2")) {
return SOC2Logo;
}
};
+38
View File
@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 304 182" style="enable-background:new 0 0 304 182;" xml:space="preserve">
<style type="text/css">
.st0{fill:#252F3E;}
.st1{fill-rule:evenodd;clip-rule:evenodd;fill:#FF9900;}
</style>
<g>
<path class="st0" d="M86.4,66.4c0,3.7,0.4,6.7,1.1,8.9c0.8,2.2,1.8,4.6,3.2,7.2c0.5,0.8,0.7,1.6,0.7,2.3c0,1-0.6,2-1.9,3l-6.3,4.2
c-0.9,0.6-1.8,0.9-2.6,0.9c-1,0-2-0.5-3-1.4C76.2,90,75,88.4,74,86.8c-1-1.7-2-3.6-3.1-5.9c-7.8,9.2-17.6,13.8-29.4,13.8
c-8.4,0-15.1-2.4-20-7.2c-4.9-4.8-7.4-11.2-7.4-19.2c0-8.5,3-15.4,9.1-20.6c6.1-5.2,14.2-7.8,24.5-7.8c3.4,0,6.9,0.3,10.6,0.8
c3.7,0.5,7.5,1.3,11.5,2.2v-7.3c0-7.6-1.6-12.9-4.7-16c-3.2-3.1-8.6-4.6-16.3-4.6c-3.5,0-7.1,0.4-10.8,1.3c-3.7,0.9-7.3,2-10.8,3.4
c-1.6,0.7-2.8,1.1-3.5,1.3c-0.7,0.2-1.2,0.3-1.6,0.3c-1.4,0-2.1-1-2.1-3.1v-4.9c0-1.6,0.2-2.8,0.7-3.5c0.5-0.7,1.4-1.4,2.8-2.1
c3.5-1.8,7.7-3.3,12.6-4.5c4.9-1.3,10.1-1.9,15.6-1.9c11.9,0,20.6,2.7,26.2,8.1c5.5,5.4,8.3,13.6,8.3,24.6V66.4z M45.8,81.6
c3.3,0,6.7-0.6,10.3-1.8c3.6-1.2,6.8-3.4,9.5-6.4c1.6-1.9,2.8-4,3.4-6.4c0.6-2.4,1-5.3,1-8.7v-4.2c-2.9-0.7-6-1.3-9.2-1.7
c-3.2-0.4-6.3-0.6-9.4-0.6c-6.7,0-11.6,1.3-14.9,4c-3.3,2.7-4.9,6.5-4.9,11.5c0,4.7,1.2,8.2,3.7,10.6
C37.7,80.4,41.2,81.6,45.8,81.6z M126.1,92.4c-1.8,0-3-0.3-3.8-1c-0.8-0.6-1.5-2-2.1-3.9L96.7,10.2c-0.6-2-0.9-3.3-0.9-4
c0-1.6,0.8-2.5,2.4-2.5h9.8c1.9,0,3.2,0.3,3.9,1c0.8,0.6,1.4,2,2,3.9l16.8,66.2l15.6-66.2c0.5-2,1.1-3.3,1.9-3.9c0.8-0.6,2.2-1,4-1
h8c1.9,0,3.2,0.3,4,1c0.8,0.6,1.5,2,1.9,3.9l15.8,67l17.3-67c0.6-2,1.3-3.3,2-3.9c0.8-0.6,2.1-1,3.9-1h9.3c1.6,0,2.5,0.8,2.5,2.5
c0,0.5-0.1,1-0.2,1.6c-0.1,0.6-0.3,1.4-0.7,2.5l-24.1,77.3c-0.6,2-1.3,3.3-2.1,3.9c-0.8,0.6-2.1,1-3.8,1h-8.6c-1.9,0-3.2-0.3-4-1
c-0.8-0.7-1.5-2-1.9-4L156,23l-15.4,64.4c-0.5,2-1.1,3.3-1.9,4c-0.8,0.7-2.2,1-4,1H126.1z M254.6,95.1c-5.2,0-10.4-0.6-15.4-1.8
c-5-1.2-8.9-2.5-11.5-4c-1.6-0.9-2.7-1.9-3.1-2.8c-0.4-0.9-0.6-1.9-0.6-2.8v-5.1c0-2.1,0.8-3.1,2.3-3.1c0.6,0,1.2,0.1,1.8,0.3
c0.6,0.2,1.5,0.6,2.5,1c3.4,1.5,7.1,2.7,11,3.5c4,0.8,7.9,1.2,11.9,1.2c6.3,0,11.2-1.1,14.6-3.3c3.4-2.2,5.2-5.4,5.2-9.5
c0-2.8-0.9-5.1-2.7-7c-1.8-1.9-5.2-3.6-10.1-5.2L246,52c-7.3-2.3-12.7-5.7-16-10.2c-3.3-4.4-5-9.3-5-14.5c0-4.2,0.9-7.9,2.7-11.1
c1.8-3.2,4.2-6,7.2-8.2c3-2.3,6.4-4,10.4-5.2c4-1.2,8.2-1.7,12.6-1.7c2.2,0,4.5,0.1,6.7,0.4c2.3,0.3,4.4,0.7,6.5,1.1
c2,0.5,3.9,1,5.7,1.6c1.8,0.6,3.2,1.2,4.2,1.8c1.4,0.8,2.4,1.6,3,2.5c0.6,0.8,0.9,1.9,0.9,3.3v4.7c0,2.1-0.8,3.2-2.3,3.2
c-0.8,0-2.1-0.4-3.8-1.2c-5.7-2.6-12.1-3.9-19.2-3.9c-5.7,0-10.2,0.9-13.3,2.8c-3.1,1.9-4.7,4.8-4.7,8.9c0,2.8,1,5.2,3,7.1
c2,1.9,5.7,3.8,11,5.5l14.2,4.5c7.2,2.3,12.4,5.5,15.5,9.6c3.1,4.1,4.6,8.8,4.6,14c0,4.3-0.9,8.2-2.6,11.6
c-1.8,3.4-4.2,6.4-7.3,8.8c-3.1,2.5-6.8,4.3-11.1,5.6C264.4,94.4,259.7,95.1,254.6,95.1z"/>
<g>
<path class="st1" d="M273.5,143.7c-32.9,24.3-80.7,37.2-121.8,37.2c-57.6,0-109.5-21.3-148.7-56.7c-3.1-2.8-0.3-6.6,3.4-4.4
c42.4,24.6,94.7,39.5,148.8,39.5c36.5,0,76.6-7.6,113.5-23.2C274.2,133.6,278.9,139.7,273.5,143.7z"/>
<path class="st1" d="M287.2,128.1c-4.2-5.4-27.8-2.6-38.5-1.3c-3.2,0.4-3.7-2.4-0.8-4.5c18.8-13.2,49.7-9.4,53.3-5
c3.6,4.5-1,35.4-18.6,50.2c-2.7,2.3-5.3,1.1-4.1-1.9C282.5,155.7,291.4,133.4,287.2,128.1z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.4 KiB

+9
View File
@@ -0,0 +1,9 @@
<svg width="40" height="45" viewBox="0 0 40 45" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<rect width="40" height="44.8" fill="url(#pattern0)"/>
<defs>
<pattern id="pattern0" patternContentUnits="objectBoundingBox" width="1" height="1">
<use xlink:href="#image0_407_5880" transform="matrix(0.00282945 0 0 0.0025 0.00666748 0)"/>
</pattern>
<image id="image0_407_5880" width="400" height="400" xlink:href="data:image/png;base64,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 truncated
</defs>
</svg>

After

Width:  |  Height:  |  Size: 128 KiB

+2
View File
@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="465.74" height="466.54" version="1.1" viewBox="0 0 465.73919 466.54269" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><defs><clipPath id="a"><path d="m72 660.3h59.6v59.701h-59.6z"/></clipPath></defs><g transform="matrix(1.3333 0 0 -1.3333 97.411 1153.7)"><g transform="matrix(6 0 0 6 -507.97 -3451.8)"><g clip-path="url(#a)"><g transform="matrix(.010781 0 0 -.0108 72 720)"><g transform="translate(5445,2745)"><path d="m0 0c0 1491-1209 2700-2700 2700s-2700-1209-2700-2700 1209-2700 2700-2700 2700 1209 2700 2700" fill="#fff"/></g></g><g transform="matrix(.010781 0 0 -.0108 72 720)"><g transform="translate(2745,110)"><path d="m0 0c-1453 0-2635 1182-2635 2635s1182 2635 2635 2635 2635-1182 2635-2635-1182-2635-2635-2635m0 5114c-1367 0-2479-1112-2479-2479s1112-2479 2479-2479 2479 1112 2479 2479-1112 2479-2479 2479" fill="#005288"/></g></g><g transform="matrix(.010781 0 0 -.0108 72 720)"><g transform="translate(2893,3725)"><path d="m0 0c84-16 173-27 261-30v21c-215 10-375 69-481 126-15-15-29-31-43-46 74-27 165-52 263-71m-206 131c36 37 73 73 113 107v-71c0-38 26-69 58-69 33 0 59 31 59 69v163c18 14 36 26 55 39v-253c0-38 26-69 58-69s59 31 59 69v324c21 13 43 24 65 35v-466c-208 10-363 68-467 122m147-401 39 19 21-12-53-31zm22-82 42-33-27-21zm22-79 39 31 39-30-39-149zm84 180v-25l-21 13zm-63 12 63 31v-16l-45-26zm65-167-27 21 41 33zm-47 129 68-39-67-54-68 54zm66-11 6 3 8 4-3-13zm-44-2033-59 33-32 45c-18-8-66-29-110-45-45-17-106-36-106-36 114 1 141 1 146 1h1c5 1 34 1 160 2m1601 1341c0 991-804 1794-1795 1794-207 0-407-35-592-99 0 0 346-277 493-426-102-96-194-204-274-322-63-93-117-191-162-292l-860 859 29-350c-267-313-428-720-428-1164 0-991 803-1794 1794-1794s1795 803 1795 1794m-2694-1457 400 215-231 237 170-7-140 233h9c105 0 203 34 283 92 184-29 447-64 695-79-1-2-2-3-3-5-3-7-7-14-11-22-5-11-11-22-17-33-7-15-15-30-22-45-9-17-18-35-27-52-9-20-19-40-29-60 0 0 43-59 126-84 77-24 162-1 212 66 16 21 28 45 28 70 0 0 132-198 100-311-32-114-284-102-284-102h-84l-76-80c-20-21-48-34-77-34h-1022zm-330 1679 203-215v-214l-183 193zm621-287c97-35 162-128 162-231v-9c-33-5-59-27-70-57h-157c-6 15-15 28-26 38-15 12-35 20-56 20-37 0-69-24-82-58h-43l-87 93v300l-270 286-23 270 272-287c-6-12-9-25-9-39 0-48 39-87 87-87s87 39 87 87-39 87-87 87c-13 0-24-3-35-8l-323 342-23 270 303-320c-6-12-9-25-9-39 0-48 39-87 87-87s87 39 87 87-39 87-87 87c-12 0-24-3-35-8l-354 377-27 312 698-697v-722zm-302 685c15 0 27-12 27-27s-12-28-27-28c-16 0-28 13-28 28s12 27 28 27m0-332c15 0 27-12 27-27 0-16-12-28-27-28-16 0-28 12-28 28 0 15 12 27 28 27m155-652c15 0 28-12 28-27 0-16-13-28-28-28s-27 12-27 28c0 15 12 27 27 27m320 0c15 0 28-12 28-27 0-16-13-28-28-28s-27 12-27 28c0 15 12 27 27 27m19 816v-421c93-79 152-196 152-327 0-133-60-252-156-330-22-18-46-34-72-48-59-31-127-49-199-49-152 0-285 79-361 198v1c-6 9-133 228-133 228l-16 187 296-312h68c12-33 44-57 82-57 21 0 40 7 55 20 12 10 21 22 27 37h157c12-33 44-57 81-57 48 0 87 39 87 87 0 30-16 57-40 73 1 7 2 15 2 22 0 121-73 231-182 279v622zm2039-574v-467s-489-126-1095-126h-38c-272 2-605 44-839 78 87 88 141 208 141 341 0 134-55 261-152 352v15h1889c-27 251-107 490-232 702v-51h-154v-78h-86v-91h-52v-55c0-8-2-15-7-22-8-12-21-19-35-19s-27 7-35 19c-4 7-6 14-6 22v55h-52v90h-63v-295h-398v428l-120-59c-33-111-66-220-97-330 9-6 15-16 15-28 0-20-16-35-35-35s-35 15-35 35c0 12 6 22 15 28-30 103-60 206-91 309l-2-1-27-13v103h-46l-21-352-1-18h-141l-1 18-21 352h-109c-46-76-86-156-119-238l-63 63c188 438 541 789 981 975 665-281 1132-940 1132-1707" fill="#005288"/></g></g><g transform="matrix(.010781 0 0 -.0108 72 720)"><g transform="translate(593,2862)"><path d="m0 0-2-68c37-1 67 9 92 27 24 19 38 45 40 79 4 41-9 74-38 98s-70 38-123 42c-54 5-98-3-130-22s-50-49-53-91c-6-70 34-111 120-124l10 66c-49 6-73 24-70 53 1 15 10 27 26 34 17 8 49 10 95 7 41-4 69-10 83-20 15-9 22-22 21-39-3-27-26-41-71-42" fill="#005288"/></g></g><g transform="matrix(.010781 0 0 -.0108 72 720)"><g transform="translate(374,2785)"><path d="m0 0 3-81 124-43-121-55 3-72 190 100 142 5-3 73-142-5z" fill="#005288"/></g></g><g transform="matrix(.010781 0 0 -.0108 72 720)"><g transform="translate(388,2487)"><path d="m0 0 18-113c4-27 9-47 16-60 6-12 17-22 33-29 15-7 34-9 55-6 37 6 58 26 64 59 19-36 49-50 90-44 21 3 39 11 52 24 13 12 21 26 24 42 2 16 2 38-3 65l-17 113zm145-49 4-24c3-23 1-39-6-46-8-7-17-12-28-14-10-1-19 0-28 5s-15 17-18 37l-4 29zm141 22 5-36c6-34-6-53-35-57-16-3-28 0-36 7s-13 20-16 36l-5 36z" fill="#005288"/></g></g><g transform="matrix(.010781 0 0 -.0108 72 720)"><g transform="translate(444,2178)"><path d="m0 0 56-196 55 15-36 125 75 22 28-97 55 15-27 98 79 22 36-125 57 17-56 196z" fill="#005288"/></g></g><g transform="matrix(.010781 0 0 -.0108 72 720)"><g transform="translate(523,1919)"><path d="m0 0 41-97c8-19 15-33 21-42 6-10 15-18 27-25s25-11 41-11 32 3 48 10c39 17 60 43 63 78l159 1-31 73-140-10-14 34 122 52-29 67zm78-47 87 37 13-30c11-27 2-46-28-59-10-4-19-6-28-4-8 1-14 4-19 8-4 4-9 11-13 21z" fill="#005288"/></g></g><g transform="matrix(.010781 0 0 -.0108Line truncated

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

@@ -0,0 +1,9 @@
<svg width="41" height="45" viewBox="0 0 41 45" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<rect x="0.666626" width="40" height="44.8" fill="url(#pattern0)"/>
<defs>
<pattern id="pattern0" patternContentUnits="objectBoundingBox" width="1" height="1">
<use xlink:href="#image0_407_6053" transform="matrix(0.00396921 0 0 0.00354393 0.0880347 0.104852)"/>
</pattern>
<image id="image0_407_6053" width="204" height="223" xlink:href="data:image/png;base64,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 truncated
</defs>
</svg>

After

Width:  |  Height:  |  Size: 47 KiB

+9
View File
@@ -0,0 +1,9 @@
<svg width="40" height="45" viewBox="0 0 40 45" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<rect width="40" height="44.8" fill="url(#pattern0)"/>
<defs>
<pattern id="pattern0" patternContentUnits="objectBoundingBox" width="1" height="1">
<use xlink:href="#image0_407_6036" transform="matrix(0.00583333 0 0 0.00520833 -0.0950001 0)"/>
</pattern>
<image id="image0_407_6036" width="204" height="192" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMwAAADACAIAAADHghveAAAMPmlDQ1BJQ0MgUHJvZmlsZQAASImVVwdYU8kWnluSkEBoAQSkhN4EkRpASggt9N5shCRAKDEGgoodXVRw7SICNnRVRMFKsyN2FsXeFwsqyrpYsCtvUkDXfeV7831z57//nPnPmXNn7r0DgNpxjkiUi6oDkCcsEMcG+9OTU1LppKcABxggAy1gzuHmi5jR0eEAlqH27+XddYBI2yv2Uq1/9v/XosHj53MBQKIhTuflc/MgPgAAXs0ViQsAIEp5s6kFIimGFWiJYYAQL5LiTDmuluJ0Od4js4mPZUHcDoCSCocjzgRA9RLk6YXcTKih2g+xo5AnEAKgRofYJy9vMg/iNIitoY0IYqk+I/0Hncy/aaYPa3I4mcNYPhdZUQoQ5ItyOdP/z3T875KXKxnyYQmrSpY4JFY6Z5i3mzmTw6RYBeI+YXpkFMSaEH8Q8GT2EKOULElIgtweNeDms2DOgA7EjjxOQBjEBhAHCXMjwxV8eoYgiA0xXCHoNEEBOx5iXYgX8fMD4xQ2m8STYxW+0MYMMYup4M9yxDK/Ul/3JTkJTIX+6yw+W6GPqRZlxSdBTIHYvFCQGAmxKsQO+TlxYQqbsUVZrMghG7EkVhq/OcSxfGGwv1wfK8wQB8Uq7Evz8ofmi23KErAjFXhfQVZ8iDw/WDuXI4sfzgW7xBcyE4Z0+PnJ4UNz4fEDAuVzx57xhQlxCp0PogL/WPlYnCLKjVbY46b83GApbwqxS35hnGIsnlgAF6RcH88QFUTHy+PEi7I5odHyePDlIBywQACgAwms6WAyyAaCzr6mPngn7wkCHCAGmYAP7BXM0IgkWY8QXuNAEfgTIj7IHx7nL+vlg0LIfx1m5Vd7kCHrLZSNyAFPIM4DYSAX3ktko4TD3hLBY8gI/uGdAysXxpsLq7T/3/ND7HeGCZlwBSMZ8khXG7IkBhIDiCHEIKINro/74F54OLz6weqEM3CPoXl8tyc8IXQRHhKuEboJtyYJisU/RRkBuqF+kCIX6T/mAreEmq64P+4N1aEyroPrA3vcBfph4r7QsytkWYq4pVmh/6T9txn88DQUdmRHMkoeQfYjW/88UtVW1XVYRZrrH/MjjzV9ON+s4Z6f/bN+yD4PtmE/W2KLsP3YGewEdg47jDUBOnYMa8Y6sCNSPLy6HstW15C3WFk8OVBH8A9/Q09Wmsl8xzrHXscv8r4C/jTpOxqwJoumiwWZWQV0Jvwi8OlsIddhFN3J0ckZAOn3Rf76ehMj+24gOh3fufl/AOB9bHBw8NB3LvQYAHvd4fZv+c5ZM+CnQxmAsy1cibhQzuHSCwG+JdTgTtMDRsAMWMP5OAE34AX8QCAIBVEgHqSAiTD6LLjOxWAqmAnmgRJQBpaDNaASbARbwA6wG+wDTeAwOAFOgwvgErgG7sDV0wNegH7wDnxGEISEUBEaoocYIxaIHeKEMBAfJBAJR2KRFCQNyUSEiASZicxHypCVSCWyGalF9iItyAnkHNKF3EIeIL3Ia+QTiqEqqBZqiFqio1EGykTD0Hh0ApqJTkGL0AXoUrQCrUF3oY3oCfQCeg3tRl+gAxjAlDEdzASzxxgYC4vCUrEMTIzNxkqxcqwGq8da4XO+gnVjfdhHnIjTcDpuD1dwCJ6Ac/Ep+Gx8CV6J78Ab8Xb8Cv4A78e/EagEA4IdwZPAJiQTMglTCSWEcsI2wkHCKbiXegjviESiDtGK6A73YgoxmziDuIS4nthAPE7sIj4iDpBIJD2SHcmbFEXikApIJaR1pF2kY6TLpB7SByVlJWMlJ6UgpVQloVKxUrnSTqWjSpeVnip9JquTLcie5CgyjzydvIy8ldxKvkjuIX+maFCsKN6UeEo2ZR6lglJPOUW5S3mjrKxsquyhHKMsUJ6rXKG8R/ms8gPljyqaKrYqLJXxKhKVpSrbVY6r3FJ5Q6VSLal+1FRqAXUptZZ6knqf+kGVpuqgylblqc5RrVJtVL2s+lKNrGahxlSbqFakVq62X+2iWp86Wd1SnaXOUZ+tXqXeon5DfUCDpjFGI0ojT2OJxk6NcxrPNEmalpqBmjzNBZpbNE9qPqJhNDMai8alzadtpZ2i9WgRtay02FrZWmVau7U6tfq1NbVdtBO1p2lXaR/R7tbBdCx12Dq5Ost09ulc1/k0wnAEcwR/xOIR9SMuj3ivO1LXT5evW6rboHtN95MeXS9QL0dvhV6T3j19XN9WP0Z/qv4G/VP6fSO1RnqN5I4sHblv5G0D1MDWINZghsEWgw6DAUMjw2BDkeE6w5OGfUY6Rn5G2UarjY4a9RrTjH2MBcarjY8ZP6dr05n0XHoFvZ3eb2JgEmIiMdls0mny2dTKNMG02LTB9J4ZxYxhlmG22qzNrN/c2DzCfKZ5nfltC7IFwyLLYq3FGYv3llaWSZYLLZssn1npWrGtiqzqrO5aU619radY11hftSHaMGxybNbbXLJFbV1ts2yrbC/aoXZudgK79XZdowijPEYJR9WMumGvYs+0L7Svs3/goOMQ7lDs0OTwcrT56NTRK0afGf3N0dUx13Gr450xmmNCxxSPaR3z2snWietU5XTVmeoc5DzHudn5lYudC99lg8tNV5prhOtC1zbXr27ubmK3erded3P3NPdq9xsMLUY0YwnjrAfBw99jjsdhj4+ebp4Fnvs8//Ky98rx2un1bKzVWP7YrWMfeZt6c7w3e3f70H3SfDb5dPua+HJ8a3wf+pn58fy2+T1l2jCzmbuYL/0d/cX+B/3fszxZs1jHA7CA4IDSgM5AzcCEwMrA+0GmQZlBdUH9wa7BM4KPhxBCwkJWhNxgG7K57Fp2f6h76KzQ9jCVsLiwyrCH4bbh4vDWCDQiNGJVxN1Ii0hhZFMUiGJHrYq6F20VPSX6UAwxJjqmKuZJ7JjYmbFn4mhxk+J2xr2L949fFn8nwTpBktCWqJY4PrE28X1SQNLKpO7k0cmzki+k6KcIUppTSamJqdtSB8YFjlszrme86/iS8dcnWE2YNuHcRP2JuROPTFKbxJm0P42QlpS2M+0LJ4pTwxlIZ6dXp/dzWdy13Bc8P95qXi/fm7+S/zTDO2NlxrNM78xVmb1ZvlnlWX0ClqBS8Co7JHtj9vucqJztOYO5SbkNeUp5aXktQk1hjrB9stHkaZO7RHaiElH3FM8pa6b0i8PE2/KR/An5zQVa8Ee+Q2It+UXyoNCnsKrww9TEqfunaUwTTuuYbjt98fSnRUFFv83AZ3BntM00mTlv5oNZzFmbZyOz02e3zTGbs2BOz9zguTvmUeblzPu92LF4ZfHb+UnzWxcYLpi74NEvwb/UlaiWiEtuLPRauHERvkiwqHOx8+J1i7+V8krPlzmWlZd9WcJdcv7XMb9W/Dq4NGNp5zK3ZRuWE5cLl19f4btix0qNlUUrH62KWNW4mr66dPXbNZPWnCt3Kd+4lrJWsra7IryieZ35uuXrvlRmVV6r8q9qqDaoXlz9fj1v/eUNfhvqNxpuLNv4aZNg083NwZsbayxryrcQtxRuebI1ceuZ3xi/1W7T31a27et24fbuHbE72mvda2t3GuxcVofWSep6d43fdWl3wO7mevv6zQ06DWV7wB7Jnud70/Ze3xe2r20/Y3/9AYsD1QdpB0sbkcbpjf1NWU3dzSnNXS2hLW2tXq0HDzkc2n7Y5HDVEe0jy45Sji44Onis6NjAcdHxvhOZJx61TWq7czL55NX2mPbOU2Gnzp4OOn3yDPPMsbPeZw+f8zzXcp5xvumC24XGDteOg7+7/n6w062z8aL7xeZLHpdau8Z2Hb3se/nElYArp6+yr164Fnmt63rC9Zs3xt/ovsm7+exW7q1Xtwtvf74z9y7hbuk99Xvl9w3u1/xh80dDt1v3kQcBDzoexj2884j76MXj/MdfehY8oT4pf2r8tPaZ07PDvUG9l56Pe97zQvTic1/Jnxp/Vr+0fnngL7+/OvqT+3teiV8Nvl7yRu/N9rcub9sGogfuv8t79/l96Qe9Dzs+Mj6e+ZT06ennqV9IXyq+2nxt/Rb27e5g3uCgiCPmyH4FMFjRjAwAXm8HgJoCAA2ezyjj5Oc/WUHkZ1YZAv8Jy8+IsuIGQD38f4/pg383NwDYsxUev6C+2ngAoqkAxHsA1Nl5uA6d1WTnSmkhwnPApuiv6Xnp4N8U+Znzh7h/boFU1QX83P4L2JB8Uu+mibsAAAA4ZVhJZk1NACoAAAAIAAGHaQAEAAAAAQAAABoAAAAAAAKgAgAEAAAAAQAAAMygAwAEAAAAAQAAAMAAAAAA++tzoQAAOoJJREFUeAHtfQl8VNX1/5vJZDLZE7aQhS0QQkjYdxAEQSm1imgpVYvir8WftmJrsa0/qv/y68LP2lIXWrVaF35qXfhVVKqIoERQ9kUgIZCwk4QEAglZJ8lk5v8997735s17byaTMMlMMvM+85l58959dzn3+84995xzzzU4HA4hdIQo0JEUMHZk5qG8QxQgCoRAFsJBh1MgBLIOJ3GogBDIQhjocAqEQNbhJA4VEAJZCAMdToEQyDqcxKECQiALYaDDKRACWYeTOFRACGQhDHQ4BUIg85LEtsZmW2VtvZepQ8mUFAiBTEkNt+cll+r+9+MDazcdcZsidMM9BUIgc08bwc7v7c07//jfc+9/ZqfdavOQOnTLHQVM7m4E93UOLyOGyE1fF65Yuzu/pE7oERHcNGl/60Mgc0c747GyS+9uOPzk5pNWa4slwWxtanGXNHTdMwVCINOlj3HznsIX1h9Zn39RMBstljDB4BCMBt2koYutUiAEMh0SfZR7dNEL262NdkIYx5bDINjsNfVWndShS61RICT461CovKrWWtlsMYeJCONJQqTSIZVXl4KcclzALine truncated
</defs>
</svg>

After

Width:  |  Height:  |  Size: 24 KiB

+9
View File
@@ -0,0 +1,9 @@
<svg width="41" height="45" viewBox="0 0 41 45" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<rect x="0.333374" width="40" height="44.8" fill="url(#pattern0)"/>
<defs>
<pattern id="pattern0" patternContentUnits="objectBoundingBox" width="1" height="1">
<use xlink:href="#image0_407_5914" transform="matrix(0.000711563 0 0 0.000635324 -0.0600001 0)"/>
</pattern>
<image id="image0_407_5914" width="1574" height="1574" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABiYAAAYmCAYAAAAjWBbXAAAMPmlDQ1BJQ0MgUHJvZmlsZQAASImVVwdYU8kWnluSkEBoAQSkhN4EkRpASggt9N5shCRAKDEGgoodXVRw7SICNnRVRMFKsyN2FsXeFwsqyrpYsCtvUkDXfeV7831z57//nPnPmXNn7r0DgNpxjkiUi6oDkCcsEMcG+9OTU1LppKcABxggAy1gzuHmi5jR0eEAlqH27+XddYBI2yv2Uq1/9v/XosHj53MBQKIhTuflc/MgPgAAXs0ViQsAIEp5s6kFIimGFWiJYYAQL5LiTDmuluJ0Od4js4mPZUHcDoCSCocjzgRA9RLk6YXcTKih2g+xo5AnEAKgRofYJy9vMg/iNIitoY0IYqk+I/0Hncy/aaYPa3I4mcNYPhdZUQoQ5ItyOdP/z3T875KXKxnyYQmrSpY4JFY6Z5i3mzmTw6RYBeI+YXpkFMSaEH8Q8GT2EKOULElIgtweNeDms2DOgA7EjjxOQBjEBhAHCXMjwxV8eoYgiA0xXCHoNEEBOx5iXYgX8fMD4xQ2m8STYxW+0MYMMYup4M9yxDK/Ul/3JTkJTIX+6yw+W6GPqRZlxSdBTIHYvFCQGAmxKsQO+TlxYQqbsUVZrMghG7EkVhq/OcSxfGGwv1wfK8wQB8Uq7Evz8ofmi23KErAjFXhfQVZ8iDw/WDuXI4sfzgW7xBcyE4Z0+PnJ4UNz4fEDAuVzx57xhQlxCp0PogL/WPlYnCLKjVbY46b83GApbwqxS35hnGIsnlgAF6RcH88QFUTHy+PEi7I5odHyePDlIBywQACgAwms6WAyyAaCzr6mPngn7wkCHCAGmYAP7BXM0IgkWY8QXuNAEfgTIj7IHx7nL+vlg0LIfx1m5Vd7kCHrLZSNyAFPIM4DYSAX3ktko4TD3hLBY8gI/uGdAysXxpsLq7T/3/ND7HeGCZlwBSMZ8khXG7IkBhIDiCHEIKINro/74F54OLz6weqEM3CPoXl8tyc8IXQRHhKuEboJtyYJisU/RRkBuqF+kCIX6T/mAreEmq64P+4N1aEyroPrA3vcBfph4r7QsytkWYq4pVmh/6T9txn88DQUdmRHMkoeQfYjW/88UtVW1XVYRZrrH/MjjzV9ON+s4Z6f/bN+yD4PtmE/W2KLsP3YGewEdg47jDUBOnYMa8Y6sCNSPLy6HstW15C3WFk8OVBH8A9/Q09Wmsl8xzrHXscv8r4C/jTpOxqwJoumiwWZWQV0Jvwi8OlsIddhFN3J0ckZAOn3Rf76ehMj+24gOh3fufl/AOB9bHBw8NB3LvQYAHvd4fZv+c5ZM+CnQxmAsy1cibhQzuHSCwG+JdTgTtMDRsAMWMP5OAE34AX8QCAIBVEgHqSAiTD6LLjOxWAqmAnmgRJQBpaDNaASbARbwA6wG+wDTeAwOAFOgwvgErgG7sDV0wNegH7wDnxGEISEUBEaoocYIxaIHeKEMBAfJBAJR2KRFCQNyUSEiASZicxHypCVSCWyGalF9iItyAnkHNKF3EIeIL3Ia+QTiqEqqBZqiFqio1EGykTD0Hh0ApqJTkGL0AXoUrQCrUF3oY3oCfQCeg3tRl+gAxjAlDEdzASzxxgYC4vCUrEMTIzNxkqxcqwGq8da4XO+gnVjfdhHnIjTcDpuD1dwCJ6Ac/Ep+Gx8CV6J78Ab8Xb8Cv4A78e/EagEA4IdwZPAJiQTMglTCSWEcsI2wkHCKbiXegjviESiDtGK6A73YgoxmziDuIS4nthAPE7sIj4iDpBIJD2SHcmbFEXikApIJaR1pF2kY6TLpB7SByVlJWMlJ6UgpVQloVKxUrnSTqWjSpeVnip9JquTLcie5CgyjzydvIy8ldxKvkjuIX+maFCsKN6UeEo2ZR6lglJPOUW5S3mjrKxsquyhHKMsUJ6rXKG8R/ms8gPljyqaKrYqLJXxKhKVpSrbVY6r3FJ5Q6VSLal+1FRqAXUptZZ6knqf+kGVpuqgylblqc5RrVJtVL2s+lKNrGahxlSbqFakVq62X+2iWp86Wd1SnaXOUZ+tXqXeon5DfUCDpjFGI0ojT2OJxk6NcxrPNEmalpqBmjzNBZpbNE9qPqJhNDMai8alzadtpZ2i9WgRtay02FrZWmVau7U6tfq1NbVdtBO1p2lXaR/R7tbBdCx12Dq5Ost09ulc1/k0wnAEcwR/xOIR9SMuj3ivO1LXT5evW6rboHtN95MeXS9QL0dvhV6T3j19XN9WP0Z/qv4G/VP6fSO1RnqN5I4sHblv5G0D1MDWINZghsEWgw6DAUMjw2BDkeE6w5OGfUY6Rn5G2UarjY4a9RrTjH2MBcarjY8ZP6dr05n0XHoFvZ3eb2JgEmIiMdls0mny2dTKNMG02LTB9J4ZxYxhlmG22qzNrN/c2DzCfKZ5nfltC7IFwyLLYq3FGYv3llaWSZYLLZssn1npWrGtiqzqrO5aU619radY11hftSHaMGxybNbbXLJFbV1ts2yrbC/aoXZudgK79XZdowijPEYJR9WMumGvYs+0L7Svs3/goOMQ7lDs0OTwcrT56NTRK0afGf3N0dUx13Gr450xmmNCxxSPaR3z2snWietU5XTVmeoc5DzHudn5lYudC99lg8tNV5prhOtC1zbXr27ubmK3erded3P3NPdq9xsMLUY0YwnjrAfBw99jjsdhj4+ebp4Fnvs8//Ky98rx2un1bKzVWP7YrWMfeZt6c7w3e3f70H3SfDb5dPua+HJ8a3wf+pn58fy2+T1l2jCzmbuYL/0d/cX+B/3fszxZs1jHA7CA4IDSgM5AzcCEwMrA+0GmQZlBdUH9wa7BM4KPhxBCwkJWhNxgG7K57Fp2f6h76KzQ9jCVsLiwyrCH4bbh4vDWCDQiNGJVxN1Ii0hhZFMUiGJHrYq6F20VPSX6UAwxJjqmKuZJ7JjYmbFn4mhxk+J2xr2L949fFn8nwTpBktCWqJY4PrE28X1SQNLKpO7k0cmzki+k6KcIUppTSamJqdtSB8YFjlszrme86/iS8dcnWE2YNuHcRP2JuROPTFKbxJm0P42QlpS2M+0LJ4pTwxlIZ6dXp/dzWdy13Bc8P95qXi/fm7+S/zTDO2NlxrNM78xVmb1ZvlnlWX0ClqBS8Co7JHtj9vucqJztOYO5SbkNeUp5aXktQk1hjrB9stHkaZO7RHaiElH3FM8pa6b0i8PE2/KR/An5zQVa8Ee+Q2It+UXyoNCnsKrww9TEqfunaUwTTuuYbjt98fSnRUFFv83AZ3BntM00mTlv5oNZzFmbZyOz02e3zTGbs2BOz9zguTvmUeblzPu92LF4ZfHb+UnzWxcYLpi74NEvwb/UlaiWiEtuLPRauHERvkiwqHOx8+J1i7+V8krPlzmWlZd9WcJdcv7XMb9W/Dq4NGNp5zK3ZRuWE5cLl19f4btix0qNlUUrH62KWNW4mr66dPXbNZPWnCt3Kd+4lrJWsra7IryieZ35uuXrvlRmVV6r8q9qqDaoXlz9fj1v/eUNfhvqNxpuLNv4aZNg083NwZsbayxryrcQtxRuebI1ceuZ3xi/1W7T31a27et24fbuHbE72mvda2t3GuxcVofWSep6d43fdWl3wO7mevv6zQ06DWV7wB7Jnud70/Ze3xe2r20/Y3/9AYsD1QdpB0sbkcbpjf1NWU3dzSnNXS2hLW2tXq0HDzkc2n7Y5HDVEe0jy45Sji44Onis6NjAcdHxvhOZJx61TWq7czL55NX2mPbOU2Gnzp4OOn3yDPPMsbPeZw+f8zzXcp5xvumC24XGDteOg7+7/n6w062z8aL7xeZLHpdau8Z2Hb3se/nElYArp6+yr164Fnmt63rC9Zs3xt/ovsm7+exW7q1Xtwtvf74z9y7hbuk99Xvl9w3u1/xh80dDt1v3kQcBDzoexj2884j76MXj/MdfehY8oT4pf2r8tPaZ07PDvUG9l56Pe97zQvTic1/Jnxp/Vr+0fnngL7+/OvqT+3teiV8Nvl7yRu/N9rcub9sGogfuv8t79/l96Qe9Dzs+Mj6e+ZT06ennqV9IXyq+2nxt/Rb27e5g3uCgiCPmyH4FMFjRjAwAXm8HgJoCAA2ezyjj5Oc/WUHkZ1YZAv8Jy8+IsuIGQD38f4/pg383NwDYsxUev6C+2ngAoqkAxHsA1Nl5uA6d1WTnSmkhwnPApuiv6Xnp4N8U+Znzh7h/boFU1QX83P4L2JB8Uu+mibsAAAA4ZVhJZk1NACoAAAAIAAGHaQAEAAAAAQAAABoAAAAAAAKgAgAEAAAAAQAABiagAwAEAAAAAQAABiYAAAAAfx9d+QAAQABJREFUeAHs3X+UXVd9GPp9rrDsYBmJH440BiOlBjPjH9Fl5FBskDSOUzvkmVgk6WtoaKw06aJOX5ZVqMEeuXhKwEDpq5U/CLwX+hCs1xdnrcaIFq8A7nqMMQE3gBiRUBQTeFKh1oDtVooN6Id17zt77CuPpPlxf517z4/PWUtrZu49Z+/v97Ov5sf53r13CA4CBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAALine truncated
</defs>
</svg>

After

Width:  |  Height:  |  Size: 164 KiB

+455
View File
@@ -0,0 +1,455 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0.00 0.00 100.00 100.00">
<g stroke-width="2.00" fill="none" stroke-linecap="butt">
<path stroke="#848584" vector-effect="non-scaling-stroke" d="
M 48.23 60.97
L 43.12 67.44
Q 42.76 67.89 42.23 67.66
C 36.78 65.22 31.94 71.81 36.22 76.09
Q 38.02 77.89 40.38 77.56
C 43.18 77.18 45.96 74.47 45.02 71.42
Q 44.65 70.21 44.02 69.09
Q 43.75 68.62 44.08 68.20
L 49.24 61.63
Q 49.56 61.22 50.06 61.39
Q 61.24 65.17 65.37 53.86
Q 65.55 53.39 66.05 53.37
L 76.75 53.00
Q 77.69 52.96 77.90 53.88
C 79.90 62.49 90.68 65.73 96.38 58.39
C 101.86 51.31 96.65 39.99 86.93 41.28
Q 78.93 42.35 77.60 51.24
Q 77.53 51.70 77.05 51.72
L 66.20 52.15
Q 65.56 52.17 65.46 51.54
C 64.95 48.35 64.30 46.30 62.01 43.93
Q 61.60 43.50 61.97 43.03
L 67.61 35.97
Q 68.00 35.49 68.56 35.76
C 75.35 39.06 79.78 29.09 73.12 26.29
C 68.17 24.21 64.34 29.50 66.80 34.16
Q 67.05 34.64 66.71 35.07
L 61.01 42.25
Q 60.65 42.71 60.11 42.47
Q 51.98 38.79 46.37 45.44
Q 46.00 45.87 45.52 45.57
L 34.41 38.51
Q 33.89 38.18 34.05 37.59
C 34.32 36.55 34.82 35.64 34.96 34.55
C 36.06 25.73 26.63 19.00 18.95 24.03
C 13.82 27.39 12.49 33.80 16.12 39.22
Q 16.46 39.73 16.03 40.16
L 9.27 46.81
A 0.72 0.72 0.0 0 1 8.50 46.97
C -0.18 43.45 -2.23 55.00 4.29 56.50
C 9.15 57.63 12.10 52.98 9.95 48.66
Q 9.69 48.12 10.11 47.71
L 16.76 41.14
Q 17.16 40.74 17.63 41.07
Q 25.88 46.89 32.97 39.48
Q 33.23 39.21 33.55 39.41
L 44.90 46.63
Q 45.31 46.89 45.26 47.37
Q 45.24 47.67 45.08 47.94
Q 44.82 48.39 44.71 48.90
Q 43.26 55.44 48.15 59.96
Q 48.66 60.43 48.23 60.97"
/>
<path stroke="#848584" vector-effect="non-scaling-stroke" d="
M 34.25 32.97
A 9.72 9.72 0.0 0 0 24.53 23.25
A 9.72 9.72 0.0 0 0 14.81 32.97
A 9.72 9.72 0.0 0 0 24.53 42.69
A 9.72 9.72 0.0 0 0 34.25 32.97"
/>
<path stroke="#825212" vector-effect="non-scaling-stroke" d="
M 75.76 31.11
A 4.63 4.63 0.0 0 0 71.13 26.48
A 4.63 4.63 0.0 0 0 66.50 31.11
A 4.63 4.63 0.0 0 0 71.13 35.74
A 4.63 4.63 0.0 0 0 75.76 31.11"
/>
<path stroke="#848584" vector-effect="non-scaling-stroke" d="
M 64.59 51.77
A 9.72 9.72 0.0 0 0 54.87 42.05
A 9.72 9.72 0.0 0 0 45.15 51.77
A 9.72 9.72 0.0 0 0 54.87 61.49
A 9.72 9.72 0.0 0 0 64.59 51.77"
/>
<path stroke="#848584" vector-effect="non-scaling-stroke" d="
M 97.88 51.77
A 9.71 9.71 0.0 0 0 88.17 42.06
A 9.71 9.71 0.0 0 0 78.46 51.77
A 9.71 9.71 0.0 0 0 88.17 61.48
A 9.71 9.71 0.0 0 0 97.88 51.77"
/>
<path stroke="#825212" vector-effect="non-scaling-stroke" d="
M 10.13 51.48
A 4.54 4.54 0.0 0 0 5.59 46.94
A 4.54 4.54 0.0 0 0 1.05 51.48
A 4.54 4.54 0.0 0 0 5.59 56.02
A 4.54 4.54 0.0 0 0 10.13 51.48"
/>
<path stroke="#825212" vector-effect="non-scaling-stroke" d="
M 44.56 72.32
A 4.65 4.65 0.0 0 0 39.91 67.67
A 4.65 4.65 0.0 0 0 35.26 72.32
A 4.65 4.65 0.0 0 0 39.91 76.97
A 4.65 4.65 0.0 0 0 44.56 72.32"
/>
<path stroke="#848584" vector-effect="non-scaling-stroke" d="
M 25.04 30.94
Q 25.24 31.16 25.52 31.12
Q 27.86 30.86 25.89 29.54
Q 24.29 28.46 22.93 29.73
C 21.78 30.79 22.00 33.06 22.12 34.53
C 22.41 38.14 27.59 37.43 26.98 33.28
Q 26.89 32.71 26.33 32.57
Q 25.69 32.42 25.01 32.63
Q 23.86 32.99 24.83 33.70
Q 26.06 34.60 24.84 35.30
A 0.71 0.70 65.0 0 1 23.83 34.93
Q 23.12 33.02 23.74 31.22
Q 24.15 29.99 25.04 30.94"
/>
<path stroke="#848584" vector-effect="non-scaling-stroke" d="
M 54.33 49.42
Q 54.00 48.76 53.41 48.45
Q 52.77 48.13 52.80 48.84
Q 52.83 49.81 53.56 50.51
Q 54.56 51.46 53.73 52.56
Q 53.08 53.43 52.75 54.46
Q 52.58 54.98 53.13 55.02
L 53.45 55.04
A 0.49 0.48 -80.1 0 0 53.94 54.69
Q 54.15 53.92 54.75 53.63
Q 55.16 53.44 55.32 53.86
Q 55.76 55.04 56.85 54.98
Q 57.42 54.94 57.12 54.44
L 55.71 52.11
Q 55.28 51.40 55.88 50.82
Q 56.63 50.12 57.00 49.11
A 0.44 0.43 18.4 0 0 56.71 48.54
L 56.40 48.45
A 0.55 0.55 0.0 0 0 55.75 48.74
Q 55.52 49.20 55.17 49.55
Q 54.66 50.06 54.33 49.42"
/>
<path stroke="#848584" vector-effect="non-scaling-stroke" d="
M 87.99 52.73
C 92.09 52.99 91.31 46.51 86.30 47.93
Q 85.74 48.08 85.74 48.67
L 85.73 54.86
Q 85.73 55.74 86.61 55.65
L 86.93 55.62
A 0.51 0.50 85.5 0 0 87.39 55.07
L 87.27 53.45
Q 87.21 52.68 87.99 52.73"
/>
<path stroke="#848584" vector-effect="non-scaling-stroke" d="
M 87.98 51.33
L 88.46 51.24
Q 89.03 51.13 89.07 50.55
Q 89.12 49.66 88.34 49.29
Q 87.97 49.11 87.66 49.38
Q 87.04 49.92 87.23 50.84
Q 87.36 51.46 87.98 51.33"
/>
</g>
<path fill="#fefefe" d="
M 0.00 0.00
L 100.00 0.00
L 100.00 100.00
L 0.00 100.00
L 0.00 0.00
Z
M 48.23 60.97
L 43.12 67.44
Q 42.76 67.89 42.23 67.66
C 36.78 65.22 31.94 71.81 36.22 76.09
Q 38.02 77.89 40.38 77.56
C 43.18 77.18 45.96 74.47 45.02 71.42
Q 44.65 70.21 44.02 69.09
Q 43.75 68.62 44.08 68.20
L 49.24 61.63
Q 49.56 61.22 50.06 61.39
Q 61.24 65.17 65.37 53.86
Q 65.55 53.39 66.05 53.37
L 76.75 53.00
Q 77.69 52.96 77.90 53.88
C 79.90 62.49 90.68 65.73 96.38 58.39
C 101.86 51.31 96.65 39.99 86.93 41.28
Q 78.93 42.35 77.60 51.24
Q 77.53 51.70 77.05 51.72
L 66.20 52.15
Q 65.56 52.17 65.46 51.54
C 64.95 48.35 64.30 46.30 62.01 43.93
Q 61.60 43.50 61.97 43.03
L 67.61 35.97
Q 68.00 35.49 68.56 35.76
C 75.35 39.06 79.78 29.09 73.12 26.29
C 68.17 24.21 64.34 29.50 66.80 34.16
Q 67.05 34.64 66.71 35.07
L 61.01 42.25
Q 60.65 42.71 60.11 42.47
Q 51.98 38.79 46.37 45.44
Q 46.00 45.87 45.52 45.57
L 34.41 38.51
Q 33.89 38.18 34.05 37.59
C 34.32 36.55 34.82 35.64 34.96 34.55
C 36.06 25.73 26.63 19.00 18.95 24.03
C 13.82 27.39 12.49 33.80 16.12 39.22
Q 16.46 39.73 16.03 40.16
L 9.27 46.81
A 0.72 0.72 0.0 0 1 8.50 46.97
C -0.18 43.45 -2.23 55.00 4.29 56.50
C 9.15 57.63 12.10 52.98 9.95 48.66
Q 9.69 48.12 10.11 47.71
L 16.76 41.14
Q 17.16 40.74 17.63 41.07
Q 25.88 46.89 32.97 39.48
Q 33.23 39.21 33.55 39.41
L 44.90 46.63
Q 45.31 46.89 45.26 47.37
Q 45.24 47.67 45.08 47.94
Q 44.82 48.39 44.71 48.90
Q 43.26 55.44 48.15 59.96
Q 48.66 60.43 48.23 60.97
Z"
/>
<path fill="#0a0b0a" d="
M 48.15 59.96
Q 43.26 55.44 44.71 48.90
Q 44.82 48.39 45.08 47.94
Q 45.24 47.67 45.26 47.37
Q 45.31 46.89 44.90 46.63
L 33.55 39.41
Q 33.23 39.21 32.97 39.48
Q 25.88 46.89 17.63 41.07
Q 17.16 40.74 16.76 41.14
L 10.11 47.71
Q 9.69 48.12 9.95 48.66
C 12.10 52.98 9.15 57.63 4.29 56.50
C -2.23 55.00 -0.18 43.45 8.50 46.97
A 0.72 0.72 0.0 0 0 9.27 46.81
L 16.03 40.16
Q 16.46 39.73 16.12 39.22
C 12.49 33.80 13.82 27.39 18.95 24.03
C 26.63 19.00 36.06 25.73 34.96 34.55
C 34.82 35.64 34.32 36.55 34.05 37.59
Q 33.89 38.18 34.41 38.51
L 45.52 45.57
Q 46.00 45.87 46.37 45.44
Q 51.98 38.79 60.11 42.47
Q 60.65 42.71 61.01 42.25
L 66.71 35.07
Q 67.05 34.64 66.80 34.16
C 64.34 29.50 68.17 24.21 73.12 26.29
C 79.78 29.09 75.35 39.06 68.56 35.76
Q 68.00 35.49 67.61 35.97
L 61.97 43.03
Q 61.60 43.50 62.01 43.93
C 64.30 46.30 64.95 48.35 65.46 51.54
Q 65.56 52.17 66.20 52.15
L 77.05 51.72
Q 77.53 51.70 77.60 51.24
Q 78.93 42.35 86.93 41.28
C 96.65 39.99 101.86 51.31 96.38 58.39
C 90.68 65.73 79.90 62.49 77.90 53.88
Q 77.69 52.96 76.75 53.00
L 66.05 53.37
Q 65.55 53.39 65.37 53.86
Q 61.24 65.17 50.06 61.39
Q 49.56 61.22 49.24 61.63
L 44.08 68.20
Q 43.75 68.62 44.02 69.09
Q 44.65 70.21 45.02 71.42
C 45.96 74.47 43.18 77.18 40.38 77.56
Q 38.02 77.89 36.22 76.09
C 31.94 71.81 36.78 65.22 42.23 67.66
Q 42.76 67.89 43.12 67.44
L 48.23 60.97
Q 48.66 60.43 48.15 59.96
Z
M 34.25 32.97
A 9.72 9.72 0.0 0 0 24.53 23.25
A 9.72 9.72 0.0 0 0 14.81 32.97
A 9.72 9.72 0.0 0 0 24.53 42.69
A 9.72 9.72 0.0 0 0 34.25 32.97
Z
M 75.76 31.11
A 4.63 4.63 0.0 0 0 71.13 26.48
A 4.63 4.63 0.0 0 0 66.50 31.11
A 4.63 4.63 0.0 0 0 71.13 35.74
A 4.63 4.63 0.0 0 0 75.76 31.11
Z
M 64.59 51.77
A 9.72 9.72 0.0 0 0 54.87 42.05
A 9.72 9.72 0.0 0 0 45.15 51.77
A 9.72 9.72 0.0 0 0 54.87 61.49
A 9.72 9.72 0.0 0 0 64.59 51.77
Z
M 97.88 51.77
A 9.71 9.71 0.0 0 0 88.17 42.06
A 9.71 9.71 0.0 0 0 78.46 51.77
A 9.71 9.71 0.0 0 0 88.17 61.48
A 9.71 9.71 0.0 0 0 97.88 51.77
Z
M 10.13 51.48
A 4.54 4.54 0.0 0 0 5.59 46.94
A 4.54 4.54 0.0 0 0 1.05 51.48
A 4.54 4.54 0.0 0 0 5.59 56.02
A 4.54 4.54 0.0 0 0 10.13 51.48
Z
M 44.56 72.32
A 4.65 4.65 0.0 0 0 39.91 67.67
A 4.65 4.65 0.0 0 0 35.26 72.32
A 4.65 4.65 0.0 0 0 39.91 76.97
A 4.65 4.65 0.0 0 0 44.56 72.32
Z"
/>
<path fill="#fefefe" d="
M 34.25 32.97
A 9.72 9.72 0.0 0 1 24.53 42.69
A 9.72 9.72 0.0 0 1 14.81 32.97
A 9.72 9.72 0.0 0 1 24.53 23.25
A 9.72 9.72 0.0 0 1 34.25 32.97
Z
M 25.04 30.94
Q 25.24 31.16 25.52 31.12
Q 27.86 30.86 25.89 29.54
Q 24.29 28.46 22.93 29.73
C 21.78 30.79 22.00 33.06 22.12 34.53
C 22.41 38.14 27.59 37.43 26.98 33.28
Q 26.89 32.71 26.33 32.57
Q 25.69 32.42 25.01 32.63
Q 23.86 32.99 24.83 33.70
Q 26.06 34.60 24.84 35.30
A 0.71 0.70 65.0 0 1 23.83 34.93
Q 23.12 33.02 23.74 31.22
Q 24.15 29.99 25.04 30.94
Z"
/>
<circle fill="#f9981a" cx="71.13" cy="31.11" r="4.63"/>
<path fill="#0a0b0a" d="
M 23.74 31.22
Q 23.12 33.02 23.83 34.93
A 0.71 0.70 65.0 0 0 24.84 35.30
Q 26.06 34.60 24.83 33.70
Q 23.86 32.99 25.01 32.63
Q 25.69 32.42 26.33 32.57
Q 26.89 32.71 26.98 33.28
C 27.59 37.43 22.41 38.14 22.12 34.53
C 22.00 33.06 21.78 30.79 22.93 29.73
Q 24.29 28.46 25.89 29.54
Q 27.86 30.86 25.52 31.12
Q 25.24 31.16 25.04 30.94
Q 24.15 29.99 23.74 31.22
Z"
/>
<path fill="#fefefe" d="
M 64.59 51.77
A 9.72 9.72 0.0 0 1 54.87 61.49
A 9.72 9.72 0.0 0 1 45.15 51.77
A 9.72 9.72 0.0 0 1 54.87 42.05
A 9.72 9.72 0.0 0 1 64.59 51.77
Z
M 54.33 49.42
Q 54.00 48.76 53.41 48.45
Q 52.77 48.13 52.80 48.84
Q 52.83 49.81 53.56 50.51
Q 54.56 51.46 53.73 52.56
Q 53.08 53.43 52.75 54.46
Q 52.58 54.98 53.13 55.02
L 53.45 55.04
A 0.49 0.48 -80.1 0 0 53.94 54.69
Q 54.15 53.92 54.75 53.63
Q 55.16 53.44 55.32 53.86
Q 55.76 55.04 56.85 54.98
Q 57.42 54.94 57.12 54.44
L 55.71 52.11
Q 55.28 51.40 55.88 50.82
Q 56.63 50.12 57.00 49.11
A 0.44 0.43 18.4 0 0 56.71 48.54
L 56.40 48.45
A 0.55 0.55 0.0 0 0 55.75 48.74
Q 55.52 49.20 55.17 49.55
Q 54.66 50.06 54.33 49.42
Z"
/>
<path fill="#fefefe" d="
M 97.88 51.77
A 9.71 9.71 0.0 0 1 88.17 61.48
A 9.71 9.71 0.0 0 1 78.46 51.77
A 9.71 9.71 0.0 0 1 88.17 42.06
A 9.71 9.71 0.0 0 1 97.88 51.77
Z
M 87.99 52.73
C 92.09 52.99 91.31 46.51 86.30 47.93
Q 85.74 48.08 85.74 48.67
L 85.73 54.86
Q 85.73 55.74 86.61 55.65
L 86.93 55.62
A 0.51 0.50 85.5 0 0 87.39 55.07
L 87.27 53.45
Q 87.21 52.68 87.99 52.73
Z"
/>
<circle fill="#f9981a" cx="5.59" cy="51.48" r="4.54"/>
<path fill="#0a0b0a" d="
M 87.27 53.45
L 87.39 55.07
A 0.51 0.50 85.5 0 1 86.93 55.62
L 86.61 55.65
Q 85.73 55.74 85.73 54.86
L 85.74 48.67
Q 85.74 48.08 86.30 47.93
C 91.31 46.51 92.09 52.99 87.99 52.73
Q 87.21 52.68 87.27 53.45
Z
M 87.98 51.33
L 88.46 51.24
Q 89.03 51.13 89.07 50.55
Q 89.12 49.66 88.34 49.29
Q 87.97 49.11 87.66 49.38
Q 87.04 49.92 87.23 50.84
Q 87.36 51.46 87.98 51.33
Z"
/>
<path fill="#0a0b0a" d="
M 55.17 49.55
Q 55.52 49.20 55.75 48.74
A 0.55 0.55 0.0 0 1 56.40 48.45
L 56.71 48.54
A 0.44 0.43 18.4 0 1 57.00 49.11
Q 56.63 50.12 55.88 50.82
Q 55.28 51.40 55.71 52.11
L 57.12 54.44
Q 57.42 54.94 56.85 54.98
Q 55.76 55.04 55.32 53.86
Q 55.16 53.44 54.75 53.63
Q 54.15 53.92 53.94 54.69
A 0.49 0.48 -80.1 0 1 53.45 55.04
L 53.13 55.02
Q 52.58 54.98 52.75 54.46
Q 53.08 53.43 53.73 52.56
Q 54.56 51.46 53.56 50.51
Q 52.83 49.81 52.80 48.84
Q 52.77 48.13 53.41 48.45
Q 54.00 48.76 54.33 49.42
Q 54.66 50.06 55.17 49.55
Z"
/>
<path fill="#fefefe" d="
M 87.98 51.33
Q 87.36 51.46 87.23 50.84
Q 87.04 49.92 87.66 49.38
Q 87.97 49.11 88.34 49.29
Q 89.12 49.66 89.07 50.55
Q 89.03 51.13 88.46 51.24
L 87.98 51.33
Z"
/>
<circle fill="#f9981a" cx="39.91" cy="72.32" r="4.65"/>
</svg>

After

Width:  |  Height:  |  Size: 12 KiB

+9
View File
@@ -0,0 +1,9 @@
<svg width="40" height="45" viewBox="0 0 40 45" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<rect width="40" height="44.8" fill="url(#pattern0)"/>
<defs>
<pattern id="pattern0" patternContentUnits="objectBoundingBox" width="1" height="1">
<use xlink:href="#image0_407_5984" transform="matrix(0.00165375 0 0 0.00147656 0.0132889 0.0570321)"/>
</pattern>
<image id="image0_407_5984" width="600" height="600" xlink:href="data:image/png;base64,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 truncated
</defs>
</svg>

After

Width:  |  Height:  |  Size: 48 KiB

@@ -0,0 +1,9 @@
<svg width="41" height="45" viewBox="0 0 41 45" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<rect x="0.666626" width="40" height="44.8" fill="url(#pattern0)"/>
<defs>
<pattern id="pattern0" patternContentUnits="objectBoundingBox" width="1" height="1">
<use xlink:href="#image0_407_5949" transform="matrix(0.00233051 0 0 0.00208081 -0.237112 0.0838373)"/>
</pattern>
<image id="image0_407_5949" width="650" height="400" xlink:href="data:image/png;base64,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 truncated
</defs>
</svg>

After

Width:  |  Height:  |  Size: 139 KiB

@@ -0,0 +1,315 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0.00 0.00 500.00 300.00">
<path fill="#016299" d="
M 158.03 92.18
A 0.27 0.26 -50.8 0 0 157.56 92.23
L 133.49 153.75
Q 133.29 154.26 132.74 154.26
L 117.81 154.27
Q 117.04 154.27 116.75 153.55
L 93.39 93.85
Q 92.38 91.25 91.83 93.99
L 79.83 153.77
Q 79.73 154.26 79.23 154.26
L 58.72 154.26
A 0.45 0.45 0.0 0 1 58.28 153.72
L 78.16 53.57
Q 78.22 53.25 78.55 53.25
L 98.56 53.25
Q 99.17 53.25 99.41 53.82
L 124.60 114.49
Q 125.30 116.18 125.99 114.48
L 151.01 53.62
A 0.59 0.59 0.0 0 1 151.56 53.25
L 171.54 53.25
Q 172.10 53.25 172.21 53.80
L 192.72 153.73
A 0.47 0.46 -5.6 0 1 192.26 154.28
L 170.98 154.26
A 0.48 0.48 0.0 0 1 170.51 153.88
Q 164.38 123.38 158.25 92.76
Q 158.18 92.40 158.03 92.18
Z"
/>
<rect fill="#016299" x="197.86" y="53.25" width="20.38" height="101.02" rx="0.38"/>
<path fill="#016299" d="
M 248.05 70.24
A 0.57 0.57 0.0 0 0 247.49 69.57
L 225.05 69.62
Q 224.49 69.62 224.49 69.05
L 224.49 53.50
Q 224.49 53.25 224.75 53.25
L 291.20 53.25
A 0.46 0.45 -0.0 0 1 291.66 53.70
L 291.69 69.02
A 0.58 0.58 0.0 0 1 291.11 69.60
L 268.55 69.58
Q 267.89 69.58 267.89 70.24
L 267.87 153.71
Q 267.87 154.29 267.29 154.29
L 248.53 154.26
Q 247.99 154.26 247.98 153.71
Q 247.90 113.33 247.96 71.39
Q 247.96 70.81 248.05 70.24
Z"
/>
<path fill="#016299" d="
M 378.18 151.96
L 378.20 53.90
A 0.65 0.65 0.0 0 1 378.85 53.25
L 439.77 53.25
Q 440.30 53.25 440.30 53.77
L 440.30 69.02
Q 440.30 69.59 439.73 69.59
L 399.08 69.59
Q 398.49 69.59 398.49 70.17
L 398.49 95.03
A 0.54 0.54 0.0 0 0 399.03 95.57
L 439.23 95.55
A 0.69 0.68 -0.0 0 1 439.92 96.23
L 439.95 111.28
A 0.73 0.72 -89.6 0 1 439.22 112.01
L 398.92 111.98
A 0.43 0.43 0.0 0 0 398.49 112.41
L 398.49 137.15
A 0.65 0.65 0.0 0 0 399.15 137.80
L 439.78 137.76
Q 440.29 137.76 440.29 138.28
L 440.30 153.73
Q 440.30 154.27 439.76 154.27
L 354.77 154.26
Q 354.04 154.26 353.59 153.69
L 322.21 113.37
Q 321.59 112.58 321.59 111.57
L 321.58 94.93
Q 321.58 94.33 322.18 94.31
Q 330.93 94.07 341.46 94.08
C 347.32 94.08 353.98 90.87 355.04 84.56
C 356.49 76.04 350.55 70.28 342.17 70.27
Q 330.13 70.26 318.50 70.23
A 0.36 0.36 0.0 0 0 318.14 70.59
L 318.14 153.69
Q 318.14 154.28 317.56 154.28
L 298.50 154.26
A 0.52 0.51 0.6 0 1 297.99 153.75
L 297.98 53.63
Q 297.98 53.20 298.41 53.21
Q 318.18 53.37 340.34 53.16
Q 348.47 53.08 354.39 54.64
C 363.38 57.00 372.60 64.63 374.49 74.23
C 376.35 83.66 375.47 94.49 368.51 101.41
Q 358.96 110.89 344.12 111.94
A 0.69 0.69 0.0 0 0 343.64 113.07
L 376.85 152.45
Q 378.18 154.02 378.18 151.96
Z"
/>
<path fill="#c64128" d="
M 230.30 202.72
C 225.79 198.17 222.44 192.50 223.68 186.06
Q 225.88 174.61 237.19 171.82
C 243.35 170.31 250.25 172.62 254.56 177.46
C 260.15 183.76 260.08 193.76 254.44 200.16
Q 251.26 203.77 246.86 205.76
A 0.90 0.90 0.0 0 0 246.56 207.18
L 254.93 216.61
A 0.69 0.68 39.0 0 0 256.04 216.50
L 263.39 203.88
Q 263.51 203.67 263.76 203.67
L 274.57 203.66
Q 274.82 203.66 274.70 203.88
L 263.00 224.53
Q 262.72 225.02 263.10 225.44
L 275.42 238.78
A 0.38 0.38 0.0 0 1 275.39 239.32
L 268.56 245.42
A 0.45 0.44 47.3 0 1 267.94 245.39
L 258.30 234.72
A 0.78 0.78 0.0 0 0 257.06 234.82
C 251.41 243.84 242.45 248.65 231.77 245.21
C 216.95 240.42 210.30 223.12 220.67 210.78
C 223.18 207.79 226.82 205.56 230.20 203.52
Q 230.76 203.18 230.30 202.72
Z
M 248.91 190.31
C 249.51 185.51 247.25 181.73 242.19 181.06
C 238.82 180.60 235.04 182.40 233.78 185.57
C 231.59 191.08 235.32 195.52 239.43 199.17
Q 239.81 199.50 240.25 199.27
C 244.10 197.30 248.33 195.00 248.91 190.31
Z
M 248.77 224.33
L 238.93 213.60
A 4.22 4.22 0.0 0 0 232.97 213.34
L 229.66 216.37
A 11.43 11.41 47.5 0 0 228.97 232.50
L 229.06 232.61
A 11.43 11.41 47.5 0 0 245.19 233.33
L 248.50 230.29
A 4.22 4.22 0.0 0 0 248.77 224.33
Z"
/>
<path fill="#c64128" d="
M 316.53 236.21
C 325.70 237.60 333.15 234.06 339.52 227.34
Q 339.84 227.00 340.30 227.00
L 351.79 226.98
Q 352.78 226.97 352.26 227.81
Q 343.43 242.15 327.68 245.58
C 306.15 250.26 286.42 234.20 283.32 213.61
C 279.76 189.93 301.11 168.47 324.78 171.76
Q 343.44 174.35 352.65 190.63
Q 352.99 191.23 352.30 191.23
L 341.44 191.23
A 2.35 2.32 -21.3 0 1 339.73 190.49
C 330.13 180.10 315.42 177.80 304.03 186.97
Q 295.74 193.64 294.02 204.40
C 291.70 218.96 301.67 233.95 316.53 236.21
Z"
/>
<path fill="#c64128" d="
M 372.00 212.51
Q 373.04 213.66 373.04 215.17
Q 373.03 229.75 373.10 244.32
Q 373.10 244.93 372.49 244.93
L 362.91 244.88
A 0.59 0.58 -90.0 0 1 362.33 244.29
L 362.33 173.44
A 0.48 0.48 0.0 0 1 362.81 172.96
L 372.70 172.95
Q 373.06 172.95 373.06 173.31
Q 373.13 184.59 372.84 195.52
C 372.74 198.97 373.39 202.37 373.05 205.87
Q 372.81 208.40 374.27 206.32
Q 378.12 200.83 382.18 195.74
Q 386.47 190.36 390.19 185.21
Q 394.58 179.13 398.83 173.21
Q 398.94 173.06 399.12 173.04
Q 400.52 172.93 411.21 172.92
A 0.05 0.04 -69.3 0 1 411.24 173.00
Q 397.61 190.69 384.61 207.81
A 0.78 0.77 45.4 0 0 384.61 208.74
Q 397.76 226.47 411.39 244.88
A 0.04 0.04 0.0 0 1 411.36 244.94
Q 399.76 244.99 399.30 244.89
Q 399.13 244.85 399.03 244.72
Q 387.57 228.88 375.97 212.45
Q 375.60 211.92 374.96 211.91
L 372.29 211.86
A 0.39 0.39 0.0 0 0 372.00 212.51
Z"
/>
<path fill="#c64128" d="
M 78.48 225.70
L 70.67 244.67
A 0.43 0.43 0.0 0 1 70.27 244.94
L 59.40 244.99
A 0.43 0.43 0.0 0 1 59.00 244.39
L 88.53 173.23
A 0.43 0.43 0.0 0 1 88.93 172.97
L 97.05 172.95
A 0.43 0.43 0.0 0 1 97.45 173.21
L 127.21 244.41
A 0.43 0.43 0.0 0 1 126.81 245.01
L 115.90 244.93
A 0.43 0.43 0.0 0 1 115.50 244.67
L 107.65 225.68
A 0.43 0.43 0.0 0 0 107.25 225.41
L 78.88 225.43
A 0.43 0.43 0.0 0 0 78.48 225.70
Z
M 82.29 214.85
A 0.40 0.40 0.0 0 0 82.67 215.39
L 103.19 215.39
A 0.40 0.40 0.0 0 0 103.57 214.85
L 93.30 187.38
A 0.40 0.40 0.0 0 0 92.56 187.38
L 82.29 214.85
Z"
/>
<path fill="#c64128" d="
M 148.27 183.14
L 148.27 244.56
A 0.41 0.41 0.0 0 1 147.86 244.97
L 137.95 244.91
A 0.41 0.41 0.0 0 1 137.54 244.50
L 137.54 183.14
A 0.41 0.41 0.0 0 0 137.13 182.73
L 121.27 182.75
A 0.41 0.41 0.0 0 1 120.86 182.34
L 120.84 173.37
A 0.41 0.41 0.0 0 1 121.25 172.96
L 164.97 172.94
A 0.41 0.41 0.0 0 1 165.38 173.35
L 165.38 182.35
A 0.41 0.41 0.0 0 1 164.97 182.76
L 148.68 182.73
A 0.41 0.41 0.0 0 0 148.27 183.14
Z"
/>
<path fill="#c64128" d="
M 187.79 182.72
L 171.91 182.75
A 0.46 0.46 0.0 0 1 171.45 182.29
L 171.44 173.41
A 0.46 0.46 0.0 0 1 171.90 172.95
L 215.60 172.96
A 0.46 0.46 0.0 0 1 216.06 173.42
L 216.01 182.28
A 0.46 0.46 0.0 0 1 215.55 182.74
L 199.38 182.74
A 0.46 0.46 0.0 0 0 198.92 183.20
L 198.94 244.47
A 0.46 0.46 0.0 0 1 198.48 244.93
L 188.67 244.94
A 0.46 0.46 0.0 0 1 188.21 244.48
L 188.25 183.18
A 0.46 0.46 0.0 0 0 187.79 182.72
Z"
/>
<path fill="#c64128" d="
M 431.21 237.97
A 0.56 0.56 0.0 0 0 430.42 238.50
L 430.50 244.18
A 0.84 0.83 -87.3 0 1 429.57 245.03
L 429.02 244.97
A 0.94 0.93 3.7 0 1 428.18 244.02
L 428.39 234.99
A 0.60 0.59 -1.3 0 0 427.74 234.38
L 425.24 234.57
Q 424.82 234.60 424.83 235.01
L 424.97 243.97
Q 424.99 244.87 424.09 244.97
Q 423.71 245.01 423.62 245.02
Q 422.73 245.12 422.73 244.22
L 422.73 235.26
Q 422.73 234.54 422.01 234.52
L 420.93 234.50
Q 420.12 234.48 420.12 233.68
L 420.12 233.19
A 0.69 0.69 0.0 0 1 420.81 232.50
L 431.06 232.56
A 0.80 0.79 -8.4 0 1 431.82 233.12
Q 433.00 237.12 434.36 240.46
Q 434.90 241.81 435.37 240.43
L 437.85 233.10
Q 438.05 232.51 438.68 232.50
L 440.78 232.48
Q 441.33 232.48 441.33 233.03
L 441.25 244.31
A 0.70 0.69 -85.1 0 1 440.44 245.00
L 439.95 244.92
Q 438.74 244.72 438.85 243.50
Q 438.99 241.98 439.01 240.48
Q 439.01 240.00 438.70 239.61
Q 438.51 239.38 438.21 239.39
Q 437.55 239.41 437.35 240.03
L 436.04 244.05
A 1.26 1.25 -89.5 0 1 435.20 244.88
Q 434.04 245.23 433.63 244.13
Q 432.61 241.40 431.70 238.52
Q 431.57 238.12 431.21 237.97
Z"
/>
</svg>

After

Width:  |  Height:  |  Size: 8.3 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64"><path d="M12.506 8.407C5.603 8.407 0 14.01 0 20.912l.097 34.74h11.117l-.097-23.1v-11.63a1.4 1.4 0 0 1 1.25-1.39c.047-.005.09 0 .14 0 .383 0 .73.157.98.408L42.653 51.93c2.342 2.35 5.523 3.668 8.84 3.665C58.397 55.594 64 49.99 64 43.088V8.35H52.883V43.14a1.39 1.39 0 0 1-1.39 1.337c-.368 0-.72-.147-.98-.408L21.347 12.07c-2.342-2.35-5.524-3.668-8.84-3.664z"/></svg>

After

Width:  |  Height:  |  Size: 426 B

@@ -0,0 +1,9 @@
<svg width="40" height="45" viewBox="0 0 40 45" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<rect width="40" height="44.8" fill="url(#pattern0)"/>
<defs>
<pattern id="pattern0" patternContentUnits="objectBoundingBox" width="1" height="1">
<use xlink:href="#image0_407_5932" transform="matrix(0.00406504 0 0 0.0036295 0 0.109829)"/>
</pattern>
<image id="image0_407_5932" width="246" height="215" xlink:href="data:image/png;base64,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 truncated
</defs>
</svg>

After

Width:  |  Height:  |  Size: 48 KiB

+1
View File
@@ -0,0 +1 @@
<svg id="svg" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="400" height="400" viewBox="0, 0, 400,400"><g id="svgg"><path id="path1" d="M181.488 3.971 C 181.877 4.046,182.439 4.043,182.738 3.965 C 183.037 3.886,182.719 3.825,182.031 3.829 C 181.344 3.832,181.099 3.896,181.488 3.971 M207.578 3.974 C 208.051 4.045,208.824 4.045,209.297 3.974 C 209.770 3.902,209.383 3.844,208.438 3.844 C 207.492 3.844,207.105 3.902,207.578 3.974 M175.220 6.267 C 174.053 6.402,171.100 6.944,168.658 7.470 C 166.216 7.996,162.391 8.785,160.156 9.223 C 153.967 10.435,138.143 14.908,131.094 17.438 C 121.036 21.047,106.262 28.624,94.791 36.055 C 91.840 37.967,88.540 40.087,87.456 40.767 C 82.295 44.006,73.047 52.211,61.404 63.882 C 52.279 73.029,50.918 74.572,44.768 82.752 C 42.919 85.212,40.562 88.333,39.530 89.689 C 38.498 91.044,36.968 93.420,36.129 94.967 C 35.290 96.515,33.983 98.597,33.224 99.594 C 28.326 106.029,17.446 132.286,12.965 148.487 C 6.187 172.987,3.613 211.259,7.635 227.716 C 7.879 228.714,8.241 231.197,8.440 233.233 C 8.638 235.269,9.011 237.660,9.269 238.546 C 9.526 239.432,10.347 242.758,11.092 245.938 C 15.712 265.642,25.063 286.369,39.357 308.594 C 45.014 317.390,58.310 333.286,65.548 339.907 C 75.315 348.843,82.927 354.856,92.813 361.446 C 101.136 366.996,102.423 367.813,102.846 367.813 C 103.101 367.812,104.358 368.460,105.639 369.252 C 118.785 377.376,141.956 385.796,162.243 389.820 C 180.990 393.539,208.011 394.968,219.254 392.835 C 220.123 392.671,223.213 392.310,226.120 392.034 C 235.850 391.112,254.870 386.541,264.058 382.917 C 266.037 382.136,269.133 381.015,270.938 380.424 C 282.933 376.498,310.707 359.828,324.689 348.163 C 337.074 337.829,357.706 314.280,363.266 304.130 C 363.992 302.804,365.389 300.457,366.369 298.916 C 370.628 292.217,381.768 268.863,382.972 264.106 C 383.218 263.137,383.763 261.641,384.183 260.781 C 384.603 259.922,385.394 257.883,385.939 256.250 C 386.485 254.617,387.263 252.340,387.669 251.189 C 388.353 249.247,390.287 240.145,391.408 233.594 C 393.545 221.098,394.636 192.104,393.341 182.188 C 393.060 180.039,392.469 175.140,392.027 171.301 C 391.585 167.461,391.015 163.704,390.759 162.951 C 390.504 162.199,389.735 159.152,389.051 156.182 C 386.904 146.855,381.892 131.174,378.948 124.570 C 377.808 122.013,376.665 119.211,376.407 118.342 C 376.149 117.474,375.378 115.903,374.693 114.852 C 374.008 113.802,373.161 112.107,372.810 111.086 C 371.660 107.745,368.123 101.851,361.468 92.188 C 352.620 79.341,350.801 76.942,345.515 71.151 C 343.550 68.998,340.887 65.912,339.599 64.294 C 337.074 61.123,333.807 58.028,331.942 57.043 C 331.304 56.706,329.094 54.799,327.031 52.807 C 322.282 48.219,318.908 45.537,311.116 40.157 C 307.691 37.791,303.921 35.166,302.739 34.323 C 301.557 33.479,299.841 32.458,298.926 32.053 C 298.011 31.649,295.664 30.369,293.710 29.209 C 274.704 17.927,240.905 7.682,217.969 6.252 C 212.165 5.890,178.360 5.901,175.220 6.267 M214.688 9.715 C 217.180 9.890,220.555 10.240,222.188 10.493 C 223.820 10.746,226.492 11.160,228.125 11.413 C 254.033 15.426,274.205 22.353,293.095 33.722 C 295.672 35.273,298.676 36.948,299.770 37.444 C 305.667 40.118,323.277 53.681,332.300 62.500 C 341.110 71.110,355.938 88.597,355.938 90.376 C 355.938 90.565,357.040 92.280,358.388 94.187 C 363.109 100.869,366.878 107.925,374.759 124.844 C 379.890 135.857,380.102 136.520,385.165 157.344 C 386.585 163.188,387.970 168.742,388.241 169.688 C 389.371 173.620,389.993 185.280,389.990 202.500 C 389.988 214.364,389.957 214.790,388.329 225.469 C 387.909 228.219,387.326 232.297,387.033 234.531 C 386.463 238.882,385.270 243.971,384.558 245.094 C 384.318 245.472,383.819 246.906,383.450 248.281 C 383.080 249.656,381.724 254.086,380.436 258.125 C 375.460 273.726,376.212 271.744,372.430 279.219 C 371.473 281.109,369.974 284.133,369.099 285.938 C 368.224 287.742,367.152 289.781,366.718 290.469 C 366.283 291.156,365.834 292.000,365.720 292.344 C 365.282 293.665,363.316 297.075,360.976 300.574 C 359.635 302.579,357.656 305.623,356.577 307.340 C 355.498 309.056,353.928 311.212,353.089 312.131 C 352.249 313.049,351.559 313.965,351.555 314.166 C 351.550 314.367,350.812 315.402,349.914 316.467 C 349.016 317.532,346.529 320.485,344.388 323.029 C 337.377 331.361,322.581 345.644,315.000 351.399 C 309.427 355.630,295.492 364.688,294.556 364.688 C 294.501 364.687,293.066 365.476,291.368 366.440 C 286.032 369.469,276.772 373.979,273.876 374.959 C 272.346 375.477,269.887 376.460,268.412 377.142 C 262.659 379.805,247.103 384.423,239.844 385.623 C 238.984 385.765,237.719 386.091,237.031 386.348 C 236.344 386.606,234.094 386.982,232.031 387.186 C 229.969 387.389,227.438 387.744,226.406 387.973 C 223.251 388.676,217.053 389.208,204.531 389.853 C 166.381 391.817,116.169 375.617,84.844 351.237 C 73.837 342.671,58.784 328.831,58.277 326.812 C 58.169 326.384,57.465 325.518,56.712 324.889 C 49.407 318.782,33.808 294.264,25.868 276.406 C 7.215 234.457,4.544 183.113,18.813 140.781Line truncated

After

Width:  |  Height:  |  Size: 547 KiB

+9
View File
@@ -0,0 +1,9 @@
<svg width="41" height="45" viewBox="0 0 41 45" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<rect x="0.333374" width="40" height="44.8" fill="url(#pattern0)"/>
<defs>
<pattern id="pattern0" patternContentUnits="objectBoundingBox" width="1" height="1">
<use xlink:href="#image0_407_5966" transform="matrix(0.0024888 0 0 0.00222214 0.0431086 0.086682)"/>
</pattern>
<image id="image0_407_5966" width="375" height="372" xlink:href="data:image/png;base64,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 truncated
</defs>
</svg>

After

Width:  |  Height:  |  Size: 174 KiB

+4
View File
@@ -0,0 +1,4 @@
export * from "./compliance/IconCompliance";
export * from "./Icons";
export * from "./prowler/ProwlerIcons";
export * from "./services/IconServices";
@@ -0,0 +1,42 @@
import * as React from "react";
import { IconSvgProps } from "@/types";
export const AWSProviderBadge: React.FC<IconSvgProps> = ({
size,
width,
height,
...props
}) => (
<svg
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
fill="none"
focusable="false"
height={size || height}
role="presentation"
viewBox="0 0 256 256"
width={size || width}
{...props}
>
<g fill="none">
<rect width="256" height="256" fill="#f4f2ed" rx="60" />
<path
fill="#252f3e"
d="M84.745 111.961c0 2.434.263 4.407.723 5.855a35 35 0 0 0 2.106 4.737c.329.526.46 1.052.46 1.513c0 .658-.395 1.316-1.25 1.973l-4.145 2.764c-.592.394-1.184.592-1.71.592c-.658 0-1.316-.329-1.974-.921a20.4 20.4 0 0 1-2.368-3.092a51 51 0 0 1-2.04-3.882q-7.697 9.08-19.342 9.079c-5.526 0-9.934-1.579-13.158-4.737c-3.223-3.158-4.868-7.368-4.868-12.631c0-5.593 1.974-10.132 5.987-13.553s9.342-5.132 16.118-5.132c2.237 0 4.54.198 6.974.527s4.934.855 7.566 1.447v-4.803c0-5-1.053-8.487-3.092-10.526c-2.106-2.04-5.658-3.026-10.724-3.026c-2.303 0-4.671.263-7.105.855s-4.803 1.316-7.106 2.237a19 19 0 0 1-2.302.855c-.46.132-.79.198-1.053.198c-.92 0-1.382-.658-1.382-2.04v-3.224c0-1.052.132-1.842.461-2.302s.921-.921 1.842-1.382q3.454-1.776 8.29-2.96c3.223-.856 6.644-1.25 10.263-1.25c7.829 0 13.552 1.776 17.237 5.328c3.618 3.553 5.46 8.948 5.46 16.185v21.316zm-26.71 10c2.17 0 4.407-.395 6.776-1.185c2.368-.789 4.473-2.237 6.25-4.21c1.052-1.25 1.842-2.632 2.236-4.211s.658-3.487.658-5.723v-2.764a55 55 0 0 0-6.052-1.118a50 50 0 0 0-6.185-.395c-4.408 0-7.631.856-9.802 2.632s-3.224 4.276-3.224 7.566c0 3.092.79 5.394 2.434 6.973c1.58 1.645 3.882 2.435 6.908 2.435m52.828 7.105c-1.184 0-1.974-.198-2.5-.658c-.526-.395-.987-1.316-1.381-2.566l-15.46-50.855c-.396-1.316-.593-2.171-.593-2.632c0-1.052.526-1.645 1.579-1.645h6.447c1.25 0 2.106.198 2.566.658c.526.395.921 1.316 1.316 2.566l11.052 43.553l10.264-43.553c.329-1.316.723-2.17 1.25-2.566c.526-.394 1.447-.657 2.631-.657h5.263c1.25 0 2.106.197 2.632.657c.526.395.987 1.316 1.25 2.566l10.395 44.079l11.381-44.079c.395-1.316.856-2.17 1.316-2.566c.526-.394 1.382-.657 2.566-.657h6.118c1.053 0 1.645.526 1.645 1.644c0 .33-.066.658-.132 1.053c-.065.395-.197.92-.46 1.645l-15.855 50.855q-.593 1.974-1.382 2.566c-.526.394-1.382.658-2.5.658h-5.658c-1.25 0-2.105-.198-2.631-.658c-.527-.461-.987-1.316-1.25-2.632l-10.198-42.434l-10.131 42.368c-.329 1.316-.724 2.171-1.25 2.632c-.527.46-1.448.658-2.632.658zm84.54 1.776c-3.421 0-6.842-.395-10.132-1.184c-3.289-.79-5.855-1.645-7.566-2.632c-1.052-.592-1.776-1.25-2.039-1.842a4.65 4.65 0 0 1-.395-1.842v-3.355c0-1.382.526-2.04 1.513-2.04q.593 0 1.184.198c.395.131.987.394 1.645.658a35.8 35.8 0 0 0 7.237 2.302a39.5 39.5 0 0 0 7.829.79c4.145 0 7.368-.724 9.605-2.171c2.237-1.448 3.421-3.553 3.421-6.25c0-1.842-.592-3.356-1.776-4.606s-3.421-2.368-6.645-3.421l-9.539-2.96c-4.803-1.513-8.356-3.75-10.527-6.71c-2.171-2.895-3.289-6.12-3.289-9.54q0-4.144 1.776-7.303c1.184-2.105 2.763-3.947 4.737-5.394c1.974-1.514 4.211-2.632 6.842-3.422c2.632-.79 5.395-1.118 8.29-1.118c1.447 0 2.96.066 4.408.263c1.513.197 2.894.46 4.276.724c1.316.329 2.566.658 3.75 1.053q1.776.591 2.763 1.184c.921.526 1.579 1.052 1.974 1.644q.592.79.592 2.172v3.092c0 1.381-.526 2.105-1.513 2.105c-.527 0-1.382-.263-2.5-.79q-5.625-2.565-12.632-2.565c-3.75 0-6.71.592-8.75 1.842s-3.092 3.158-3.092 5.855c0 1.842.658 3.421 1.974 4.671s3.75 2.5 7.237 3.618l9.342 2.96c4.736 1.514 8.158 3.619 10.197 6.317s3.026 5.789 3.026 9.21c0 2.829-.592 5.395-1.71 7.632c-1.184 2.237-2.763 4.21-4.803 5.789c-2.039 1.645-4.474 2.829-7.302 3.685c-2.961.921-6.053 1.381-9.408 1.381"
/>
<path
fill="#f90"
fillRule="evenodd"
d="M207.837 162.816c-21.645 15.987-53.092 24.474-80.132 24.474c-37.894 0-72.04-14.014-97.829-37.303c-2.04-1.842-.197-4.342 2.237-2.895c27.895 16.184 62.303 25.987 97.895 25.987c24.013 0 50.395-5 74.671-15.263c3.618-1.645 6.71 2.368 3.158 5"
clipRule="evenodd"
/>
<path
fill="#f90"
fillRule="evenodd"
d="M216.85 152.553c-2.763-3.553-18.289-1.711-25.329-.856c-2.105.264-2.434-1.579-.526-2.96c12.368-8.684 32.697-6.184 35.066-3.29c2.368 2.961-.658 23.29-12.237 33.027c-1.777 1.513-3.487.723-2.698-1.25c2.632-6.513 8.487-21.185 5.724-24.671"
clipRule="evenodd"
/>
</g>
</svg>
);
@@ -0,0 +1,80 @@
import * as React from "react";
import { IconSvgProps } from "@/types";
export const AzureProviderBadge: React.FC<IconSvgProps> = ({
size,
width,
height,
...props
}) => (
<svg
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
fill="none"
focusable="false"
height={size || height}
role="presentation"
viewBox="0 0 256 256"
width={size || width}
{...props}
>
<g fill="none">
<rect width="256" height="256" fill="#f4f2ed" rx="60" />
<path
fill="url(#skillIconsAzureLight0)"
d="M94.674 34.002h59.182L92.42 216.032a9.44 9.44 0 0 1-8.94 6.419H37.422a9.42 9.42 0 0 1-9.318-8.026a9.4 9.4 0 0 1 .39-4.407L85.733 40.421A9.44 9.44 0 0 1 94.674 34z"
/>
<path
fill="#0078d4"
d="M180.674 156.095H86.826a4.34 4.34 0 0 0-4.045 2.75a4.34 4.34 0 0 0 1.079 4.771l60.305 56.287a9.48 9.48 0 0 0 6.468 2.548h53.141z"
/>
<path
fill="url(#skillIconsAzureLight1)"
d="M94.675 34.002a9.36 9.36 0 0 0-8.962 6.544L28.565 209.863a9.412 9.412 0 0 0 8.882 12.588h47.247a10.1 10.1 0 0 0 7.75-6.592l11.397-33.586l40.708 37.968a9.63 9.63 0 0 0 6.059 2.21h52.943l-23.22-66.355l-67.689.016l41.428-122.11z"
/>
<path
fill="url(#skillIconsAzureLight2)"
d="M170.264 40.412a9.42 9.42 0 0 0-8.928-6.41H95.379a9.42 9.42 0 0 1 8.928 6.41l57.241 169.604a9.43 9.43 0 0 1-1.273 8.509a9.43 9.43 0 0 1-7.655 3.928h65.959a9.43 9.43 0 0 0 7.654-3.929a9.42 9.42 0 0 0 1.272-8.508z"
/>
<defs>
<linearGradient
id="skillIconsAzureLight0"
x1="116.244"
x2="54.783"
y1="47.967"
y2="229.54"
gradientUnits="userSpaceOnUse"
>
<stop stopColor="#114a8b" />
<stop offset="1" stopColor="#0669bc" />
</linearGradient>
<linearGradient
id="skillIconsAzureLight1"
x1="135.444"
x2="121.227"
y1="132.585"
y2="137.392"
gradientUnits="userSpaceOnUse"
>
<stop stopOpacity="0.3" />
<stop offset="0.071" stopOpacity="0.2" />
<stop offset="0.321" stopOpacity="0.1" />
<stop offset="0.623" stopOpacity="0.05" />
<stop offset="1" stopOpacity="0" />
</linearGradient>
<linearGradient
id="skillIconsAzureLight2"
x1="127.625"
x2="195.091"
y1="42.671"
y2="222.414"
gradientUnits="userSpaceOnUse"
>
<stop stopColor="#3ccbf4" />
<stop offset="1" stopColor="#2892df" />
</linearGradient>
</defs>
</g>
</svg>
);
@@ -0,0 +1,42 @@
import * as React from "react";
import { IconSvgProps } from "@/types";
export const GCPProviderBadge: React.FC<IconSvgProps> = ({
size,
width,
height,
...props
}) => (
<svg
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
fill="none"
focusable="false"
height={size || height}
role="presentation"
viewBox="0 0 256 256"
width={size || width}
{...props}
>
<g fill="none">
<rect width="256" height="256" fill="#f4f2ed" rx="60" />
<path
fill="#ea4335"
d="m161.009 92.39l17.385-17.386l1.159-7.32c-31.68-28.807-82.04-25.54-110.6 6.816c-7.932 8.986-13.817 20.19-16.955 31.76l6.226-.878l34.77-5.733l2.684-2.745c15.466-16.986 41.617-19.272 59.475-4.82z"
/>
<path
fill="#4285f4"
d="M203.16 105.749a78.3 78.3 0 0 0-23.607-38.064l-24.4 24.4a43.37 43.37 0 0 1 15.921 34.404v4.331c11.993 0 21.716 9.722 21.716 21.715s-9.723 21.473-21.716 21.473h-43.493l-4.27 4.636v26.047l4.27 4.087h43.493c31.195.243 56.681-24.605 56.924-55.8a56.48 56.48 0 0 0-24.838-47.229"
/>
<path
fill="#34a853"
d="M84.149 208.778h43.432v-34.77H84.149a21.3 21.3 0 0 1-8.906-1.952l-6.161 1.891l-17.507 17.385l-1.525 5.917c9.818 7.413 21.796 11.582 34.099 11.529"
/>
<path
fill="#fbbc05"
d="M84.149 95.989C52.953 96.175 27.815 121.615 28 152.81a56.49 56.49 0 0 0 22.049 44.438l25.193-25.193c-10.93-4.938-15.787-17.802-10.849-28.731s17.802-15.787 28.73-10.85a21.72 21.72 0 0 1 10.85 10.85l25.193-25.193a56.42 56.42 0 0 0-45.018-22.143"
/>
</g>
</svg>
);
@@ -0,0 +1,32 @@
import * as React from "react";
import { IconSvgProps } from "@/types";
export const KS8ProviderBadge: React.FC<IconSvgProps> = ({
size,
width,
height,
...props
}) => (
<svg
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
fill="none"
focusable="false"
height={size || height}
role="presentation"
viewBox="0 0 256 256"
width={size || width}
{...props}
>
<g fill="none">
<rect width="256" height="256" fill="#326ce5" rx="60" />
<path
fill="#fff"
stroke="#fff"
strokeWidth=".11"
d="M128.073 30a6.014 6.014 0 0 0-5.589 6.314v1.605c.153 1.817.426 3.617.785 5.4a53 53 0 0 1 .563 10.326 6.23 6.23 0 0 1-1.86 2.943l-.128 2.407c-3.456.29-6.886.819-10.265 1.587a73.1 73.1 0 0 0-37.153 21.204l-2.047-1.45a4.42 4.42 0 0 1-3.37-.342 53 53 0 0 1-7.655-6.912 47 47 0 0 0-3.729-3.967l-1.263-1.007a6.82 6.82 0 0 0-3.95-1.493 5.46 5.46 0 0 0-4.531 2.013 6.02 6.02 0 0 0 1.4 8.388l1.177.939a47 47 0 0 0 4.71 2.756 49 49 0 0 1 8.516 5.973 6.3 6.3 0 0 1 1.134 3.276l1.86 1.707a73.54 73.54 0 0 0-11.587 51.248l-2.39.683a7.8 7.8 0 0 1-2.44 2.457 53 53 0 0 1-10.188 1.681 48 48 0 0 0-5.46.427l-1.511.341h-.17a5.716 5.716 0 0 0-4.791 6.924 5.7 5.7 0 0 0 1.488 2.736 5.72 5.72 0 0 0 5.836 1.407h.111l1.536-.17a46 46 0 0 0 5.103-1.86 53 53 0 0 1 9.915-2.901 6.2 6.2 0 0 1 3.26 1.151l2.56-.426a73.92 73.92 0 0 0 32.766 40.983l-1.041 2.176a5.7 5.7 0 0 1 .512 3.208 55 55 0 0 1-5.146 9.318 48 48 0 0 0-3.054 4.548l-.726 1.536a5.72 5.72 0 0 0 2.49 8.015 5.7 5.7 0 0 0 3.057.527 5.72 5.72 0 0 0 4.719-3.687l.707-1.45a47 47 0 0 0 1.639-5.205c1.51-3.729 2.33-7.731 4.394-10.206a4.6 4.6 0 0 1 2.441-1.194l1.28-2.33a73.55 73.55 0 0 0 52.443.128l1.134 2.176a4.44 4.44 0 0 1 2.902 1.749 54 54 0 0 1 3.891 9.557 48 48 0 0 0 1.655 5.214l.708 1.45a5.72 5.72 0 0 0 7.784 3.154 5.7 5.7 0 0 0 2.345-2.036 5.71 5.71 0 0 0 .136-5.981l-.742-1.536a47 47 0 0 0-3.055-4.531c-1.962-2.833-3.652-5.854-5.12-9.019a4.43 4.43 0 0 1 .581-3.414 20 20 0 0 1-.948-2.295 74.02 74.02 0 0 0 32.622-41.128l2.423.426a4.53 4.53 0 0 1 3.183-1.177c3.387.648 6.706 1.706 9.915 2.901a43 43 0 0 0 5.102 1.963c.41.111 1.007.204 1.468.315h.111a5.71 5.71 0 0 0 5.823-1.421 5.7 5.7 0 0 0 1.504-2.729 5.7 5.7 0 0 0-.151-3.111 5.72 5.72 0 0 0-4.642-3.806l-1.655-.392a48 48 0 0 0-5.461-.427 52 52 0 0 1-10.188-1.681 6.4 6.4 0 0 1-2.458-2.457l-2.304-.683a73.8 73.8 0 0 0-11.826-51.137l2.014-1.86a4.5 4.5 0 0 1 1.058-3.226 53.6 53.6 0 0 1 8.447-5.939 48 48 0 0 0 4.71-2.756l1.246-1.007a5.72 5.72 0 0 0 1.456-8.42 5.716 5.716 0 0 0-8.53-.463l-1.262 1.007a48 48 0 0 0-3.729 3.968 53.3 53.3 0 0 1-7.45 7.023 6.3 6.3 0 0 1-3.447.375l-2.159 1.536a74.55 74.55 0 0 0-47.229-22.783c0-.853-.111-2.133-.128-2.534a4.48 4.48 0 0 1-1.86-2.833 53 53 0 0 1 .648-10.3c.367-1.791.623-3.583.785-5.4v-1.707a6.01 6.01 0 0 0-5.589-6.314zm-7.117 44.08-1.706 29.806h-.128c-.086 1.843-1.169 3.413-2.833 4.301-1.664.887-3.635.623-5.12-.461L86.722 90.404a58.54 58.54 0 0 1 28.184-15.35 60 60 0 0 1 6.033-.973zm14.233 0a59.1 59.1 0 0 1 34.046 16.418l-24.216 17.211a5.02 5.02 0 0 1-5.274.614 5.02 5.02 0 0 1-2.867-4.471zm-57.34 27.536 22.355 19.95v.128a5.02 5.02 0 0 1 1.587 4.898 5.02 5.02 0 0 1-3.558 3.712v.085l-28.687 8.251a58.53 58.53 0 0 1 8.31-37.032zm100.286 0a59.58 59.58 0 0 1 8.55 36.922l-28.73-8.277v-.111a4.96 4.96 0 0 1-3.559-3.712 5.05 5.05 0 0 1 1.588-4.897l22.185-19.856zm-54.645 21.503h9.148l5.589 7.099-2.031 8.875-8.217 3.95-8.235-3.95-1.937-8.875zm29.302 24.216a5.3 5.3 0 0 1 1.152 0l29.584 4.992c-4.267 12.287-12.689 22.783-23.679 29.805l-11.434-27.68a5 5 0 0 1-.383-2.354 5.04 5.04 0 0 1 4.726-4.635zm-49.687.128a5.03 5.03 0 0 1 4.13 2.287 4.99 4.99 0 0 1 .401 4.693v.111l-11.365 27.459a58.7 58.7 0 0 1-23.534-29.498l29.327-4.975q.489-.051.99 0zm24.78 11.946a5.04 5.04 0 0 1 4.582 2.628h.111l14.455 26.085-5.777 1.707a58.83 58.83 0 0 1-32.067-1.655l14.506-26.085c.879-1.536 2.5-2.475 4.267-2.518z"
/>
</g>
</svg>
);
@@ -0,0 +1,4 @@
export * from "./AWSProviderBadge";
export * from "./AzureProviderBadge";
export * from "./GCPProviderBadge";
export * from "./KS8ProviderBadge";
@@ -0,0 +1,57 @@
import React from "react";
import { IconSvgProps } from "../../../types/index";
export const ProwlerExtended: React.FC<IconSvgProps> = ({
size,
width = 216,
height,
...props
}) => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 1233.67 204.4"
fill="none"
height={size || height}
width={size || width}
color="evenodd"
{...props}
>
<path
className="cls-1"
d="M1169.38 132.04c20.76-12.21 34.44-34.9 34.44-59.79 0-38.18-31.06-69.25-69.25-69.25l-216.9.23V148.4h-64.8V3h-79.95l-47.14 95.97V3h-52.09l-47.14 95.97V3h-53.48v69.6C560.37 30.64 521.34 0 475.28 0c-42.63 0-79.24 26.25-94.54 63.43C376.39 29.4 347.26 3 312.07 3H212.06v47.43C202.9 22.91 176.91 3 146.35 3H0l46.34 46.33v151.64h53.47v-76.68l17.21 17.21h29.33c30.56 0 56.54-19.91 65.71-47.43v106.91h53.48v-81.51l76.01 81.51h69.62l-64.29-68.94c11.14-6.56 20.22-16.15 26.26-27.46 1.27 55.26 46.58 99.82 102.14 99.82 46.06 0 85.09-30.64 97.81-72.6v69.18h60.88l38.34-78.06v78.06h60.88l66.2-134.78v135.69h95.41l22.86-22.86v22.86h95.05l21.84-21.84v20.93h53.48v-81.5l76.01 81.5h69.62l-64.29-68.94ZM146.35 88.02H99.81V56.48h46.54c8.7 0 15.77 7.07 15.77 15.77s-7.07 15.77-15.77 15.77Zm165.72 0-46.54-.18V56.48h46.54c8.7 0 15.77 7.07 15.77 15.77s-7.08 15.77-15.77 15.77Zm163.21 62.9c-26.86 0-48.72-21.86-48.72-48.72s21.86-48.72 48.72-48.72S524 75.34 524 102.2s-21.86 48.72-48.72 48.72Zm559.28-2.51h-63.41v-20.35h42.91V77.18h-42.91V56.72h63.41v91.69Zm100.01-60.39-46.54-.18V56.48h46.54c8.7 0 15.77 7.07 15.77 15.77s-7.07 15.77-15.77 15.77Z"
fill="currentColor"
fillRule="evenodd"
clipRule="evenodd"
/>
</svg>
);
};
export const ProwlerShort: React.FC<IconSvgProps> = ({
size,
width = 30,
height,
...props
}) => (
<svg
id="Layer_1"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 432.08 396.77"
fill="none"
height={size || height}
width={size || width}
color="evenodd"
{...props}
>
<path
className="cls-1"
d="M293.3.01H0s92.87,92.85,92.87,92.85v303.9h107.17v-153.68l34.48,34.49h58.78c76.52,0,138.78-62.26,138.78-138.78S369.82.01,293.3.01ZM293.3,170.4h-93.27v-63.21h93.27c17.43,0,31.6,14.18,31.6,31.6s-14.18,31.6-31.6,31.6Z"
fill="currentColor"
fillRule="evenodd"
clipRule="evenodd"
color="evenodd"
/>
</svg>
);
@@ -0,0 +1,923 @@
import { IconSvgProps } from "@/types";
export const IAMAccessAnalyzerIcon: React.FC<IconSvgProps> = ({
size = 32,
width,
height,
className = "rounded-md",
...props
}) => (
<svg
aria-hidden="false"
aria-label="IAM Access Analyzer logo"
fill="none"
focusable="false"
height={size || height}
role="presentation"
viewBox="0 0 40 40"
width={size || width}
className={className}
{...props}
xmlns="http://www.w3.org/2000/svg"
>
<g fill="none" fillRule="evenodd">
<path fill="#DD344C" d="M0 0h40v40H0z" />
<path
fill="#FFF"
d="m33.131 31.955-6.998-6.998a.501.501 0 0 1-.092-.58c.945-1.85.63-4.273-.732-5.635a4.629 4.629 0 0 0-3.855-1.33c-1.432.173-2.696 1.001-3.466 2.27a4.583 4.583 0 0 0 .103 4.868c1.354 2.06 3.977 2.781 6.094 1.681a.5.5 0 0 1 .584.091l1.194 1.194a.504.504 0 0 1 .146.39l-.089 1.258 1.38-.003h.001a.5.5 0 0 1 .353.147l.598.597a.502.502 0 0 1 .146.347l.017 1.406 1.231-.106a.502.502 0 0 1 .397.144l1.097 1.097 1.761.45.13-1.288Zm1.019-.135-.213 2.101a.5.5 0 0 1-.621.434l-2.456-.627a.499.499 0 0 1-.23-.13l-1.029-1.03-1.537.133a.499.499 0 0 1-.543-.493l-.021-1.74-.306-.306-1.709.004h-.001a.502.502 0 0 1-.499-.535l.11-1.569-.783-.783c-2.515 1.1-5.491.202-7.057-2.179a5.587 5.587 0 0 1-.122-5.937c.931-1.533 2.462-2.534 4.201-2.744a5.63 5.63 0 0 1 4.682 1.616c1.8 1.8 1.898 4.535 1.072 6.463l6.918 6.918a.5.5 0 0 1 .144.404ZM22.737 21.208a1.087 1.087 0 0 0-.774-.32c-.292 0-.567.113-.774.32a1.098 1.098 0 0 0 0 1.55 1.12 1.12 0 0 0 1.548 0 1.098 1.098 0 0 0 0-1.55Zm.707-.707a2.099 2.099 0 0 1 0 2.964 2.09 2.09 0 0 1-1.481.613 2.09 2.09 0 0 1-1.481-.613 2.099 2.099 0 0 1 0-2.964 2.097 2.097 0 0 1 2.962 0ZM9.662 13.312c0 .263.016.52.047.778a.504.504 0 0 1-.423.56C8.035 14.981 6 15.968 6 18.882c0 .107.006.213.011.315.135 2.461 2.487 3.794 4.633 3.8H15v1h-4.357c-3.063-.008-5.483-2.048-5.63-4.746A6.369 6.369 0 0 1 5 18.882c0-3.389 2.318-4.647 3.677-5.094a7.265 7.265 0 0 1-.015-.476c0-2.777 1.876-5.653 4.364-6.69 2.91-1.226 5.992-.618 8.245 1.625.591.581 1.348 1.451 1.88 2.511.566-.515 1.207-.768 1.941-.768 1.455 0 3.086 1.217 3.376 3.88 3.008.715 4.532 2.43 4.532 5.1 0 2.927-1.281 4.57-3.916 5.02l-.168-.987C30.352 22.758 32 22.08 32 18.97c0-3.018-2.34-3.843-4.099-4.2a.5.5 0 0 1-.4-.462c-.113-1.953-1.103-3.317-2.409-3.317-.664 0-1.186.302-1.691.981a.501.501 0 0 1-.872-.13c-.437-1.22-1.308-2.24-1.961-2.883-1.958-1.95-4.634-2.476-7.157-1.414-2.102.877-3.749 3.41-3.749 5.768Z"
/>
</g>
</svg>
);
export const AWSAccountIcon: React.FC<IconSvgProps> = ({
size = 32,
width,
height,
className = "rounded-md",
...props
}) => (
<svg
aria-hidden="false"
aria-label="AWS Account logo"
fill="none"
focusable="false"
height={size || height}
role="presentation"
viewBox="0 0 40 40"
width={size || width}
className={className}
{...props}
xmlns="http://www.w3.org/2000/svg"
>
<g fill="none" fillRule="evenodd">
<path fill="#E7157B" d="M0 0h40v40H0z" />
<path
fill="#FFF"
d="M24.5 22c1.93 0 3.5 1.57 3.5 3.5S26.43 29 24.5 29 21 27.43 21 25.5s1.57-3.5 3.5-3.5Zm0 8c2.481 0 4.5-2.019 4.5-4.5S26.981 21 24.5 21a4.505 4.505 0 0 0-4.5 4.5c0 2.481 2.019 4.5 4.5 4.5Zm1.523-19.12L29.084 17h-6.121l3.06-6.12ZM22.154 18h7.739a.502.502 0 0 0 .447-.724l-3.869-7.738c-.17-.338-.725-.338-.895 0l-3.869 7.738a.503.503 0 0 0 .447.724ZM11 23h7v-7h-7v7Zm-.5 1h8a.5.5 0 0 0 .5-.5v-8a.5.5 0 0 0-.5-.5h-8a.5.5 0 0 0-.5.5v8a.5.5 0 0 0 .5.5ZM8 32h24V8H8v24ZM32.5 7h-25a.5.5 0 0 0-.5.5v25a.5.5 0 0 0 .5.5h25a.5.5 0 0 0 .5-.5v-25a.5.5 0 0 0-.5-.5Z"
/>
</g>
</svg>
);
export const AWSCertificateManagerIcon: React.FC<IconSvgProps> = ({
size = 32,
width,
height,
className = "rounded-md",
...props
}) => (
<svg
aria-hidden="false"
aria-label="AWS Certificate Manager logo"
fill="none"
focusable="false"
height={size || height}
role="presentation"
viewBox="0 0 40 40"
width={size || width}
className={className}
{...props}
xmlns="http://www.w3.org/2000/svg"
>
<g
id="Icon-Architecture/32/Arch_AWS-Certificate-Manager_32"
stroke="none"
strokeWidth="1"
fill="none"
fillRule="evenodd"
>
<g
id="Icon-Architecture-BG/32/Security-Identity-Compliance"
fill="#DD344C"
>
<rect id="Rectangle" x="0" y="0" width="40" height="40" />
</g>
<path
d="M17.8323574,19.6308709 C17.9645126,19.7501051 18.0221441,19.9309436 17.9843855,20.1058203 L17.4706696,22.4676501 L19.7401611,21.076585 C19.8991448,20.9782168 20.0998616,20.9782168 20.2608325,21.076585 L22.513432,22.4646692 L22.0146208,20.0978714 C21.9788495,19.9259755 22.0354874,19.7481179 22.1646616,19.6288837 L24.1787856,17.7877097 L21.5138223,17.2730156 C21.3608006,17.2432071 21.2306327,17.1438453 21.162071,17.0027516 L19.9955286,14.6160813 L18.8021576,17.0097069 C18.7326023,17.1478198 18.6034281,17.2461879 18.4523936,17.2750029 L15.7953795,17.7926778 L17.8323574,19.6308709 Z M16.2524575,23.3946956 L16.9529792,20.1753736 L14.3913553,17.8632247 C14.2512509,17.7370352 14.1956067,17.5432797 14.2462827,17.3614476 C14.2959651,17.1806092 14.4450122,17.0434899 14.6288371,17.006726 L18.0211505,16.3469637 L19.5553426,13.2717163 C19.6398027,13.1028012 19.8405195,12.965682 20.0024841,12.9964841 C20.1912772,12.9974777 20.3641719,13.1057821 20.4466447,13.2756908 L21.9460591,16.3439829 L25.3373789,16.9997707 C25.5212037,17.0345473 25.6702509,17.170673 25.7209269,17.3525051 C25.771603,17.5323499 25.7169523,17.7261054 25.5788353,17.8532885 L23.0450335,20.1704055 L23.7246886,23.3927084 C23.7654282,23.5864639 23.68693,23.7871747 23.5249654,23.9014407 C23.3600199,24.0167004 23.1473793,24.0216685 22.9774655,23.9183323 L19.9995032,22.08312 L16.9976933,23.9233003 C16.9172078,23.9729812 16.8277796,23.9968281 16.7373576,23.9968281 C16.6369992,23.9968281 16.5356471,23.9660259 16.4501934,23.9054152 C16.2872352,23.7901555 16.2097307,23.5884511 16.2524575,23.3946956 L16.2524575,23.3946956 Z M32.9785301,10.0951198 L32.9755492,7.99361792 L6.99364775,7.99361792 L6.99364775,10.0951198 L32.9785301,10.0951198 Z M34,32.5021974 C34,32.6343486 33.9483303,32.7605381 33.8549274,32.8539382 C33.7615245,32.9473383 33.6353313,33 33.5031761,33 L29.9369743,33 L29.9369743,32.0063821 L33.0063522,32.0063821 L32.9795238,11.0887377 L6.99364775,11.0887377 L6.99364775,32.0063821 L25.9623833,32.0063821 L25.9623833,33 L6.49682388,33 C6.36466872,33 6.23847546,32.9473383 6.14507257,32.8549318 C6.05266333,32.7615317 6,32.6353422 6,32.503191 L6,7.49680896 C6,7.22257041 6.22158345,7 6.49682388,7 L33.4713794,7 C33.7466198,7 33.9682033,7.22257041 33.9682033,7.49680896 L34,32.5021974 Z M11.0576671,29.9754271 L15.0322581,29.9754271 L15.0322581,28.9818091 L11.0576671,28.9818091 L11.0576671,29.9754271 Z M17.0195536,29.9982803 L23.9750878,29.9982803 L23.9750878,29.0046624 L17.0195536,29.0046624 L17.0195536,29.9982803 Z M11.0576671,26.9985478 L15.0322581,26.9985478 L15.0322581,26.0049299 L11.0576671,26.0049299 L11.0576671,26.9985478 Z M17.0195536,26.9985478 L27.9496788,26.9985478 L27.9496788,26.0049299 L17.0195536,26.0049299 L17.0195536,26.9985478 Z"
id="AWS-Certificate-Manager_Icon_32_Squid"
fill="#FFFFFF"
/>
</g>
</svg>
);
export const AWSAthenaIcon: React.FC<IconSvgProps> = ({
size = 32,
width,
height,
className = "rounded-md",
...props
}) => (
<svg
aria-hidden="false"
aria-label="Amazon Athena logo"
fill="none"
focusable="false"
height={size || height}
role="presentation"
viewBox="0 0 40 40"
width={size || width}
className={className}
{...props}
xmlns="http://www.w3.org/2000/svg"
>
<g fill="none" fillRule="evenodd">
<path fill="#8C4FFF" d="M0 0h40v40H0z" />
<path
fill="#FFF"
d="M18.726 14.285c1.8 0 2.982.285 3.42.5-.438.214-1.62.498-3.42.498-1.8 0-2.982-.284-3.42-.499.438-.214 1.62-.499 3.42-.499Zm.014 3.198c-.088-.027-.164-.079-.164-.181 0-.14.114-.253.256-.253.042 0 .072.026.107.043l-.199.391Zm2.939 4.156a1.203 1.203 0 0 1-.303.156 4.024 4.024 0 0 1-.566.166 9.261 9.261 0 0 1-1.978.202c-1.542 0-2.616-.322-2.846-.522l-.688-5.794c1.187.401 2.937.433 3.428.433.496 0 2.275-.033 3.464-.445l-.202 2.298a34.447 34.447 0 0 1-2.44-1.048.754.754 0 0 0-1.475.216.753.753 0 0 0 1.041.69c1.178.56 2.09.944 2.781 1.186l-.216 2.462Zm-7.525-6.797.822 6.891c0 1.411 3.698 1.427 3.856 1.427.78 0 1.564-.08 2.206-.226.269-.061.507-.133.71-.212.624-.241.94-.574.938-.946l.204-2.305c.299.068.534.098.715.098.478 0 .604-.189.69-.319l.077-.476c-.122-.492-1-1.187-1.297-1.404l.224-2.543-.005-.001c.001-.014.007-.027.007-.042 0-1.424-3.811-1.496-4.575-1.496-.764 0-4.575.072-4.575 1.496 0 .02.008.037.01.057h-.007Zm4.591-5.448c4.765 0 8.643 3.803 8.643 8.478s-3.878 8.478-8.643 8.478c-4.765 0-8.642-3.803-8.642-8.478s3.877-8.478 8.642-8.478ZM6.017 21.267v.998h4.177a9.69 9.69 0 0 0 8.551 5.082c5.326 0 9.66-4.25 9.66-9.475 0-5.224-4.334-9.475-9.66-9.475-4.41 0-8.132 2.917-9.283 6.886H6.017v.997h3.217a9.236 9.236 0 0 0-.148 1.592c0 .136.015.268.02.403H5v.997h4.203c.105.693.285 1.36.535 1.995H6.017Zm27.486 11.265c-.614.603-1.68.608-2.295.005l-5.253-5.152a12.068 12.068 0 0 0 2.34-2.246l5.214 5.142a1.566 1.566 0 0 1-.006 2.25Zm.726-2.955-5.334-5.26a11.64 11.64 0 0 0 1.953-6.445C30.848 11.326 25.418 6 18.745 6 13.95 6 9.6 8.782 7.665 13.087l.93.402c1.773-3.943 5.757-6.492 10.15-6.492 6.112 0 11.086 4.879 11.086 10.875s-4.974 10.876-11.086 10.876c-3.732 0-7.192-1.827-9.254-4.885l-.848.551c2.251 3.338 6.027 5.331 10.102 5.331 2.333 0 4.506-.662 6.356-1.788l5.388 5.286c.498.488 1.159.757 1.86.757a2.66 2.66 0 0 0 1.872-.761 2.55 2.55 0 0 0 .008-3.662Z"
/>
</g>
</svg>
);
export const AWSLambdaIcon: React.FC<IconSvgProps> = ({
size = 32,
width,
height,
className = "rounded-md",
...props
}) => (
<svg
aria-hidden="false"
aria-label="AWS Lambda logo"
fill="none"
focusable="false"
height={size || height}
role="presentation"
viewBox="0 0 40 40"
width={size || width}
className={className}
{...props}
xmlns="http://www.w3.org/2000/svg"
>
<g fill="none" fillRule="evenodd">
<path fill="#ED7100" d="M0 0h40v40H0z" />
<path
fill="#FFF"
d="M14.386 33H8.27l6.763-14.426 3.064 6.44L14.387 33Zm1.085-15.798a.49.49 0 0 0-.442-.282h-.002a.493.493 0 0 0-.441.285l-7.538 16.08a.507.507 0 0 0 .028.482c.09.145.247.233.415.233h7.206c.19 0 .363-.111.445-.286l3.944-8.489a.508.508 0 0 0-.002-.432l-3.613-7.591ZM32.018 33h-5.882l-9.47-20.711a.491.491 0 0 0-.444-.289H12.37l.005-5h7.549l9.424 20.71c.08.177.256.29.446.29h2.224v5Zm.49-6h-2.4L20.684 6.29a.492.492 0 0 0-.446-.29h-8.353a.496.496 0 0 0-.491.5l-.006 6c0 .132.052.259.144.354a.488.488 0 0 0 .347.146h4.032l9.468 20.711c.08.176.254.289.445.289h6.686a.495.495 0 0 0 .491-.5v-6c0-.276-.219-.5-.491-.5Z"
/>
</g>
</svg>
);
export const AWSCloudFormationIcon: React.FC<IconSvgProps> = ({
size = 32,
width,
height,
className = "rounded-md",
...props
}) => (
<svg
aria-hidden="false"
aria-label="AWS CloudFormation logo"
fill="none"
focusable="false"
height={size || height}
role="presentation"
viewBox="0 0 40 40"
width={size || width}
className={className}
{...props}
xmlns="http://www.w3.org/2000/svg"
>
<g fill="none" fillRule="evenodd">
<path fill="#E7157B" d="M0 0h40v40H0z" />
<path
fill="#FFF"
d="M27 19.982h3V18.98h-3v1.002Zm-13 6.009h3v-1.002h-3v1.002Zm-5 0h3v-1.002H9v1.002Zm0-3.005h6v-1.001H9v1.001Zm0-6.009h5v-1.001H9v1.001Zm0 3.005h16V18.98H9v1.002Zm9.968 11.016H7V13.973h11.968v3.021h1v-3.522a.5.5 0 0 0-.5-.5H6.5a.5.5 0 0 0-.5.5V31.5a.5.5 0 0 0 .5.501h12.968a.5.5 0 0 0 .5-.5v-9.498h-1v8.996ZM34 18.48c0 4.455-3.96 4.506-4 4.506h-8v-1.001h8c.305-.003 3-.122 3-3.505 0-2.848-2.569-3.425-3.083-3.513a.5.5 0 0 1-.415-.535l.003-.03c-.029-1.607-1.015-2.11-1.442-2.25-.799-.268-1.687-.012-2.158.618a.5.5 0 0 1-.871-.131c-.311-.87-.762-1.434-1.388-2.062-1.569-1.559-3.691-1.987-5.683-1.15-1.044.44-1.956 1.438-2.502 2.737l-.922-.388c.646-1.539 1.753-2.731 3.036-3.272 2.379-1.002 4.911-.491 6.777 1.364.534.536.978 1.057 1.323 1.734.749-.552 1.752-.717 2.704-.402 1.216.405 1.985 1.461 2.109 2.865C32.19 14.495 34 15.857 34 18.48Z"
/>
</g>
</svg>
);
export const AWSCloudTrailIcon: React.FC<IconSvgProps> = ({
size = 32,
width,
height,
className = "rounded-md",
...props
}) => (
<svg
aria-hidden="false"
aria-label="AWS CloudTrail logo"
fill="none"
focusable="false"
height={size || height}
role="presentation"
viewBox="0 0 40 40"
width={size || width}
className={className}
{...props}
xmlns="http://www.w3.org/2000/svg"
>
<g fill="none" fillRule="evenodd">
<path fill="#E7157B" d="M0 0h40v40H0z" />
<path
fill="#FFF"
d="M14.027 24.131h4.013v-.97h-4.013v.97Zm15.959-5.31c-.11-1.476-.846-2.56-2.017-2.938-.927-.302-1.958-.084-2.746.536a10.025 10.025 0 0 0-1.813-2.395c-2.006-1.915-4.56-2.507-7.01-1.622-2.257.815-4.881 3.57-4.881 5.9v.029c-1.474.368-2.505 2.275-2.505 3.316h1.003c0-.752.969-2.408 2.003-2.408a.493.493 0 0 0 .502-.485v-.452c0-1.866 2.326-4.303 4.23-4.99 2.827-1.022 4.944.44 5.953 1.403.651.62 1.52 1.738 1.886 2.725.064.168.216.289.399.316a.517.517 0 0 0 .474-.188c.548-.704 1.424-1.01 2.187-.765.856.277 1.346 1.157 1.346 2.414 0 .238.178.44.42.478 1.238.2 3.58 1.28 3.58 3.95 0 3.321-3.357 3.383-3.498 3.384H18.04V28H29.5c1.557-.003 4.5-.916 4.5-4.354 0-3.047-2.432-4.427-4.014-4.824Zm-13.952 7.245h7.023v-.971h-7.023v.97ZM13.024 28h3.01v-.97h-3.01V28Zm-1.004-1.934h2.007v-.971H12.02v.97ZM7.003 24.13h5.017v-.97H7.003v.97ZM6 26.066h5.017v-.971H6v.97ZM8.007 28h3.01v-.97h-3.01V28Z"
/>
</g>
</svg>
);
export const AWSCloudWatchIcon: React.FC<IconSvgProps> = ({
size = 32,
width,
height,
className = "rounded-md",
...props
}) => (
<svg
aria-hidden="false"
aria-label="AWS CloudWatch logo"
fill="none"
focusable="false"
height={size || height}
role="presentation"
viewBox="0 0 40 40"
width={size || width}
className={className}
{...props}
xmlns="http://www.w3.org/2000/svg"
>
<g fill="none" fillRule="evenodd">
<path fill="#E7157B" d="M0 0h40v40H0z" />
<path
fill="#FFF"
d="M27.63 24.124c0-1.903-1.58-3.452-3.522-3.452-1.943 0-3.523 1.549-3.523 3.452 0 1.904 1.58 3.452 3.523 3.452 1.942 0 3.522-1.548 3.522-3.452Zm1.006 0c0 2.447-2.032 4.439-4.528 4.439-2.497 0-4.53-1.992-4.53-4.439s2.033-4.438 4.53-4.438c2.496 0 4.528 1.991 4.528 4.438Zm4.276 6.579-3.387-2.99c-.289.418-.63.793-1.008 1.133l3.367 2.976a.781.781 0 0 0 1.085-.056.745.745 0 0 0-.057-1.063Zm-8.804-1.154c3.051 0 5.535-2.433 5.535-5.425 0-2.991-2.484-5.424-5.535-5.424-3.052 0-5.536 2.433-5.536 5.424 0 2.992 2.484 5.425 5.536 5.425Zm9.478.422a1.72 1.72 0 0 1 .13 2.455 1.79 1.79 0 0 1-1.32.574c-.423 0-.847-.147-1.185-.445l-3.494-3.089a6.592 6.592 0 0 1-3.61 1.07c-3.606 0-6.541-2.877-6.541-6.412s2.935-6.41 6.542-6.41c3.607 0 6.541 2.875 6.541 6.41 0 .968-.226 1.883-.62 2.707l3.557 3.14Zm-22.994-5.354h4.96v.987h-4.966c-.557-.006-1.156-.206-1.786-.596-1.044-.634-2.8-2.127-2.8-4.973 0-3.442 2.41-4.706 3.812-5.15a7.265 7.265 0 0 1-.016-.492c0-2.804 1.942-5.714 4.515-6.765 3.013-1.237 6.203-.623 8.531 1.638a8.456 8.456 0 0 1 1.786 2.563 3.404 3.404 0 0 1 2.177-.787c1.56 0 3.319 1.165 3.626 3.713C31.887 15.08 35 16.23 35 20.075c0 1.653-.567 2.99-1.687 3.972l-.672-.733c.898-.788 1.353-1.878 1.353-3.238 0-3.382-2.888-4.223-4.13-4.427a.5.5 0 0 1-.33-.207.486.486 0 0 1-.08-.378c-.147-2.074-1.438-3.036-2.65-3.036-.756 0-1.466.361-1.947.99a.513.513 0 0 1-.477.194.5.5 0 0 1-.398-.32c-.433-1.172-1.054-2.156-1.847-2.925-2.032-1.973-4.812-2.508-7.437-1.43-2.184.894-3.895 3.465-3.895 5.856 0 .268.016.536.048.794a.486.486 0 0 1-.122.387.51.51 0 0 1-.29.16c-1.297.327-3.433 1.32-3.433 4.301 0 2.25 1.266 3.492 2.327 4.138.473.292.896.44 1.26.444Z"
/>
</g>
</svg>
);
export const AWSConfigIcon: React.FC<IconSvgProps> = ({
size = 32,
width,
height,
className = "rounded-md",
...props
}) => (
<svg
aria-hidden="false"
aria-label="AWS Config logo"
fill="none"
focusable="false"
height={size || height}
role="presentation"
viewBox="0 0 40 40"
width={size || width}
className={className}
{...props}
xmlns="http://www.w3.org/2000/svg"
>
<g fill="none" fillRule="evenodd">
<path fill="#E7157B" d="M0 0h40v40H0z" />
<path
fill="#FFF"
d="m29.765 24.019.945.333-.592 1.68a.502.502 0 0 1-.526.332l-1.915-.205.105-.996.908.096a2.472 2.472 0 0 0-2.005-1.042 2.48 2.48 0 0 0-2.421 1.978l-.983-.2a3.487 3.487 0 0 1 3.404-2.78c1.157 0 2.2.587 2.838 1.492l.242-.688Zm-.657 3.165.982.2a3.488 3.488 0 0 1-3.405 2.78 3.474 3.474 0 0 1-2.718-1.331l-.12.544-.979-.217.395-1.777a.503.503 0 0 1 .562-.387l1.808.27-.147.99-.79-.117a2.474 2.474 0 0 0 1.99 1.024 2.48 2.48 0 0 0 2.422-1.979Zm3.89.36-.594-.035a.487.487 0 0 0-.517.39 5.286 5.286 0 0 1-.682 1.65.502.502 0 0 0 .089.64l.444.396-1.153 1.153-.394-.442a.502.502 0 0 0-.641-.09 5.25 5.25 0 0 1-1.65.684.5.5 0 0 0-.39.518l.033.59h-1.63l.034-.59a.5.5 0 0 0-.389-.519 5.198 5.198 0 0 1-1.65-.684.503.503 0 0 0-.64.09l-.396.443-1.153-1.152.443-.395a.5.5 0 0 0 .09-.642 5.272 5.272 0 0 1-.683-1.65.49.49 0 0 0-.516-.39l-.594.034v-1.63l.596.033a.483.483 0 0 0 .516-.39 5.27 5.27 0 0 1 .685-1.646.501.501 0 0 0-.09-.642l-.447-.398 1.153-1.152.398.445a.5.5 0 0 0 .64.09 5.26 5.26 0 0 1 1.648-.682.5.5 0 0 0 .389-.517l-.034-.597h1.63l-.033.597a.501.501 0 0 0 .39.518c.585.132 1.138.361 1.645.682a.5.5 0 0 0 .64-.09l.4-.447 1.153 1.153-.446.397a.502.502 0 0 0-.09.642c.322.508.552 1.063.684 1.647.054.238.254.415.517.39l.595-.033v1.63Zm.845-2.526a.53.53 0 0 0-.373-.137l-.715.042a6.254 6.254 0 0 0-.49-1.181l.535-.477a.499.499 0 0 0 .02-.729l-1.902-1.903a.602.602 0 0 0-.368-.146.496.496 0 0 0-.36.167l-.48.538a6.197 6.197 0 0 0-1.176-.49l.04-.718a.5.5 0 0 0-.5-.53h-2.691a.493.493 0 0 0-.364.158.498.498 0 0 0-.136.373l.04.717a6.215 6.215 0 0 0-1.178.489l-.478-.537a.498.498 0 0 0-.36-.167.553.553 0 0 0-.369.146l-1.902 1.903a.501.501 0 0 0 .02.729l.536.479c-.203.376-.367.77-.49 1.179l-.715-.042a.537.537 0 0 0-.373.137.503.503 0 0 0-.156.364v2.691c0 .139.057.27.156.365a.49.49 0 0 0 .373.135l.713-.04c.122.411.285.806.489 1.183l-.533.475a.502.502 0 0 0-.02.727l1.902 1.904a.502.502 0 0 0 .728-.021l.476-.533c.376.205.772.37 1.182.491l-.041.712a.5.5 0 0 0 .5.529h2.69a.501.501 0 0 0 .5-.53l-.04-.71c.41-.123.805-.286 1.183-.49l.475.531a.5.5 0 0 0 .36.168.56.56 0 0 0 .367-.147l1.903-1.904a.5.5 0 0 0-.021-.727l-.533-.477c.203-.377.366-.772.489-1.18l.714.04a.503.503 0 0 0 .53-.5v-2.692a.5.5 0 0 0-.157-.364ZM8.002 27.973h9.049v1.002h-9.55a.501.501 0 0 1-.501-.5V6.5C7 6.224 7.224 6 7.5 6h23.108c.276 0 .5.224.5.501v11.484h-1.001V7.002H8.002v20.971Zm16.547-12.995h-.494V12.97H27.101v2.008h-2.552Zm.501-3.01h.986V9.988h-.986v1.982Zm1.988 6.017V15.98h.564c.276 0 .5-.224.5-.5v-3.011a.501.501 0 0 0-.5-.501h-.564V9.485a.5.5 0 0 0-.501-.5h-1.988a.5.5 0 0 0-.5.5v2.483h-.495a.5.5 0 0 0-.5.5v3.011a.5.5 0 0 0 .5.501h.494v2.005h1.002V15.98h.986v2.005h1.002Zm-9.518.955h-.57v-1.91h3.106v1.91H17.52Zm.5-2.913h1.034v-6.04H18.02v6.04Zm2.037 5.903v-1.988h.5c.277 0 .502-.225.502-.501v-2.912a.501.501 0 0 0-.501-.502h-.501V9.485a.5.5 0 0 0-.501-.5H17.52a.5.5 0 0 0-.501.5v6.542h-.57c-.276 0-.5.225-.5.502v2.912c0 .276.224.5.5.5h.57v5.955h1.002v-5.954h1.034v1.988h1.002Zm-9.071-8.878h.986V9.987h-.986v3.065Zm0 11.979h.986v-8.083h-.986v8.083Zm-.994-10.977H12.966v1.891H9.992v-1.89Zm.493 11.979h1.988a.5.5 0 0 0 .501-.501v-8.585h.493a.5.5 0 0 0 .501-.5v-2.894a.5.5 0 0 0-.5-.5h-.494V9.484a.5.5 0 0 0-.5-.5h-1.99a.5.5 0 0 0-.5.5v3.567H9.49a.5.5 0 0 0-.501.501v2.893a.5.5 0 0 0 .5.501h.494v8.585a.5.5 0 0 0 .5.5Z"
/>
</g>
</svg>
);
export const AWSDatabaseMigrationServiceIcon: React.FC<IconSvgProps> = ({
size = 32,
width,
height,
className = "rounded-md",
...props
}) => (
<svg
aria-hidden="false"
aria-label="AWS Database Migration Service logo"
fill="none"
focusable="false"
height={size || height}
role="presentation"
viewBox="0 0 40 40"
width={size || width}
className={className}
{...props}
xmlns="http://www.w3.org/2000/svg"
>
<g fill="none" fillRule="evenodd">
<path fill="#C925D1" d="M0 0h40v40H0z" />
<path
fill="#FFF"
d="M22.024 33h1v-2.997h-1V33Zm-5.019 0h1v-2.997h-1V33ZM26 32.001h1v-2.996h-1V32Zm-13 0h1v-2.996h-1V32Zm7.018-14.96c-3.717 0-6.018-.958-6.018-1.847v-3.548c1.173.885 3.304 1.418 6.018 1.418 2.604 0 4.782-.583 5.982-1.49v3.62c0 .908-2.238 1.847-5.982 1.847Zm0 5.019c-3.547 0-6.018-1.084-6.018-2.057V16.8c1.34.892 3.805 1.24 6.018 1.24 2.725 0 4.825-.462 5.982-1.25v3.213c0 .973-2.456 2.057-5.982 2.057Zm0 5.006c-3.491 0-6.018-1.503-6.018-2.516v-2.9c1.294.928 3.664 1.408 6.018 1.408 2.635 0 4.793-.547 5.982-1.416v2.908c0 1-2.386 2.516-5.982 2.516Zm0-19.067c3.695 0 5.982.989 5.982 1.905 0 .903-2.276 2.161-5.982 2.161-3.43 0-6.018-.929-6.018-2.161 0-.778 2.143-1.905 6.018-1.905Zm0-.999C16.638 7 13 7.909 13 9.904V24.55c0 1.873 3.279 3.515 7.018 3.515 3.719 0 6.982-1.642 6.982-3.515V9.904C27 7.898 23.493 7 20.018 7Z"
/>
</g>
</svg>
);
export const AmazonEC2Icon: React.FC<IconSvgProps> = ({
size = 32,
width,
height,
className = "rounded-md",
...props
}) => (
<svg
aria-hidden="false"
aria-label="Amazon EC2 logo"
fill="none"
focusable="false"
height={size || height}
role="presentation"
viewBox="0 0 40 40"
width={size || width}
className={className}
{...props}
xmlns="http://www.w3.org/2000/svg"
>
<g fill="none" fillRule="evenodd">
<path fill="#ED7100" d="M0 0h40v40H0z" />
<path
fill="#FFF"
d="M26.052 27 26 13.948 13 14v13.052L26.052 27ZM27 14h2v1h-2v2h2v1h-2v2h2v1h-2v2h2v1h-2v2h2v1h-2v.052a.95.95 0 0 1-.948.948H26v2h-1v-2h-2v2h-1v-2h-2v2h-1v-2h-2v2h-1v-2h-2v2h-1v-2h-.052a.95.95 0 0 1-.948-.948V27h-2v-1h2v-2h-2v-1h2v-2h-2v-1h2v-2h-2v-1h2v-2h-2v-1h2v-.052a.95.95 0 0 1 .948-.948H13v-2h1v2h2v-2h1v2h2v-2h1v2h2v-2h1v2h2v-2h1v2h.052a.95.95 0 0 1 .948.948V14Zm-6 19H7V19h2v-1H7.062C6.477 18 6 18.477 6 19.062v13.876C6 33.523 6.477 34 7.062 34h13.877c.585 0 1.061-.477 1.061-1.062V31h-1v2ZM34 7.062v13.876c0 .585-.476 1.062-1.061 1.062H30v-1h3V7H19v3h-1V7.062C18 6.477 18.477 6 19.062 6h13.877C33.524 6 34 6.477 34 7.062Z"
/>
</g>
</svg>
);
export const AmazonEMRIcon: React.FC<IconSvgProps> = ({
size = 32,
width,
height,
className = "rounded-md",
...props
}) => (
<svg
aria-hidden="false"
aria-label="Amazon EMR logo"
fill="none"
focusable="false"
height={size || height}
role="presentation"
viewBox="0 0 40 40"
width={size || width}
className={className}
{...props}
xmlns="http://www.w3.org/2000/svg"
>
<g fill="none" fillRule="evenodd">
<path fill="#8C4FFF" d="M0 0h40v40H0z" />
<path
fill="#FFF"
d="M32.011 25.364a2.01 2.01 0 0 0-.584-1.423 1.984 1.984 0 0 0-2.82 0 2.025 2.025 0 0 0 0 2.844c.753.76 2.067.761 2.82 0 .376-.38.584-.884.584-1.421Zm-4.777-1.07c.14-.372.353-.722.646-1.026l-1.058-2.03-1.316 2.507 1.728.55Zm-3.159.043-6.866-2.189a5.531 5.531 0 0 1-1.405 1.654l3.386 4.737a2.949 2.949 0 0 1 2.832-.29l2.053-3.912Zm-1.226 6.668a1.993 1.993 0 0 0-1.981-1.996c-.508 0-1.015.194-1.401.584-.374.378-.58.879-.58 1.412 0 .534.206 1.035.58 1.413.772.778 2.03.778 2.801 0 .375-.378.58-.88.58-1.413Zm-10.401-7.027c2.458 0 4.457-2.015 4.457-4.495 0-2.478-2-4.495-4.457-4.495-2.458 0-4.457 2.017-4.457 4.495 0 2.48 2 4.495 4.457 4.495Zm6.439-14.983c0 .534.206 1.035.58 1.413.772.779 2.03.779 2.801 0 .375-.378.58-.88.58-1.413a1.993 1.993 0 0 0-1.98-1.997 1.993 1.993 0 0 0-1.981 1.997Zm3.016 2.801a2.981 2.981 0 0 1-1.035.195 2.947 2.947 0 0 1-1.563-.455l-2.974 4.1a5.511 5.511 0 0 1 1.321 2.222l6.374-1.985-2.123-4.077Zm-4.007 7.687c0 .612-.103 1.197-.286 1.746l6.936 2.21 1.717-3.272-1.768-3.395-6.64 2.068c.024.212.041.426.041.643Zm7.562-3.01 1.368 2.625 1.292-2.463c-.07-.06-.145-.108-.212-.175a2.985 2.985 0 0 1-.453-.609l-1.995.622Zm3.149-.72c.753.76 2.067.762 2.82 0 .376-.379.584-.883.584-1.42a2.01 2.01 0 0 0-.584-1.423 1.984 1.984 0 0 0-2.82 0 2.025 2.025 0 0 0 0 2.844Zm3.52 7.482a3.03 3.03 0 0 1 0 4.256 2.957 2.957 0 0 1-2.11.88 2.96 2.96 0 0 1-2.11-.88 3.01 3.01 0 0 1-.865-2.21l-2.007-.638-2.18 4.15c.037.034.079.058.114.094.56.565.87 1.318.87 2.118 0 .8-.31 1.554-.87 2.119-.58.584-1.34.876-2.101.876a2.947 2.947 0 0 1-2.101-.876 2.987 2.987 0 0 1-.871-2.119c0-.643.208-1.25.577-1.76l-3.5-4.898c-.756.4-1.614.63-2.525.63C9.445 24.977 7 22.513 7 19.483c0-3.028 2.445-5.493 5.448-5.493 1.163 0 2.24.372 3.125 1l2.992-4.124a2.98 2.98 0 0 1-.669-1.87c0-.801.309-1.553.87-2.12a2.956 2.956 0 0 1 4.203 0c.56.567.87 1.319.87 2.12a2.99 2.99 0 0 1-.87 2.118c-.066.067-.142.117-.213.176l2.232 4.284 2.103-.656a3.028 3.028 0 0 1 .815-2.714 2.97 2.97 0 0 1 4.221 0 3.03 3.03 0 0 1 0 4.256 2.957 2.957 0 0 1-2.11.88c-.355 0-.709-.07-1.046-.196l-1.588 3.025 1.309 2.513a2.96 2.96 0 0 1 3.435.553Zm-19.184-4.251h1.981v.999h-1.98v1.998h-.991v-1.998H9.972v-1h1.98v-1.997h.991v1.998Z"
/>
</g>
</svg>
);
export const AWSGlueIcon: React.FC<IconSvgProps> = ({
size = 32,
width,
height,
className = "rounded-md",
...props
}) => (
<svg
aria-hidden="false"
aria-label="AWS Glue logo"
fill="none"
focusable="false"
height={size || height}
role="presentation"
viewBox="0 0 40 40"
width={size || width}
className={className}
{...props}
xmlns="http://www.w3.org/2000/svg"
>
<g fill="none" fillRule="evenodd">
<rect width="40" height="40" fill="#8C4FFF" />
<path
fill="#FFF"
d="M27.163 15.543c-1.839.412-4.673.495-6.663.495-1.977 0-4.785-.082-6.625-.486l5.539 8.197a.504.504 0 0 1 .086.281v1.068c.701.202 1.349.205 2 .007V24.03c0-.1.03-.2.087-.282l5.576-8.205Zm1.69-.771.03.02-6.383 9.392v1.276a.5.5 0 0 1-.314.464 4.382 4.382 0 0 1-1.653.329 4.909 4.909 0 0 1-1.706-.324.501.501 0 0 1-.327-.47v-1.275l-6.395-9.465A.572.572 0 0 1 12 14.41c0-1.425 4.692-1.689 7.488-1.72l.012 1.002c-3.343.036-5.421.41-6.212.7.943.309 3.533.644 7.212.644 3.45 0 5.943-.295 7.017-.587-.5-.133-1.416-.315-3.078-.519l.121-.994c4.122.503 4.44.928 4.44 1.474 0 .131-.06.25-.147.362ZM16.5 11.008h1v-1.001h-1v1.001Zm-1 .501V9.506a.5.5 0 0 1 .5-.501h2a.5.5 0 0 1 .5.5v2.004a.5.5 0 0 1-.5.501h-2a.5.5 0 0 1-.5-.5Zm5-3.506h1V7.002h-1v1.001Zm-1 .501V6.501A.5.5 0 0 1 20 6h2a.5.5 0 0 1 .5.5v2.004a.5.5 0 0 1-.5.501h-2a.5.5 0 0 1-.5-.5Zm2 4.508h1V12.01h-1v1.002Zm-.5-2.004h2a.5.5 0 0 1 .5.501v2.004a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1-.5-.5v-2.004a.5.5 0 0 1 .5-.5Zm3.424 18.325a.5.5 0 0 1-.079.536l-3.469 3.96a.5.5 0 0 1-.752 0l-3.469-3.96a.5.5 0 0 1 .376-.83h1.453v-1.002h1v1.503a.5.5 0 0 1-.5.5h-.849L20.5 32.74l2.365-2.699H22a.5.5 0 0 1-.5-.5v-1.503h1v1.002h1.469c.196 0 .374.114.455.294Z"
/>
</g>
</svg>
);
export const AmazonGuardDutyIcon: React.FC<IconSvgProps> = ({
size = 32,
width,
height,
className = "rounded-md",
...props
}) => (
<svg
aria-hidden="false"
aria-label="Amazon GuardDuty logo"
fill="none"
focusable="false"
height={size || height}
role="presentation"
viewBox="0 0 40 40"
width={size || width}
className={className}
{...props}
xmlns="http://www.w3.org/2000/svg"
>
<g fill="none" fillRule="evenodd">
<rect width="40" height="40" fill="#DD344C" />
<path
fill="#FFF"
d="M25 21.814c-.001.19-.074 4.475-4 4.818V14.594l4 1.734v5.486Zm-9-.015v-5.471l4-1.734v12.038c-3.927-.344-3.999-4.643-4-4.833Zm9.699-6.258-5-2.167a.5.5 0 0 0-.398 0l-5 2.167A.499.499 0 0 0 15 16v5.799c0 .059.062 5.867 5.5 5.867 5.437 0 5.5-5.792 5.5-5.851V16c0-.2-.118-.38-.301-.459ZM34 22.752l-1.794-.102a.479.479 0 0 0-.516.389 11.379 11.379 0 0 1-1.482 3.577.498.498 0 0 0 .09.64l1.342 1.197-3.188 3.185-1.191-1.336a.501.501 0 0 0-.64-.09 11.361 11.361 0 0 1-3.582 1.483.502.502 0 0 0-.389.517L22.753 34h-4.506l.103-1.789a.503.503 0 0 0-.389-.517 11.348 11.348 0 0 1-3.578-1.484.505.505 0 0 0-.641.089l-1.194 1.339-3.188-3.186 1.339-1.194a.497.497 0 0 0 .09-.639A11.351 11.351 0 0 1 9.31 23.04c-.054-.238-.245-.394-.517-.39L7 22.752v-4.506l1.797.103a.482.482 0 0 0 .515-.389 11.411 11.411 0 0 1 1.485-3.572.498.498 0 0 0-.09-.64L9.36 12.547l3.188-3.187 1.199 1.345c.162.181.434.22.64.09a11.405 11.405 0 0 1 3.574-1.479.5.5 0 0 0 .389-.516L18.247 7h4.506l-.103 1.802a.502.502 0 0 0 .389.516c1.268.287 2.47.784 3.571 1.48a.5.5 0 0 0 .64-.09l1.202-1.348 3.188 3.187-1.344 1.198a.499.499 0 0 0-.09.64 11.42 11.42 0 0 1 1.482 3.575.489.489 0 0 0 .516.389L34 18.246v4.506Zm.844-5.398a.516.516 0 0 0-.372-.136l-1.909.109a12.42 12.42 0 0 0-1.291-3.113L32.7 12.94a.497.497 0 0 0 .021-.726l-3.936-3.935a.497.497 0 0 0-.726.021l-1.278 1.431a12.348 12.348 0 0 0-3.109-1.288l.109-1.915A.501.501 0 0 0 23.282 6h-5.564a.5.5 0 0 0-.499.528l.109 1.913a12.376 12.376 0 0 0-3.111 1.288L12.941 8.3a.497.497 0 0 0-.358-.167.59.59 0 0 0-.368.146l-3.936 3.935a.5.5 0 0 0 .021.726l1.43 1.276a12.394 12.394 0 0 0-1.292 3.111l-1.91-.109a.544.544 0 0 0-.372.136.5.5 0 0 0-.156.363v5.565a.502.502 0 0 0 .528.499l1.906-.109a12.33 12.33 0 0 0 1.289 3.117L8.3 28.059a.497.497 0 0 0-.021.726l3.936 3.935a.497.497 0 0 0 .726-.021l1.27-1.423a12.35 12.35 0 0 0 3.116 1.293l-.108 1.902a.501.501 0 0 0 .499.529h5.564a.5.5 0 0 0 .499-.529l-.108-1.901a12.374 12.374 0 0 0 3.119-1.292l1.267 1.421a.497.497 0 0 0 .358.167.473.473 0 0 0 .368-.146l3.936-3.935a.5.5 0 0 0-.021-.726l-1.426-1.272a12.275 12.275 0 0 0 1.29-3.115l1.908.109a.502.502 0 0 0 .528-.499v-5.565a.5.5 0 0 0-.156-.363Z"
/>
</g>
</svg>
);
export const AWSIAMIcon: React.FC<IconSvgProps> = ({
size = 32,
width,
height,
className = "rounded-md",
...props
}) => (
<svg
aria-hidden="false"
aria-label="AWS IAM logo"
fill="none"
focusable="false"
height={size || height}
role="presentation"
viewBox="0 0 40 40"
width={size || width}
className={className}
{...props}
xmlns="http://www.w3.org/2000/svg"
>
<g fill="none" fillRule="evenodd">
<rect fill="#DD344C" width="40" height="40" />
<path
fill="#FFF"
d="m33.131 31.955-6.998-6.998a.501.501 0 0 1-.092-.58c.945-1.85.63-4.273-.732-5.635a4.629 4.629 0 0 0-3.855-1.33c-1.432.173-2.696 1.001-3.466 2.27a4.583 4.583 0 0 0 .103 4.868c1.354 2.06 3.977 2.781 6.094 1.681a.5.5 0 0 1 .584.091l1.194 1.194a.504.504 0 0 1 .146.39l-.089 1.258 1.38-.003h.001a.5.5 0 0 1 .353.147l.598.597a.502.502 0 0 1 .146.347l.017 1.406 1.231-.106a.502.502 0 0 1 .397.144l1.097 1.097 1.761.45.13-1.288Zm1.019-.135-.213 2.101a.5.5 0 0 1-.621.434l-2.456-.627a.499.499 0 0 1-.23-.13l-1.029-1.03-1.537.133a.499.499 0 0 1-.543-.493l-.021-1.74-.306-.306-1.709.004h-.001a.502.502 0 0 1-.499-.535l.11-1.569-.783-.783c-2.515 1.1-5.491.202-7.057-2.179a5.587 5.587 0 0 1-.122-5.937c.931-1.533 2.462-2.534 4.201-2.744a5.63 5.63 0 0 1 4.682 1.616c1.8 1.8 1.898 4.535 1.072 6.463l6.918 6.918a.5.5 0 0 1 .144.404ZM22.737 21.208a1.087 1.087 0 0 0-.774-.32c-.292 0-.567.113-.774.32a1.098 1.098 0 0 0 0 1.55 1.12 1.12 0 0 0 1.548 0 1.098 1.098 0 0 0 0-1.55Zm.707-.707a2.099 2.099 0 0 1 0 2.964 2.09 2.09 0 0 1-1.481.613 2.09 2.09 0 0 1-1.481-.613 2.099 2.099 0 0 1 0-2.964 2.097 2.097 0 0 1 2.962 0ZM9.662 13.312c0 .263.016.52.047.778a.504.504 0 0 1-.423.56C8.035 14.981 6 15.968 6 18.882c0 .107.006.213.011.315.135 2.461 2.487 3.794 4.633 3.8H15v1h-4.357c-3.063-.008-5.483-2.048-5.63-4.746A6.369 6.369 0 0 1 5 18.882c0-3.389 2.318-4.647 3.677-5.094a7.265 7.265 0 0 1-.015-.476c0-2.777 1.876-5.653 4.364-6.69 2.91-1.226 5.992-.618 8.245 1.625.591.581 1.348 1.451 1.88 2.511.566-.515 1.207-.768 1.941-.768 1.455 0 3.086 1.217 3.376 3.88 3.008.715 4.532 2.43 4.532 5.1 0 2.927-1.281 4.57-3.916 5.02l-.168-.987C30.352 22.758 32 22.08 32 18.97c0-3.018-2.34-3.843-4.099-4.2a.5.5 0 0 1-.4-.462c-.113-1.953-1.103-3.317-2.409-3.317-.664 0-1.186.302-1.691.981a.501.501 0 0 1-.872-.13c-.437-1.22-1.308-2.24-1.961-2.883-1.958-1.95-4.634-2.476-7.157-1.414-2.102.877-3.749 3.41-3.749 5.768Z"
/>
</g>
</svg>
);
export const AmazonInspectorIcon: React.FC<IconSvgProps> = ({
size = 32,
width,
height,
className = "rounded-md",
...props
}) => (
<svg
aria-hidden="false"
aria-label="Amazon Inspector logo"
fill="none"
focusable="false"
height={size || height}
role="presentation"
viewBox="0 0 40 40"
width={size || width}
className={className}
{...props}
xmlns="http://www.w3.org/2000/svg"
>
<g fill="none" fillRule="evenodd">
<rect width="40" height="40" fill="#DD344C" />
<path
fill="#FFF"
d="M17.75 15.605c-.66 0-1.196-.547-1.196-1.218 0-.672.536-1.219 1.196-1.219.66 0 1.196.547 1.196 1.22 0 .67-.537 1.217-1.196 1.217m-.888 3.797c1.1 0 1.996.912 1.996 2.034 0 1.121-.895 2.035-1.996 2.035-1.1 0-1.996-.914-1.996-2.035 0-1.122.895-2.034 1.996-2.034m.044 5.956c-3.918 0-7.106-3.25-7.106-7.243 0-3.993 3.188-7.241 7.106-7.241 3.917 0 7.106 3.248 7.106 7.241 0 1.037-.22 2.021-.607 2.914l-3.728-.553a3.016 3.016 0 0 0-1.434-1.718v-2.21a2.215 2.215 0 0 0 1.689-2.16c0-1.227-.98-2.225-2.182-2.225-1.203 0-2.182.998-2.182 2.224 0 1.052.724 1.932 1.689 2.16v1.89c-.131-.017-.26-.04-.395-.04-.783 0-1.492.315-2.025.82l-1.233-1.268c.343-.494.549-1.092.549-1.741 0-1.035-.51-1.986-1.364-2.545l-.533.846a2.02 2.02 0 0 1 .911 1.699c0 1.113-.89 2.02-1.983 2.02v1.005c.644 0 1.238-.215 1.725-.572l1.327 1.366c-.22.424-.356.898-.356 1.409 0 1.676 1.338 3.04 2.982 3.04 1.617 0 2.932-1.323 2.974-2.961l3.073.455c-1.26 2.031-3.477 3.388-6.003 3.388m0-15.489c-4.462 0-8.092 3.7-8.092 8.246 0 4.548 3.63 8.248 8.092 8.248 4.461 0 8.092-3.7 8.092-8.248 0-4.547-3.63-8.246-8.092-8.246M32.593 32.44a1.64 1.64 0 0 1-2.338.126l-6.405-5.888c.852-.72 1.59-1.57 2.194-2.517l6.426 5.898c.676.623.73 1.692.123 2.381M6.986 18.115c0-5.574 4.45-10.11 9.92-10.11 5.469 0 9.919 4.536 9.919 10.11 0 5.575-4.45 10.11-9.92 10.11s-9.919-4.535-9.919-10.11m26.145 11.198-6.576-6.036a11.213 11.213 0 0 0 1.256-5.162C27.81 11.986 22.918 7 16.906 7 10.892 7 6 11.986 6 18.115c0 6.13 4.892 11.116 10.906 11.116 2.276 0 4.391-.718 6.141-1.939l6.548 6.02a2.62 2.62 0 0 0 3.73-.199 2.737 2.737 0 0 0-.194-3.8"
/>
</g>
</svg>
);
export const AmazonMacieIcon: React.FC<IconSvgProps> = ({
size = 32,
width,
height,
className = "rounded-md",
...props
}) => (
<svg
aria-hidden="false"
aria-label="Amazon Macie logo"
fill="none"
focusable="false"
height={size || height}
role="presentation"
viewBox="0 0 40 40"
width={size || width}
className={className}
{...props}
xmlns="http://www.w3.org/2000/svg"
>
<g fill="none" fillRule="evenodd">
<rect width="40" height="40" fill="#DD344C" />
<path
fill="#FFF"
d="M28.002 20.905c-.99-.79-1.822-1.451-2.522-1.996.825-1.51 1.788-3.223 2.222-3.599 1.257.812 5.098 7.709 5.33 9.376-.922-.497-3.262-2.369-5.03-3.781m-7.55 4.089c-.288-.139-.885-1.005-1.574-2.13l.44-.726c.892-1.474 2.368-3.91 2.904-4.25.311.104 1.057.624 1.959 1.304-1.124 2.065-3.116 5.702-3.73 5.802m-3.947-.353c-.336-.407-.929-1.507-1.304-2.202-.96-1.782-1.318-2.4-1.832-2.418H13.346c-.407 0-.884.408-2.647 2.068-.675.636-1.66 1.563-2.36 2.12 1.012-2.256 3.974-7.633 4.978-8.98.458.67 1.374 2.315 2.134 3.683.912 1.64 1.646 2.937 2.249 3.949-.468.756-.926 1.466-1.196 1.78m16.791-2.111c-.916-2.129-4.267-8.28-5.607-8.28-.698 0-1.353 1.008-3.016 4.042-1.313-.985-2.04-1.423-2.454-1.438h-.025c-.734 0-1.544 1.144-3.724 4.746l-.175.288c-.692-1.177-1.413-2.474-1.974-3.48-2.188-3.936-2.494-4.389-2.971-4.407L13.34 14c-1.148 0-5.871 9.652-6.164 10.413-.143.372-.32.835.015 1.13.623.551 1.536-.21 4.185-2.704.65-.612 1.504-1.415 1.906-1.71.262.353.72 1.206 1.041 1.802 1.104 2.048 1.576 2.823 2.173 2.84.429-.028.798-.373 1.79-1.951C19.294 25.412 19.87 26 20.364 26h.007c1.111-.013 2.328-1.994 4.617-6.189.8.621 1.651 1.301 2.398 1.899 5.065 4.048 5.837 4.519 6.381 3.916.302-.337.455-.945-.472-3.096"
/>
</g>
</svg>
);
export const AWSNetworkFirewallIcon: React.FC<IconSvgProps> = ({
size = 32,
width,
height,
className = "rounded-md",
...props
}) => (
<svg
aria-hidden="false"
aria-label="AWS Network Firewall logo"
fill="none"
focusable="false"
height={size || height}
role="presentation"
viewBox="0 0 40 40"
width={size || width}
className={className}
{...props}
xmlns="http://www.w3.org/2000/svg"
>
<g fill="none" fillRule="evenodd">
<rect width="40" height="40" fill="#DD344C" />
<path
fill="#FFF"
d="M33.007 29.006h-7v-3h7v3Zm-6 4h-6v-3h6v3Zm-10.257-1.74a6.062 6.062 0 0 1-5.137-2.823c-.896-1.41-.912-3.23-.01-4.802 1.049-1.593.672-3.807.336-5.027.964.493 1.838 1.755 2.147 2.311a.486.486 0 0 0 .478.256.497.497 0 0 0 .429-.327c.967-2.655.31-4.611-.4-5.776 2.07.699 5.66 2.6 5.66 7.379 0 .136.056.266.154.361a.484.484 0 0 0 .368.138c.59-.025 1.651-.473 2.16-1.886.044.133.072.279.072.436v3.5h-5.5a.5.5 0 0 0-.5.5v4a.5.5 0 0 0 .5.5h2.5v.384c-.855.561-1.999.876-3.257.876Zm-7.691-8.609c-.976-.646-2.139-1.874-2.139-4.059 0-2.988 2.079-3.968 3.318-4.287a.498.498 0 0 0 .375-.514l-.049-.821c0-2.33 1.735-6.026 6.391-6.026 1.896 0 3.399.545 4.466 1.619.763.766 1.356 1.745 1.767 2.909a.5.5 0 0 0 .874.131c.62-.842 1.69-1.194 2.659-.875 1.098.36 1.726 1.437 1.723 2.936a.5.5 0 0 0 .411.563c2.794.481 4.152 1.827 4.152 4.113 0 1.322-.341 2.333-1.016 3.084a.493.493 0 0 0-.484-.424h-7.559c-.206-.937-.921-1.499-.956-1.526a.496.496 0 0 0-.502-.062.496.496 0 0 0-.298.407c-.124 1.181-.589 1.708-.962 1.944-.465-6.83-7.828-8.036-7.906-8.048a.5.5 0 0 0-.388.884c.093.076 2.086 1.74 1.409 4.904-.67-.891-1.824-2.098-3.16-2.098a.5.5 0 0 0-.464.686c.013.032 1.201 3.068.093 4.909h-.564c-.434 0-.847-.121-1.191-.349Zm16.448 2.349h-1.5v-3h7v3h-5.5Zm-.5 4h-7v-3h7v3Zm8.5-4h-1.5v-2.241c1.324-.963 2-2.446 2-4.419 0-1.868-.806-4.221-4.57-5.019-.105-1.766-.987-3.077-2.404-3.543a3.354 3.354 0 0 0-3.197.618 8.365 8.365 0 0 0-1.706-2.539c-1.262-1.269-3.003-1.913-5.175-1.913-5.098 0-7.391 4.109-7.39 7.056l.027.453c-1.352.444-3.672 1.708-3.672 5.139 0 2.768 1.62 4.254 2.587 4.893a3.145 3.145 0 0 0 1.743.515h.075c-.641 1.656-.496 3.495.445 4.974a7.059 7.059 0 0 0 5.98 3.286c1.203 0 2.325-.26 3.257-.726v1.966a.5.5 0 0 0 .5.5h7a.5.5 0 0 0 .5-.5v-3.5h5.5a.5.5 0 0 0 .5-.5v-4a.5.5 0 0 0-.5-.5Z"
/>
</g>
</svg>
);
export const AWSOrganizationsIcon: React.FC<IconSvgProps> = ({
size = 32,
width,
height,
className = "rounded-md",
...props
}) => (
<svg
aria-hidden="false"
aria-label="AWS Organizations logo"
fill="none"
focusable="false"
height={size || height}
role="presentation"
viewBox="0 0 40 40"
width={size || width}
className={className}
{...props}
xmlns="http://www.w3.org/2000/svg"
>
<g fill="none" fillRule="evenodd">
<rect width="40" height="40" fill="#E7157B" />
<path
fill="#FFF"
d="M28.986 32.001h3.016v-3.039h-3.016v3.039Zm-10.025 0h3.077v-3.039h-3.077v3.039Zm-9.963 0h3.037v-3.039H8.998v3.039Zm23.503-4.038h-1.498v-2.497a.5.5 0 0 0-.499-.5H21.02V22.97h-.998v1.997h-9.484c-.277 0-.5.224-.5.5v2.497H8.5c-.276 0-.499.224-.499.5V32.5c0 .275.223.499.5.499h4.034a.5.5 0 0 0 .5-.5v-4.037a.5.5 0 0 0-.5-.5h-1.497v-1.998h8.985v1.998h-1.56a.5.5 0 0 0-.5.5V32.5a.5.5 0 0 0 .5.499h4.076c.276 0 .499-.224.499-.5v-4.037c0-.276-.223-.5-.5-.5H21.02v-1.998h8.985v1.998h-1.518a.5.5 0 0 0-.5.5V32.5a.5.5 0 0 0 .5.499H32.5a.5.5 0 0 0 .499-.5v-4.037a.5.5 0 0 0-.5-.5ZM20.537 9.04l4.788 1.983-4.803 2.397-4.775-2.396 4.79-1.984Zm.497 11.6-.013-6.353 5.007-2.499v5.882l-4.994 2.97Zm-5.988-8.85 4.977 2.497.013 6.351-4.99-2.968v-5.88Zm5.235 10.157a.51.51 0 0 0 .512 0l5.99-3.563a.501.501 0 0 0 .243-.43v-6.973a.5.5 0 0 0-.307-.461l-5.99-2.482a.497.497 0 0 0-.383 0l-5.99 2.482a.5.5 0 0 0-.308.461v6.974c0 .176.093.34.243.43l5.99 3.562Z"
/>
</g>
</svg>
);
export const AmazonRDSIcon: React.FC<IconSvgProps> = ({
size = 32,
width,
height,
className = "rounded-md",
...props
}) => (
<svg
aria-hidden="false"
aria-label="Amazon RDS logo"
fill="none"
focusable="false"
height={size || height}
role="presentation"
viewBox="0 0 40 40"
width={size || width}
className={className}
{...props}
xmlns="http://www.w3.org/2000/svg"
>
<g fill="none" fillRule="evenodd">
<rect width="40" height="40" fill="#C925D1" />
<path
fill="#FFF"
d="M11.854 28.854 7.708 33H10.5v1h-4a.5.5 0 0 1-.5-.5v-4h1v2.793l4.147-4.146.707.707ZM33 29.5h1v4c0 .277-.223.5-.5.5h-4v-1h2.793l-4.146-4.146.707-.707L33 32.293V29.5Zm1-23v4h-1V7.708l-4.146 4.146-.707-.707L32.293 7H29.5V6h4a.5.5 0 0 1 .5.5Zm-27 4H6v-4a.5.5 0 0 1 .5-.5h4v1H7.527l4.319 4.14-.691.721L7 7.88v2.62Zm25.726 8.9c0-1.558-1.806-3.1-4.83-4.123l.32-.947c3.502 1.185 5.51 3.033 5.51 5.07 0 2.037-2.008 3.886-5.51 5.071l-.32-.948c3.024-1.023 4.83-2.565 4.83-4.123Zm-24.989 0c0 1.492 1.69 2.993 4.521 4.014l-.34.94c-3.293-1.187-5.181-2.993-5.181-4.954 0-1.961 1.888-3.767 5.181-4.955l.34.941c-2.831 1.021-4.521 2.522-4.521 4.014Zm12.286-3.681c-3.136 0-4.891-.799-4.917-1.125.026-.326 1.781-1.125 4.917-1.125 3.133 0 4.89.798 4.917 1.125-.027.326-1.784 1.125-4.917 1.125Zm0 3.555c-3.182 0-4.917-.926-4.917-1.402v-2.03c1.135.595 3.069.877 4.917.877 1.848 0 3.782-.282 4.917-.877v2.03c0 .476-1.735 1.402-4.917 1.402Zm0 3.737c-3.182 0-4.917-.927-4.917-1.403v-2.343c1.119.665 3.023 1.009 4.917 1.009 1.894 0 3.798-.344 4.917-1.009v2.343c0 .476-1.735 1.403-4.917 1.403Zm0 3.279c-3.182 0-4.917-.915-4.917-1.385v-1.904c1.119.666 3.023 1.01 4.917 1.01 1.894 0 3.798-.344 4.917-1.01v1.904c0 .47-1.735 1.385-4.917 1.385Zm0-13.821c-2.85 0-5.917.665-5.917 2.125v10.311c0 1.566 2.977 2.385 5.917 2.385s5.917-.819 5.917-2.385V14.594c0-1.46-3.067-2.125-5.917-2.125Z"
/>
</g>
</svg>
);
export const AWSResourceExplorerIcon: React.FC<IconSvgProps> = ({
size = 32,
width,
height,
className = "rounded-md",
...props
}) => (
<svg
aria-hidden="false"
aria-label="AWS Resource Explorer logo"
fill="none"
focusable="false"
height={size || height}
role="presentation"
viewBox="0 0 40 40"
width={size || width}
className={className}
{...props}
xmlns="http://www.w3.org/2000/svg"
>
<g fill="none" fillRule="evenodd">
<rect width="40" height="40" fill="#E7157B" />
<path
fill="#FFF"
d="m32.746 30.427-5.431-5.48a7.535 7.535 0 0 1-2.217 2.277l5.346 5.515c.214.213.493.3.833.247.37-.057.76-.273 1.097-.61.576-.577.889-1.431.372-1.95ZM26.396 30l-2.201-2.27a7.424 7.424 0 0 1-3.212.735c-.334 0-.66-.03-.983-.073v4.35L26.396 30ZM7 27.18l12 5.538V28.19c-3.163-.872-5.496-3.767-5.496-7.204 0-1.524.461-2.942 1.247-4.125L7 13.283V27.18Zm.693-14.68 7.687 3.547a7.459 7.459 0 0 1 5.603-2.542c1.751 0 3.359.61 4.635 1.62l5.688-2.624L19.5 7.05l-11.807 5.45Zm19.77 8.485a6.487 6.487 0 0 0-6.48-6.48 6.487 6.487 0 0 0-6.479 6.48 6.486 6.486 0 0 0 6.479 6.48 6.487 6.487 0 0 0 6.48-6.48Zm3.833 6.558.704-.351v-13.91l-5.585 2.578a7.424 7.424 0 0 1 1.395 8.166l3.486 3.517Zm2.159 2.178c.864.866.707 2.28-.374 3.362-.486.486-1.073.803-1.651.892a2.299 2.299 0 0 1-.336.026c-.522 0-.996-.194-1.363-.56l-2.59-2.672-7.444 3.19a.5.5 0 0 1-.407-.005l-13-6A.5.5 0 0 1 6 27.5v-15a.5.5 0 0 1 .29-.454l13-6a.497.497 0 0 1 .42 0l13 6a.5.5 0 0 1 .29.455v15a.503.503 0 0 1-.276.447l-.687.343 1.418 1.43ZM19 23h1v-2h-1v2Zm0 3h1v-2h-1v2Zm3.415-9.395.42.907 1.267-.585-.419-.909-1.268.587Zm-1.353.622.42.909L20 18.82V20h-1v-1.18l-1.967-.907.42-.909 2.047.945 1.562-.722Z"
/>
</g>
</svg>
);
export const AmazonRoute53Icon: React.FC<IconSvgProps> = ({
size = 32,
width,
height,
className = "rounded-md",
...props
}) => (
<svg
aria-hidden="false"
aria-label="Amazon Route 53 logo"
fill="none"
focusable="false"
height={size || height}
role="presentation"
viewBox="0 0 40 40"
width={size || width}
className={className}
{...props}
xmlns="http://www.w3.org/2000/svg"
>
<g fill="none" fillRule="evenodd">
<path fill="#8C4FFF" d="M0 0h40v40H0z" />
<path
fill="#FFF"
d="M24.024 20.314c.343.317.514.74.514 1.272 0 .58-.208 1.042-.624 1.388-.416.345-.977.518-1.68.518a4.112 4.112 0 0 1-1.647-.354v-.71c.643.214 1.193.32 1.648.32.448 0 .794-.101 1.036-.302.242-.2.363-.488.363-.86 0-.715-.452-1.072-1.355-1.072a8.13 8.13 0 0 0-.842.044v-.585l1.86-2.03h-2.622v-.726h3.607v.7l-1.826 1.932a.447.447 0 0 1 .089-.009h.088c.584 0 1.048.159 1.391.474m-5.147-.154c.359.336.54.8.54 1.39 0 .58-.21 1.049-.63 1.405-.42.358-.972.537-1.656.537a4.09 4.09 0 0 1-1.692-.354v-.71c.655.214 1.216.32 1.683.32.448 0 .793-.102 1.032-.306.239-.204.36-.498.36-.882 0-.42-.113-.725-.338-.913-.224-.189-.59-.284-1.099-.284-.366 0-.823.03-1.373.09v-.587l.169-2.65h3.233v.727h-2.489l-.116 1.8c.326-.06.618-.09.878-.09.638 0 1.138.17 1.498.507m6.653 6.462c-2.242.403-4.207 1.313-5.53 2.059-1.323-.746-3.288-1.656-5.53-2.06-.627-.112-3.755-.764-3.755-2.558 0-.836.301-1.392.877-2.385.696-1.2 1.562-2.693 1.562-4.848 0-1.528-.399-2.996-1.187-4.368l.25-.306c2.61 1.293 5.533 1.118 7.783-.304 2.25 1.421 5.173 1.597 7.783.304l.25.306c-.788 1.372-1.188 2.84-1.188 4.368 0 2.155.866 3.648 1.563 4.848.576.993.876 1.549.876 2.385 0 1.794-3.127 2.446-3.754 2.559m2.312-9.792c0-1.454.41-2.849 1.22-4.146a.502.502 0 0 0-.036-.58l-.726-.893a.497.497 0 0 0-.63-.123c-1.134.627-2.319.945-3.518.945-1.448 0-2.714-.39-3.868-1.19a.502.502 0 0 0-.568 0c-1.155.8-2.42 1.19-3.868 1.19-1.2 0-2.384-.318-3.52-.945a.495.495 0 0 0-.628.123l-.726.893a.5.5 0 0 0-.036.58c.809 1.297 1.219 2.692 1.219 4.146 0 1.887-.791 3.25-1.428 4.347-.626 1.08-1.011 1.798-1.011 2.886 0 2.524 3.502 3.348 4.575 3.54 2.246.404 4.207 1.356 5.456 2.084a.493.493 0 0 0 .502 0c1.25-.728 3.21-1.68 5.455-2.083 1.074-.193 4.575-1.017 4.575-3.541 0-1.088-.384-1.806-1.01-2.886-.637-1.096-1.429-2.46-1.429-4.347M26.01 29.297c-2.822.507-5.19 2.016-6.01 2.588-.82-.572-3.188-2.08-6.01-2.588-1.406-.252-5.992-1.376-5.992-5.234 0-1.606.631-2.696 1.244-3.75.587-1.013 1.195-2.06 1.195-3.483 0-2.217-1.265-3.791-1.913-4.452.676-.821 2.314-2.818 3.147-3.89 1.263 1.18 2.728 1.824 4.177 1.824 1.61 0 2.942-.65 4.152-2.036 1.21 1.387 2.543 2.036 4.152 2.036 1.449 0 2.914-.643 4.176-1.824.833 1.072 2.472 3.069 3.148 3.89-.649.66-1.913 2.235-1.913 4.452 0 1.423.608 2.47 1.195 3.483.612 1.054 1.244 2.144 1.244 3.75 0 3.858-4.587 4.982-5.992 5.234m5.611-9.485c-.57-.98-1.06-1.828-1.06-2.982 0-2.43 1.892-4.004 1.912-4.02a.503.503 0 0 0 .07-.705c-.03-.037-3.066-3.716-3.759-4.668a.497.497 0 0 0-.768-.047c-1.16 1.24-2.532 1.924-3.864 1.924-1.471 0-2.63-.656-3.756-2.128a.516.516 0 0 0-.793 0c-1.126 1.472-2.285 2.128-3.755 2.128-1.332 0-2.704-.683-3.864-1.924a.497.497 0 0 0-.768.047c-.693.952-3.728 4.63-3.76 4.668a.502.502 0 0 0 .072.705c.019.016 1.911 1.575 1.911 4.02 0 1.154-.49 2.001-1.061 2.982C7.732 20.926 7 22.188 7 24.062c0 3.127 2.546 5.451 6.813 6.217 3.19.574 5.857 2.597 5.883 2.617a.499.499 0 0 0 .608 0c.026-.02 2.694-2.043 5.883-2.617C30.453 29.513 33 27.19 33 24.063c0-1.875-.732-3.137-1.379-4.25"
/>
</g>
</svg>
);
export const AmazonS3Icon: React.FC<IconSvgProps> = ({
size = 32,
width,
height,
className = "rounded-md",
...props
}) => (
<svg
aria-hidden="false"
aria-label="Amazon S3 logo"
fill="none"
focusable="false"
height={size || height}
role="presentation"
viewBox="0 0 32 32"
width={size || width}
className={className}
{...props}
xmlns="http://www.w3.org/2000/svg"
>
<g clipPath="url(#a)">
<rect width="32" height="32" fill="#7AA116" />
<path
fill="#fff"
fillRule="evenodd"
d="m24.426 17.113.157-1.167c1.674.959 1.708 1.355 1.708 1.366-.004.002-.284.226-1.865-.2Zm-.775-.23c-2.273-.718-5.328-2.058-7.072-2.88 0-.005.003-.01.003-.016a.964.964 0 0 0-.963-.962.963.963 0 0 0 0 1.925.947.947 0 0 0 .604-.23c1.015.475 4.637 2.14 7.32 2.97l-.944 6.99c0 .693-2.939 1.72-6.999 1.72s-6.999-1.027-7.003-1.773L6.627 10.05C8.367 11.324 12.059 12 15.6 12c3.541 0 7.235-.676 8.975-1.95l-.924 6.832ZM6.403 8.39c.023-.704 3.28-2.79 9.197-2.79 5.916 0 9.176 2.086 9.2 2.79v.091c-.152 1.303-3.89 2.718-9.2 2.718-5.31 0-9.033-1.415-9.185-2.717l-.012-.092Zm19.197.01c0-1.469-3.895-3.601-10-3.601s-10 2.132-10 3.6v.054L7.801 24.68c0 1.637 4.018 2.52 7.8 2.52 3.78 0 7.798-.883 7.794-2.466l.922-6.822c.657.171 1.188.262 1.597.262.474 0 .795-.115.989-.346a.78.78 0 0 0 .173-.665c-.11-.59-.838-1.228-2.364-2.067l-.017.02.905-6.662V8.4Z"
clipRule="evenodd"
/>
</g>
<defs>
<clipPath id="a">
<rect width="32" height="32" fill="#fff" rx="4" />
</clipPath>
</defs>
</svg>
);
export const AWSSecurityHubIcon: React.FC<IconSvgProps> = ({
size = 32,
width,
height,
className = "rounded-md",
...props
}) => (
<svg
aria-hidden="false"
aria-label="AWS Security Hub logo"
fill="none"
focusable="false"
height={size || height}
role="presentation"
viewBox="0 0 40 40"
width={size || width}
className={className}
{...props}
xmlns="http://www.w3.org/2000/svg"
>
<g fill="none" fillRule="evenodd">
<path fill="#DD344C" d="M0 0h40v40H0z" />
<path
fill="#FFF"
d="M22.034 20.429c0 .086-.034 1.908-1.507 2.258v-5.498l1.507.653v2.587Zm-4.014-.008v-2.579l1.507-.653v5.5c-1.435-.335-1.506-2.052-1.507-2.268Zm4.713-3.367-2.507-1.085a.5.5 0 0 0-.398 0l-2.507 1.085a.501.501 0 0 0-.301.459v2.908c0 1.155.629 3.339 3.007 3.339s3.007-2.179 3.007-3.331v-2.916a.501.501 0 0 0-.301-.459Zm6.657 9.792c-.882 0-1.6-.718-1.6-1.601s.718-1.601 1.6-1.601c.883 0 1.601.718 1.601 1.601s-.718 1.601-1.601 1.601Zm-9.315-2.101a4.988 4.988 0 0 1-4.983-4.983 4.988 4.988 0 0 1 4.983-4.984 4.99 4.99 0 0 1 4.984 4.984 4.989 4.989 0 0 1-4.984 4.983Zm1.553 6.058c0 .882-.718 1.601-1.601 1.601s-1.6-.719-1.6-1.601a1.602 1.602 0 0 1 3.201 0ZM18.427 8.601c0-.882.717-1.601 1.6-1.601.883 0 1.601.719 1.601 1.601a1.602 1.602 0 0 1-3.201 0ZM10.6 26.24c-.882 0-1.6-.718-1.6-1.601 0-.882.718-1.601 1.6-1.601.883 0 1.601.719 1.601 1.601 0 .883-.718 1.601-1.601 1.601Zm.408-10.629a1.603 1.603 0 0 1-1.601-1.601c0-.883.718-1.601 1.601-1.601.882 0 1.601.718 1.601 1.601s-.719 1.601-1.601 1.601Zm18.446-3.457a1.602 1.602 0 0 1 0 3.201 1.602 1.602 0 0 1 0-3.201Zm-.064 10.49c-.718 0-1.37.294-1.841.767l-1.969-1.305c.308-.72.479-1.512.479-2.344a5.942 5.942 0 0 0-.806-2.988l2.253-1.315c.477.545 1.17.896 1.948.896a2.604 2.604 0 0 0 2.601-2.601c0-1.434-1.167-2.6-2.601-2.6a2.603 2.603 0 0 0-2.601 2.6c0 .296.061.575.152.841l-2.322 1.354a5.97 5.97 0 0 0-4.156-2.148v-2.65a2.604 2.604 0 0 0 2.101-2.55A2.604 2.604 0 0 0 20.027 6a2.604 2.604 0 0 0-2.6 2.601c0 1.263.905 2.316 2.1 2.55v2.654a5.978 5.978 0 0 0-4.26 2.41l-1.857-1.211c.127-.307.199-.642.199-.994a2.604 2.604 0 0 0-2.601-2.601 2.604 2.604 0 0 0-2.601 2.601 2.604 2.604 0 0 0 2.601 2.601 2.59 2.59 0 0 0 1.847-.774l1.886 1.229a5.933 5.933 0 0 0-.649 2.696c0 .739.14 1.444.386 2.098l-1.917 1.09a2.584 2.584 0 0 0-1.961-.912A2.604 2.604 0 0 0 8 24.639a2.603 2.603 0 0 0 2.6 2.601 2.604 2.604 0 0 0 2.601-2.601c0-.288-.059-.56-.145-.819l1.853-1.055a5.984 5.984 0 0 0 4.618 2.952v2.536a2.603 2.603 0 0 0-2.1 2.55 2.604 2.604 0 0 0 2.6 2.601 2.604 2.604 0 0 0 2.601-2.601 2.604 2.604 0 0 0-2.101-2.55v-2.531a5.979 5.979 0 0 0 4.577-2.73l1.888 1.25a2.604 2.604 0 0 0 2.398 3.604 2.604 2.604 0 0 0 2.601-2.601 2.604 2.604 0 0 0-2.601-2.601Z"
/>
</g>
</svg>
);
export const AmazonSNSIcon: React.FC<IconSvgProps> = ({
size = 32,
width,
height,
className = "rounded-md",
...props
}) => (
<svg
aria-hidden="false"
aria-label="Amazon Simple Notification Service logo"
fill="none"
focusable="false"
height={size || height}
role="presentation"
viewBox="0 0 40 40"
width={size || width}
className={className}
{...props}
xmlns="http://www.w3.org/2000/svg"
>
<g fill="none" fillRule="evenodd">
<path fill="#E7157B" d="M0 0h40v40H0z" />
<path
fill="#FFF"
d="M7.01 20.078a1.1 1.1 0 0 1 1.105-1.093 1.1 1.1 0 0 1 1.104 1.093 1.1 1.1 0 0 1-1.104 1.093 1.1 1.1 0 0 1-1.105-1.093ZM20.776 33C14.813 33 9.645 28.375 8.47 22.136a2.1 2.1 0 0 0 1.69-1.558h2.949v-1h-2.95a2.104 2.104 0 0 0-1.653-1.554C9.72 12.252 14.838 8 20.776 8c2.933 0 5.354.643 7.194 1.911l.575-.821C26.534 7.703 23.92 7 20.776 7c-6.51 0-12.104 4.726-13.308 11.096C6.62 18.368 6 19.149 6 20.078c0 .916.602 1.688 1.431 1.971C8.591 28.894 14.24 34 20.776 34c3.285 0 6.788-1.667 8.786-3.094l-.59-.811C26.947 31.541 23.627 33 20.777 33ZM14.79 18.242c1.111.274 2.523.321 3.343.321.833 0 2.271-.047 3.402-.32l-2.401 5.014a.507.507 0 0 0-.048.215v2.324l-1.957.915v-3.239a.514.514 0 0 0-.044-.206l-2.295-5.024Zm3.343-1.757c2.314 0 3.554.311 3.951.52-.417.234-1.745.558-3.95.558-2.184 0-3.483-.327-3.873-.558.37-.206 1.582-.52 3.872-.52Zm-1.78 11.438a.511.511 0 0 0 .486.03l2.968-1.388a.5.5 0 0 0 .288-.452v-2.529l2.909-6.074a.806.806 0 0 0 .189-.51c0-1.252-2.751-1.515-5.06-1.515-2.266 0-4.969.263-4.969 1.515 0 .19.067.355.18.502l2.775 6.077V27.5c0 .172.088.331.235.423ZM30.877 27a1.1 1.1 0 0 1 1.104 1.093 1.1 1.1 0 0 1-1.104 1.093 1.1 1.1 0 0 1-1.104-1.093A1.1 1.1 0 0 1 30.876 27Zm0-16.03a1.1 1.1 0 0 1 1.104 1.093 1.1 1.1 0 0 1-1.104 1.093 1.1 1.1 0 0 1-1.104-1.093 1.1 1.1 0 0 1 1.104-1.093Zm1.01 8.015a1.1 1.1 0 0 1 1.104 1.093 1.1 1.1 0 0 1-1.104 1.093 1.1 1.1 0 0 1-1.104-1.093 1.1 1.1 0 0 1 1.104-1.093Zm-4.607 1.593h2.561a2.108 2.108 0 0 0 2.046 1.593A2.106 2.106 0 0 0 34 20.078a2.106 2.106 0 0 0-2.114-2.093c-.992 0-1.818.681-2.046 1.593H27.28v-7.015h1.551a2.108 2.108 0 0 0 2.046 1.593 2.106 2.106 0 0 0 2.114-2.093 2.106 2.106 0 0 0-2.114-2.093c-.991 0-1.818.681-2.046 1.593h-2.056a.502.502 0 0 0-.505.5v7.515h-3.061v1h3.061v7.515c0 .277.226.5.505.5h2.056a2.108 2.108 0 0 0 2.046 1.593 2.106 2.106 0 0 0 2.114-2.093A2.106 2.106 0 0 0 30.876 26c-.991 0-1.818.681-2.046 1.593H27.28v-7.015Z"
/>
</g>
</svg>
);
export const AWSSystemsManagerIncidentManagerIcon: React.FC<IconSvgProps> = ({
size = 32,
width,
height,
className = "rounded-md",
...props
}) => (
<svg
aria-hidden="false"
aria-label="AWS Systems Manager Incident Manager logo"
fill="none"
focusable="false"
height={size || height}
role="presentation"
viewBox="0 0 40 40"
width={size || width}
className={className}
{...props}
xmlns="http://www.w3.org/2000/svg"
>
<g fill="none" fillRule="evenodd">
<path fill="#E7157B" d="M0 0h40v40H0z" />
<path
fill="#FFF"
d="M24 27h5v1h-5v1h-1v-1h-2v-1h2v-1h1v1Zm4-5.001h1v1h-1v1h-1v-1h-6v-1h6v-1h1v1Zm-17.643 1H15v1h-4.643c-.531 0-1.081 0-1.647-.353C7.698 23.019 6 21.55 6 18.764c0-3.376 2.33-4.615 3.689-5.05l-.026-.437c-.001-2.783 1.875-5.633 4.363-6.661 2.908-1.213 5.988-.611 8.243 1.606a8.21 8.21 0 0 1 1.718 2.498 3.409 3.409 0 0 1 3.245-.602c1.398.466 2.27 1.744 2.38 3.455 1.631.274 4.388.86 4.388 4.425h-1c0-2.64-1.691-3.132-3.734-3.47l-.238-.038a.499.499 0 0 1-.402-.608c-.002-1.404-.625-2.453-1.71-2.816a2.422 2.422 0 0 0-2.703.853.498.498 0 0 1-.869-.13c-.416-1.146-1.013-2.105-1.776-2.854-1.959-1.926-4.634-2.447-7.158-1.396-2.102.869-3.748 3.375-3.748 5.708l.05.808a.501.501 0 0 1-.378.516C9.089 14.88 7 15.837 7 18.764c0 2.186 1.217 3.4 2.238 4.033.309.192.626.202 1.119.202ZM33 26.159l-.882-.05a.515.515 0 0 0-.516.39 6.684 6.684 0 0 1-.873 2.11.5.5 0 0 0 .089.64l.66.587-1.642 1.64-.585-.655a.501.501 0 0 0-.641-.091 6.671 6.671 0 0 1-2.112.876.5.5 0 0 0-.389.517l.051.877h-2.32l.051-.878a.502.502 0 0 0-.389-.517 6.686 6.686 0 0 1-2.112-.876.502.502 0 0 0-.64.09l-.586.658-1.642-1.64.659-.588a.5.5 0 0 0 .09-.64 6.683 6.683 0 0 1-.875-2.11c-.053-.239-.245-.396-.515-.39l-.881.05v-2.32l.884.05c.271.006.462-.151.515-.39a6.72 6.72 0 0 1 .875-2.106.5.5 0 0 0-.089-.64l-.663-.59 1.642-1.642.59.662a.503.503 0 0 0 .64.09 6.702 6.702 0 0 1 2.108-.873.502.502 0 0 0 .389-.517l-.051-.885h2.32l-.051.886a.502.502 0 0 0 .389.517 6.713 6.713 0 0 1 2.107.873.5.5 0 0 0 .64-.09l.591-.663 1.642 1.641-.662.59a.501.501 0 0 0-.089.64 6.68 6.68 0 0 1 .875 2.108.479.479 0 0 0 .515.389l.883-.05v2.32Zm.472-3.348-1 .057a7.68 7.68 0 0 0-.682-1.645l.748-.668a.497.497 0 0 0 .021-.726l-2.39-2.39a.5.5 0 0 0-.727.021l-.67.751a7.617 7.617 0 0 0-1.641-.68l.057-1.004a.5.5 0 0 0-.499-.53h-3.378a.5.5 0 0 0-.499.53l.057 1.002a7.697 7.697 0 0 0-1.642.68l-.669-.749a.5.5 0 0 0-.359-.167.532.532 0 0 0-.368.146l-2.39 2.39a.5.5 0 0 0 .021.726l.749.67a7.71 7.71 0 0 0-.681 1.643l-1.002-.057a.529.529 0 0 0-.372.135.505.505 0 0 0-.156.364v3.378a.5.5 0 0 0 .528.5l.998-.058c.163.576.392 1.127.681 1.647l-.745.665a.497.497 0 0 0-.021.727l2.39 2.39a.527.527 0 0 0 .368.146.5.5 0 0 0 .359-.167l.664-.746a7.72 7.72 0 0 0 1.647.684l-.057.995a.502.502 0 0 0 .499.529h3.378a.5.5 0 0 0 .499-.53l-.057-.993a7.716 7.716 0 0 0 1.648-.684l.663.745a.5.5 0 0 0 .727.02l2.39-2.39a.5.5 0 0 0-.021-.726l-.747-.666a7.686 7.686 0 0 0 .682-1.646l.999.057a.5.5 0 0 0 .528-.499V23.31a.505.505 0 0 0-.156-.364.529.529 0 0 0-.372-.135Z"
/>
</g>
</svg>
);
export const AWSTrustedAdvisorIcon: React.FC<IconSvgProps> = ({
size = 32,
width,
height,
className = "rounded-md",
...props
}) => (
<svg
aria-hidden="false"
aria-label="AWS Trusted Advisor logo"
fill="none"
focusable="false"
height={size || height}
role="presentation"
viewBox="0 0 64 64"
width={size || width}
className={className}
{...props}
xmlns="http://www.w3.org/2000/svg"
>
<g fill="none" fillRule="evenodd">
<path fill="#E7157B" d="M0 0h64v64H0z" />
<path
fill="#FFF"
d="m26 18.662 5-2.115v17.396c-4.854-.555-5-5.125-5-5.175V18.662Zm12 10.106c0 .05-.146 4.62-5 5.175V16.547l5 2.115v10.106ZM32 36c6.563 0 8-5.219 8-7.23v-10.77a.998.998 0 0 0-.61-.92l-7-2.962a1.005 1.005 0 0 0-.78 0l-7 2.962A.998.998 0 0 0 24 18v10.77c0 2.011 1.438 7.23 8 7.23ZM22 15.773l10-3.707 10 3.707v12.188C42 31.711 40.701 38 32 38c-9.518 0-10-8.362-10-10.038V15.773ZM32 40c11.135 0 12-9.214 12-12.038V15.078a1 1 0 0 0-.652-.938l-11-4.078a1.004 1.004 0 0 0-.696 0l-11 4.078a1 1 0 0 0-.652.938v12.883C20 29.193 20.337 40 32 40Zm17 6c-1.103 0-2-.897-2-2s.897-2 2-2 2 .897 2 2-.897 2-2 2Zm-15 4c0 1.103-.897 2-2 2s-2-.897-2-2 .897-2 2-2 2 .897 2 2Zm-19-4c-1.103 0-2-.897-2-2s.897-2 2-2 2 .897 2 2-.897 2-2 2Zm34-6c-.997 0-1.899.38-2.601.986l-1.672-1.674c2.5-2.055 4.626-4.714 6.171-7.872l-1.796-.88C45.646 37.616 39.093 42 32 42c-7.359 0-14.804-4.704-18.102-11.44l-1.796.88c1.488 3.04 3.69 5.664 6.32 7.725l-1.402 1.402A3.966 3.966 0 0 0 15 40c-2.206 0-4 1.795-4 4 0 2.206 1.794 4 4 4s4-1.794 4-4c0-.739-.216-1.424-.566-2.019l1.633-1.633c3.269 2.13 7.055 3.422 10.933 3.614v2.18c-1.721.448-3 2-3 3.858 0 2.206 1.794 4 4 4s4-1.794 4-4c0-1.858-1.279-3.41-3-3.858v-2.18c3.625-.189 7.09-1.392 10.112-3.435l2.135 2.135c-.15.42-.247.867-.247 1.338 0 2.206 1.794 4 4 4s4-1.794 4-4c0-2.205-1.794-4-4-4Z"
/>
</g>
</svg>
);
export const AmazonVPCIcon: React.FC<IconSvgProps> = ({
size = 32,
width,
height,
className = "rounded-md",
...props
}) => (
<svg
aria-hidden="false"
aria-label="Amazon VPC logo"
fill="none"
focusable="false"
height={size || height}
role="presentation"
viewBox="0 0 40 40"
width={size || width}
className={className}
{...props}
xmlns="http://www.w3.org/2000/svg"
>
<g fill="none" fillRule="evenodd">
<path fill="#8C4FFF" d="M0 0h40v40H0z" />
<path
fill="#FFF"
d="m30.97 21.03-2.943-1.264v10.49c2.545-.353 2.932-2.969 2.948-3.088l-.004-6.139Zm-3.943 9.24V19.76l-3.035 1.272v6.201c.013.054.445 2.767 3.035 3.036Zm.306-11.723a.5.5 0 0 1 .39.001l3.945 1.692a.5.5 0 0 1 .303.46v6.533c-.183 1.447-1.357 4.063-4.527 4.063-3.075 0-4.254-2.612-4.448-3.994l-.004-6.603c0-.2.12-.383.306-.46l4.035-1.692Zm5.65.637-5.461-2.406-5.53 2.327v7.128c-.001.042-.054 2.868 1.657 4.623.917.941 2.194 1.42 3.795 1.42 1.602 0 2.887-.48 3.819-1.423 1.735-1.758 1.72-4.585 1.72-4.613v-7.056ZM31.98 31.55c-1.127 1.144-2.654 1.725-4.536 1.725-1.884 0-3.404-.58-4.517-1.727-2.005-2.063-1.94-5.197-1.935-5.329v-7.446a.5.5 0 0 1 .305-.46l6.035-2.54a.5.5 0 0 1 .396.003l5.956 2.625c.182.08.299.26.299.457v7.375c.002.125.027 3.255-2.003 5.317Zm-20.988-7.314h8v1h-8c-2.811 0-4.811-2.016-4.98-5.016A3.745 3.745 0 0 1 6 19.841c0-2.509 1.318-4.223 3.817-4.979a8.352 8.352 0 0 1-.032-.647c0-2.916 1.577-5.445 4.017-6.445 3.298-1.348 7.042-.894 9.106 1.104a8.232 8.232 0 0 1 1.76 2.485c.609-.45 1.4-.66 2.349-.614 1.78.09 3.149 1.713 3.433 4.007 1.498.124 2.485.883 3.01 2.308l-.937.346c-.428-1.157-1.208-1.673-2.531-1.673a.5.5 0 0 1-.5-.47c-.138-2.338-1.406-3.463-2.526-3.519-.945-.053-1.645.22-2.08.791a.504.504 0 0 1-.472.193.503.503 0 0 1-.395-.323 7.378 7.378 0 0 0-1.807-2.814c-1.788-1.73-5.09-2.099-8.031-.895-2.063.845-3.396 3.011-3.396 5.52 0 .272.035.695.066.958a.498.498 0 0 1-.376.544C8.169 16.29 7 17.678 7 19.841c0 .101-.001.201.009.302.14 2.495 1.703 4.093 3.983 4.093Z"
/>
</g>
</svg>
);
export const getAWSIcon = (serviceAlias: string) => {
switch (serviceAlias) {
case "Amazon EC2":
return <AmazonEC2Icon />;
case "Amazon EMR":
return <AmazonEMRIcon />;
case "Amazon GuardDuty":
return <AmazonGuardDutyIcon />;
case "Amazon Inspector":
return <AmazonInspectorIcon />;
case "Amazon Macie":
return <AmazonMacieIcon />;
case "Amazon RDS":
return <AmazonRDSIcon />;
case "Amazon Route 53":
return <AmazonRoute53Icon />;
case "Amazon S3":
return <AmazonS3Icon />;
case "Amazon SNS":
return <AmazonSNSIcon />;
case "Amazon VPC":
return <AmazonVPCIcon />;
case "AWS Account":
return <AWSAccountIcon />;
case "AWS Athena":
return <AWSAthenaIcon />;
case "AWS Certificate Manager":
return <AWSCertificateManagerIcon />;
case "AWS CloudFormation":
return <AWSCloudFormationIcon />;
case "AWS CloudTrail":
return <AWSCloudTrailIcon />;
case "AWS CloudWatch":
return <AWSCloudWatchIcon />;
case "AWS Config":
return <AWSConfigIcon />;
case "AWS Database Migration":
return <AWSDatabaseMigrationServiceIcon />;
case "AWS Glue":
return <AWSGlueIcon />;
case "AWS IAM":
return <AWSIAMIcon />;
case "AWS Lambda":
return <AWSLambdaIcon />;
case "AWS Network Firewall":
return <AWSNetworkFirewallIcon />;
case "AWS Organizations":
return <AWSOrganizationsIcon />;
case "AWS Resource Explorer":
return <AWSResourceExplorerIcon />;
case "AWS Security Hub":
return <AWSSecurityHubIcon />;
case "AWS Systems Manager":
return <AWSSystemsManagerIncidentManagerIcon />;
case "AWS Trusted Advisor":
return <AWSTrustedAdvisorIcon />;
case "IAM Access Analyzer":
return <IAMAccessAnalyzerIcon />;
default:
return null;
}
};
@@ -0,0 +1,95 @@
"use client";
import { zodResolver } from "@hookform/resolvers/zod";
import React, { Dispatch, SetStateAction } from "react";
import { useForm } from "react-hook-form";
import * as z from "zod";
import { revokeInvite } from "@/actions/invitations/invitation";
import { DeleteIcon } from "@/components/icons";
import { useToast } from "@/components/ui";
import { CustomButton } from "@/components/ui/custom";
import { Form } from "@/components/ui/form";
const formSchema = z.object({
invitationId: z.string(),
});
export const DeleteForm = ({
invitationId,
setIsOpen,
}: {
invitationId: string;
setIsOpen: Dispatch<SetStateAction<boolean>>;
}) => {
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
invitationId,
},
});
const { toast } = useToast();
const isLoading = form.formState.isSubmitting;
async function onSubmitClient(values: z.infer<typeof formSchema>) {
const formData = new FormData();
Object.entries(values).forEach(
([key, value]) => value !== undefined && formData.append(key, value),
);
// client-side validation
const data = await revokeInvite(formData);
if (data?.errors && data.errors.length > 0) {
const error = data.errors[0];
const errorMessage = `${error.detail}`;
// show error
toast({
variant: "destructive",
title: "Oops! Something went wrong",
description: errorMessage,
});
} else {
toast({
title: "Success!",
description: "The invitation was revoked successfully.",
});
}
setIsOpen(false); // Close the modal on success
}
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmitClient)}>
<input type="hidden" name="id" value={invitationId} />
<div className="flex w-full justify-center sm:space-x-6">
<CustomButton
type="button"
ariaLabel="Cancel"
className="w-full bg-transparent"
variant="faded"
size="lg"
radius="lg"
onPress={() => setIsOpen(false)}
isDisabled={isLoading}
>
<span>Cancel</span>
</CustomButton>
<CustomButton
type="submit"
ariaLabel="Revoke"
className="w-full"
variant="solid"
color="danger"
size="lg"
isLoading={isLoading}
startContent={!isLoading && <DeleteIcon size={24} />}
>
{isLoading ? <>Loading</> : <span>Revoke</span>}
</CustomButton>
</div>
</form>
</Form>
);
};
@@ -0,0 +1,118 @@
"use client";
import { zodResolver } from "@hookform/resolvers/zod";
import { Dispatch, SetStateAction } from "react";
import { useForm } from "react-hook-form";
import * as z from "zod";
import { updateInvite } from "@/actions/invitations/invitation";
import { SaveIcon } from "@/components/icons";
import { useToast } from "@/components/ui";
import { CustomButton, CustomInput } from "@/components/ui/custom";
import { Form } from "@/components/ui/form";
import { editInviteFormSchema } from "@/types";
export const EditForm = ({
invitationId,
invitationEmail,
setIsOpen,
}: {
invitationId: string;
invitationEmail?: string;
setIsOpen: Dispatch<SetStateAction<boolean>>;
}) => {
const formSchema = editInviteFormSchema;
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
invitationId,
invitationEmail: invitationEmail,
},
});
const { toast } = useToast();
const isLoading = form.formState.isSubmitting;
const onSubmitClient = async (values: z.infer<typeof formSchema>) => {
const formData = new FormData();
console.log(values);
Object.entries(values).forEach(
([key, value]) => value !== undefined && formData.append(key, value),
);
const data = await updateInvite(formData);
if (data?.error) {
const errorMessage = `${data.error}`;
toast({
variant: "destructive",
title: "Oops! Something went wrong",
description: errorMessage,
});
} else {
toast({
title: "Success!",
description: "The invitation was updated successfully.",
});
setIsOpen(false); // Close the modal on success
}
};
return (
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmitClient)}
className="flex flex-col space-y-4"
>
<div className="text-md">
Current email: <span className="font-bold">{invitationEmail}</span>
</div>
<div>
<CustomInput
control={form.control}
name="invitationEmail"
type="email"
label="Email"
labelPlacement="outside"
placeholder={invitationEmail}
variant="bordered"
isRequired={false}
isInvalid={!!form.formState.errors.invitationEmail}
/>
</div>
<input type="hidden" name="invitationId" value={invitationId} />
<div className="flex w-full justify-center sm:space-x-6">
<CustomButton
type="button"
ariaLabel="Cancel"
className="w-full bg-transparent"
variant="faded"
size="lg"
radius="lg"
onPress={() => setIsOpen(false)}
isDisabled={isLoading}
>
<span>Cancel</span>
</CustomButton>
<CustomButton
type="submit"
ariaLabel="Save"
className="w-full"
variant="solid"
color="action"
size="lg"
isLoading={isLoading}
startContent={!isLoading && <SaveIcon size={24} />}
>
{isLoading ? <>Loading</> : <span>Save</span>}
</CustomButton>
</div>
</form>
</Form>
);
};
+2
View File
@@ -0,0 +1,2 @@
export * from "./delete-form";
export * from "./edit-form";
+2
View File
@@ -0,0 +1,2 @@
export * from "./invitation-details";
export * from "./send-invitation-button";
@@ -0,0 +1,111 @@
"use client";
import { Card, CardBody, Divider, Snippet } from "@nextui-org/react";
import { AddIcon } from "../icons";
import { CustomButton } from "../ui/custom";
import { DateWithTime } from "../ui/entities";
interface InvitationDetailsProps {
attributes: {
email: string;
state: string;
token: string;
expires_at: string;
inserted_at: string;
updated_at: string;
};
relationships?: {
inviter: {
data: {
id: string;
};
};
};
selfLink: string;
}
export const InvitationDetails = ({ attributes }: InvitationDetailsProps) => {
const baseURL = process.env.SITE_URL || "http://localhost:3000";
const invitationLink = `${baseURL}/sign-up?invitation_token=${attributes.token}`;
return (
<div className="flex flex-col gap-x-4 gap-y-8">
<Card
isBlurred
className="border-none bg-background/60 dark:bg-prowler-blue-800"
shadow="sm"
>
<CardBody>
<h2 className="text-2xl font-bold text-foreground/90">
Invitation Details
</h2>
<Divider className="my-4" />
<div className="space-y-3">
<div className="flex items-center justify-between">
<strong className="text-default-500">Email:</strong>
<span>{attributes.email}</span>
</div>
<div className="flex items-center justify-between">
<strong className="text-default-500">State:</strong>
<span className="capitalize">{attributes.state}</span>
</div>
<div className="flex items-center justify-between">
<strong className="text-default-500">Token:</strong>
<span>{attributes.token}</span>
</div>
<div className="flex items-center justify-between">
<strong className="text-default-500">Expires At:</strong>
<DateWithTime dateTime={attributes.expires_at} />
</div>
<div className="flex items-center justify-between">
<strong className="text-default-500">Inserted At:</strong>
<DateWithTime dateTime={attributes.inserted_at} />
</div>
<div className="flex items-center justify-between">
<strong className="text-default-500">Updated At:</strong>
<DateWithTime dateTime={attributes.updated_at} />
</div>
</div>
<Divider className="my-4" />
<h3 className="pb-2 text-xl font-bold text-foreground/90">
Share this link with the user:
</h3>
<div className="flex flex-col items-start justify-between">
<Snippet
classNames={{
base: "mx-auto",
}}
hideSymbol
variant="bordered"
className="overflow-hidden text-ellipsis whitespace-nowrap"
>
<p className="no-scrollbar w-96 overflow-hidden overflow-x-scroll text-ellipsis whitespace-nowrap text-sm">
{invitationLink}
</p>
</Snippet>
</div>
</CardBody>
</Card>
<div className="flex w-full items-center justify-end">
<CustomButton
asLink="/invitations/"
ariaLabel="Send Invitation"
variant="solid"
color="action"
size="md"
endContent={<AddIcon size={20} />}
>
Back to Invitations
</CustomButton>
</div>
</div>
);
};
@@ -0,0 +1,21 @@
"use client";
import { AddIcon } from "../icons";
import { CustomButton } from "../ui/custom";
export const SendInvitationButton = () => {
return (
<div className="flex w-full items-center justify-end">
<CustomButton
asLink="/invitations/new"
ariaLabel="Send Invitation"
variant="solid"
color="action"
size="md"
endContent={<AddIcon size={20} />}
>
Send Invitation
</CustomButton>
</div>
);
};
@@ -0,0 +1,72 @@
"use client";
import { ColumnDef } from "@tanstack/react-table";
import { DateWithTime } from "@/components/ui/entities";
import { DataTableColumnHeader } from "@/components/ui/table";
import { InvitationProps } from "@/types";
import { DataTableRowActions } from "./data-table-row-actions";
const getInvitationData = (row: { original: InvitationProps }) => {
return row.original.attributes;
};
export const ColumnsInvitation: ColumnDef<InvitationProps>[] = [
{
accessorKey: "email",
header: () => <div className="text-left">Email</div>,
cell: ({ row }) => {
const data = getInvitationData(row);
return <p className="font-semibold">{data?.email || "N/A"}</p>;
},
},
{
accessorKey: "state",
header: ({ column }) => (
<DataTableColumnHeader column={column} title={"State"} param="state" />
),
cell: ({ row }) => {
const { state } = getInvitationData(row);
return <p className="font-semibold">{state}</p>;
},
},
{
accessorKey: "inserted_at",
header: ({ column }) => (
<DataTableColumnHeader
column={column}
title={"Inserted At"}
param="inserted_at"
/>
),
cell: ({ row }) => {
const { inserted_at } = getInvitationData(row);
return <DateWithTime dateTime={inserted_at} showTime={false} />;
},
},
{
accessorKey: "expires_at",
header: ({ column }) => (
<DataTableColumnHeader
column={column}
title={"Expires At"}
param="expires_at"
/>
),
cell: ({ row }) => {
const { expires_at } = getInvitationData(row);
return <DateWithTime dateTime={expires_at} showTime={false} />;
},
},
{
accessorKey: "actions",
header: () => <div className="text-right">Actions</div>,
id: "actions",
cell: ({ row }) => {
return <DataTableRowActions row={row} />;
},
},
];
@@ -0,0 +1,120 @@
"use client";
import {
Button,
Dropdown,
DropdownItem,
DropdownMenu,
DropdownSection,
DropdownTrigger,
} from "@nextui-org/react";
import {
AddNoteBulkIcon,
DeleteDocumentBulkIcon,
EditDocumentBulkIcon,
} from "@nextui-org/shared-icons";
import { Row } from "@tanstack/react-table";
import clsx from "clsx";
import { useState } from "react";
import { VerticalDotsIcon } from "@/components/icons";
import { CustomAlertModal } from "@/components/ui/custom";
import { DeleteForm, EditForm } from "../forms";
interface DataTableRowActionsProps<InvitationProps> {
row: Row<InvitationProps>;
}
const iconClasses =
"text-2xl text-default-500 pointer-events-none flex-shrink-0";
export function DataTableRowActions<InvitationProps>({
row,
}: DataTableRowActionsProps<InvitationProps>) {
const [isEditOpen, setIsEditOpen] = useState(false);
const [isDeleteOpen, setIsDeleteOpen] = useState(false);
const invitationId = (row.original as { id: string }).id;
const invitationEmail = (row.original as any).attributes?.email;
return (
<>
<CustomAlertModal
isOpen={isEditOpen}
onOpenChange={setIsEditOpen}
title="Edit Invitation"
description={"Edit the invitation details"}
>
<EditForm
invitationId={invitationId}
invitationEmail={invitationEmail}
setIsOpen={setIsEditOpen}
/>
</CustomAlertModal>
<CustomAlertModal
isOpen={isDeleteOpen}
onOpenChange={setIsDeleteOpen}
title="Are you absolutely sure?"
description="This action cannot be undone. This will permanently revoke your invitation."
>
<DeleteForm invitationId={invitationId} setIsOpen={setIsDeleteOpen} />
</CustomAlertModal>
<div className="relative flex items-center justify-end gap-2">
<Dropdown
className="shadow-xl dark:bg-prowler-blue-800"
placement="bottom"
>
<DropdownTrigger>
<Button isIconOnly radius="full" size="sm" variant="light">
<VerticalDotsIcon className="text-default-400" />
</Button>
</DropdownTrigger>
<DropdownMenu
closeOnSelect
aria-label="Actions"
color="default"
variant="flat"
>
<DropdownSection title="Actions">
<DropdownItem
href={`http://localhost:3000/invitations/check-details?id=${invitationId}`}
key="check-details"
description="View invitation details"
textValue="Check Details"
startContent={<AddNoteBulkIcon className={iconClasses} />}
>
Check Details
</DropdownItem>
<DropdownItem
key="edit"
description="Allows you to edit the invitation"
textValue="Edit Invitation"
startContent={<EditDocumentBulkIcon className={iconClasses} />}
onClick={() => setIsEditOpen(true)}
>
Edit Invitation
</DropdownItem>
</DropdownSection>
<DropdownSection title="Danger zone">
<DropdownItem
key="delete"
className="text-danger"
color="danger"
description="Delete the invitation permanently"
textValue="Delete Invitation"
startContent={
<DeleteDocumentBulkIcon
className={clsx(iconClasses, "!text-danger")}
/>
}
onClick={() => setIsDeleteOpen(true)}
>
Revoke Invitation
</DropdownItem>
</DropdownSection>
</DropdownMenu>
</Dropdown>
</div>
</>
);
}
+3
View File
@@ -0,0 +1,3 @@
export * from "./column-invitations";
export * from "./data-table-row-actions";
export * from "./skeleton-table-invitations";
@@ -0,0 +1,59 @@
import { Card, Skeleton } from "@nextui-org/react";
import React from "react";
export const SkeletonTableInvitation = () => {
return (
<Card className="h-full w-full space-y-5 p-4" radius="sm">
{/* Table headers */}
<div className="hidden justify-between md:flex">
<Skeleton className="w-2/12 rounded-lg">
<div className="h-8 bg-default-200"></div>
</Skeleton>
<Skeleton className="w-2/12 rounded-lg">
<div className="h-8 bg-default-200"></div>
</Skeleton>
<Skeleton className="w-2/12 rounded-lg">
<div className="h-8 bg-default-200"></div>
</Skeleton>
<Skeleton className="w-2/12 rounded-lg">
<div className="h-8 bg-default-200"></div>
</Skeleton>
<Skeleton className="w-2/12 rounded-lg">
<div className="h-8 bg-default-200"></div>
</Skeleton>
<Skeleton className="w-1/12 rounded-lg">
<div className="h-8 bg-default-200"></div>
</Skeleton>
</div>
{/* Table body */}
<div className="space-y-3">
{[...Array(10)].map((_, index) => (
<div
key={index}
className="flex flex-col items-center justify-between space-x-0 md:flex-row md:space-x-4"
>
<Skeleton className="mb-2 w-full rounded-lg md:mb-0 md:w-2/12">
<div className="h-12 bg-default-300"></div>
</Skeleton>
<Skeleton className="mb-2 w-full rounded-lg md:mb-0 md:w-2/12">
<div className="h-12 bg-default-300"></div>
</Skeleton>
<Skeleton className="mb-2 hidden rounded-lg sm:flex md:mb-0 md:w-2/12">
<div className="h-12 bg-default-300"></div>
</Skeleton>
<Skeleton className="mb-2 hidden rounded-lg sm:flex md:mb-0 md:w-2/12">
<div className="h-12 bg-default-300"></div>
</Skeleton>
<Skeleton className="mb-2 hidden rounded-lg sm:flex md:mb-0 md:w-2/12">
<div className="h-12 bg-default-300"></div>
</Skeleton>
<Skeleton className="mb-2 hidden rounded-lg sm:flex md:mb-0 md:w-1/12">
<div className="h-12 bg-default-300"></div>
</Skeleton>
</div>
))}
</div>
</Card>
);
};
@@ -0,0 +1 @@
export * from "./send-invitation-form";
@@ -0,0 +1,107 @@
"use client";
import { zodResolver } from "@hookform/resolvers/zod";
import { SaveIcon } from "lucide-react";
import { useRouter } from "next/navigation";
import { useForm } from "react-hook-form";
import * as z from "zod";
import { sendInvite } from "@/actions/invitations/invitation";
import { useToast } from "@/components/ui";
import { CustomButton, CustomInput } from "@/components/ui/custom";
import { Form } from "@/components/ui/form";
import { ApiError } from "@/types";
const sendInvitationFormSchema = z.object({
email: z.string().email("Please enter a valid email"),
});
export type FormValues = z.infer<typeof sendInvitationFormSchema>;
export const SendInvitationForm = () => {
const { toast } = useToast();
const router = useRouter();
const form = useForm<FormValues>({
resolver: zodResolver(sendInvitationFormSchema),
defaultValues: {
email: "",
},
});
const isLoading = form.formState.isSubmitting;
const onSubmitClient = async (values: FormValues) => {
const formData = new FormData();
formData.append("email", values.email);
try {
const data = await sendInvite(formData);
if (data?.errors && data.errors.length > 0) {
data.errors.forEach((error: ApiError) => {
const errorMessage = error.detail;
switch (error.source.pointer) {
case "/data/attributes/email":
form.setError("email", {
type: "server",
message: errorMessage,
});
break;
default:
toast({
variant: "destructive",
title: "Oops! Something went wrong",
description: errorMessage,
});
}
});
} else {
const invitationId = data?.data?.id || "";
router.push(`/invitations/check-details/?id=${invitationId}`);
}
} catch (error) {
toast({
variant: "destructive",
title: "Error",
description: "An unexpected error occurred. Please try again.",
});
}
};
return (
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmitClient)}
className="flex flex-col space-y-4"
>
<CustomInput
control={form.control}
name="email"
type="email"
label="Email"
labelPlacement="inside"
placeholder="Enter the email address"
variant="bordered"
isRequired
isInvalid={!!form.formState.errors.email}
/>
<div className="flex w-full justify-end sm:space-x-6">
<CustomButton
type="submit"
ariaLabel="Send Invitation"
className="w-1/2"
variant="solid"
color="action"
size="lg"
isLoading={isLoading}
startContent={!isLoading && <SaveIcon size={24} />}
>
{isLoading ? <>Loading</> : <span>Send Invitation</span>}
</CustomButton>
</div>
</form>
</Form>
);
};
@@ -0,0 +1,3 @@
export * from "./skeleton-invitation-info";
export * from "./vertical-steps";
export * from "./workflow-send-invite";
@@ -0,0 +1,65 @@
import { Card, Skeleton } from "@nextui-org/react";
import React from "react";
export const SkeletonInvitationInfo = () => {
return (
<Card className="h-full w-full space-y-5 p-4" radius="sm">
{/* Table headers */}
<div className="hidden justify-between md:flex">
<Skeleton className="w-1/12 rounded-lg">
<div className="h-8 bg-default-200"></div>
</Skeleton>
<Skeleton className="w-2/12 rounded-lg">
<div className="h-8 bg-default-200"></div>
</Skeleton>
<Skeleton className="w-2/12 rounded-lg">
<div className="h-8 bg-default-200"></div>
</Skeleton>
<Skeleton className="w-2/12 rounded-lg">
<div className="h-8 bg-default-200"></div>
</Skeleton>
<Skeleton className="w-2/12 rounded-lg">
<div className="h-8 bg-default-200"></div>
</Skeleton>
<Skeleton className="w-1/12 rounded-lg">
<div className="h-8 bg-default-200"></div>
</Skeleton>
<Skeleton className="w-1/12 rounded-lg">
<div className="h-8 bg-default-200"></div>
</Skeleton>
</div>
{/* Table body */}
<div className="space-y-3">
{[...Array(3)].map((_, index) => (
<div
key={index}
className="flex flex-col items-center justify-between space-x-0 md:flex-row md:space-x-4"
>
<Skeleton className="mb-2 w-full rounded-lg md:mb-0 md:w-1/12">
<div className="h-12 bg-default-300"></div>
</Skeleton>
<Skeleton className="mb-2 w-full rounded-lg md:mb-0 md:w-2/12">
<div className="h-12 bg-default-300"></div>
</Skeleton>
<Skeleton className="mb-2 hidden rounded-lg sm:flex md:mb-0 md:w-2/12">
<div className="h-12 bg-default-300"></div>
</Skeleton>
<Skeleton className="mb-2 hidden rounded-lg sm:flex md:mb-0 md:w-2/12">
<div className="h-12 bg-default-300"></div>
</Skeleton>
<Skeleton className="mb-2 hidden rounded-lg sm:flex md:mb-0 md:w-2/12">
<div className="h-12 bg-default-300"></div>
</Skeleton>
<Skeleton className="mb-2 hidden rounded-lg sm:flex md:mb-0 md:w-1/12">
<div className="h-12 bg-default-300"></div>
</Skeleton>
<Skeleton className="mb-2 hidden rounded-lg sm:flex md:mb-0 md:w-1/12">
<div className="h-12 bg-default-300"></div>
</Skeleton>
</div>
))}
</div>
</Card>
);
};
@@ -0,0 +1,291 @@
"use client";
import type { ButtonProps } from "@nextui-org/react";
import { cn } from "@nextui-org/react";
import { useControlledState } from "@react-stately/utils";
import { domAnimation, LazyMotion, m } from "framer-motion";
import type { ComponentProps } from "react";
import React from "react";
export type VerticalStepProps = {
className?: string;
description?: React.ReactNode;
title?: React.ReactNode;
};
export interface VerticalStepsProps
extends React.HTMLAttributes<HTMLButtonElement> {
/**
* An array of steps.
*
* @default []
*/
steps?: VerticalStepProps[];
/**
* The color of the steps.
*
* @default "primary"
*/
color?: ButtonProps["color"];
/**
* The current step index.
*/
currentStep?: number;
/**
* The default step index.
*
* @default 0
*/
defaultStep?: number;
/**
* Whether to hide the progress bars.
*
* @default false
*/
hideProgressBars?: boolean;
/**
* The custom class for the steps wrapper.
*/
className?: string;
/**
* The custom class for the step.
*/
stepClassName?: string;
/**
* Callback function when the step index changes.
*/
onStepChange?: (stepIndex: number) => void;
}
function CheckIcon(props: ComponentProps<"svg">) {
return (
<svg
{...props}
fill="none"
stroke="currentColor"
strokeWidth={2}
viewBox="0 0 24 24"
>
<m.path
animate={{ pathLength: 1 }}
d="M5 13l4 4L19 7"
initial={{ pathLength: 0 }}
strokeLinecap="round"
strokeLinejoin="round"
transition={{
delay: 0.2,
type: "tween",
ease: "easeOut",
duration: 0.3,
}}
/>
</svg>
);
}
export const VerticalSteps = React.forwardRef<
HTMLButtonElement,
VerticalStepsProps
>(
(
{
color = "primary",
steps = [],
defaultStep = 0,
onStepChange,
currentStep: currentStepProp,
hideProgressBars = false,
stepClassName,
className,
...props
},
ref,
) => {
const [currentStep, setCurrentStep] = useControlledState(
currentStepProp,
defaultStep,
onStepChange,
);
const colors = React.useMemo(() => {
let userColor;
let fgColor;
const colorsVars = [
"[--active-fg-color:var(--step-fg-color)]",
"[--active-border-color:var(--step-color)]",
"[--active-color:var(--step-color)]",
"[--complete-background-color:var(--step-color)]",
"[--complete-border-color:var(--step-color)]",
"[--inactive-border-color:hsl(var(--nextui-default-300))]",
"[--inactive-color:hsl(var(--nextui-default-300))]",
];
switch (color) {
case "primary":
userColor = "[--step-color:hsl(var(--nextui-primary))]";
fgColor = "[--step-fg-color:hsl(var(--nextui-primary-foreground))]";
break;
case "secondary":
userColor = "[--step-color:hsl(var(--nextui-secondary))]";
fgColor = "[--step-fg-color:hsl(var(--nextui-secondary-foreground))]";
break;
case "success":
userColor = "[--step-color:hsl(var(--nextui-success))]";
fgColor = "[--step-fg-color:hsl(var(--nextui-success-foreground))]";
break;
case "warning":
userColor = "[--step-color:hsl(var(--nextui-warning))]";
fgColor = "[--step-fg-color:hsl(var(--nextui-warning-foreground))]";
break;
case "danger":
userColor = "[--step-color:hsl(var(--nextui-error))]";
fgColor = "[--step-fg-color:hsl(var(--nextui-error-foreground))]";
break;
case "default":
userColor = "[--step-color:hsl(var(--nextui-default))]";
fgColor = "[--step-fg-color:hsl(var(--nextui-default-foreground))]";
break;
default:
userColor = "[--step-color:hsl(var(--nextui-primary))]";
fgColor = "[--step-fg-color:hsl(var(--nextui-primary-foreground))]";
break;
}
if (!className?.includes("--step-fg-color")) colorsVars.unshift(fgColor);
if (!className?.includes("--step-color")) colorsVars.unshift(userColor);
if (!className?.includes("--inactive-bar-color"))
colorsVars.push(
"[--inactive-bar-color:hsl(var(--nextui-default-300))]",
);
return colorsVars;
}, [color, className]);
return (
<nav aria-label="Progress" className="max-w-fit">
<ol className={cn("flex flex-col gap-y-3", colors, className)}>
{steps?.map((step, stepIdx) => {
const status =
currentStep === stepIdx
? "active"
: currentStep < stepIdx
? "inactive"
: "complete";
return (
<li key={stepIdx} className="relative">
<div className="flex w-full max-w-full items-center">
<button
key={stepIdx}
ref={ref}
aria-current={status === "active" ? "step" : undefined}
className={cn(
"group flex w-full cursor-pointer items-center justify-center gap-4 rounded-large px-3 py-2.5",
stepClassName,
)}
onClick={() => setCurrentStep(stepIdx)}
{...props}
>
<div className="flex h-full items-center">
<LazyMotion features={domAnimation}>
<div className="relative">
<m.div
animate={status}
className={cn(
"relative flex h-[34px] w-[34px] items-center justify-center rounded-full border-medium text-large font-semibold text-default-foreground",
{
"shadow-lg": status === "complete",
},
)}
data-status={status}
initial={false}
transition={{ duration: 0.25 }}
variants={{
inactive: {
backgroundColor: "transparent",
borderColor: "var(--inactive-border-color)",
color: "var(--inactive-color)",
},
active: {
backgroundColor: "transparent",
borderColor: "var(--active-border-color)",
color: "var(--active-color)",
},
complete: {
backgroundColor:
"var(--complete-background-color)",
borderColor: "var(--complete-border-color)",
},
}}
>
<div className="flex items-center justify-center">
{status === "complete" ? (
<CheckIcon className="h-6 w-6 text-[var(--active-fg-color)]" />
) : (
<span>{stepIdx + 1}</span>
)}
</div>
</m.div>
</div>
</LazyMotion>
</div>
<div className="flex-1 text-left">
<div>
<div
className={cn(
"text-medium font-medium text-default-foreground transition-[color,opacity] duration-300 group-active:opacity-70",
{
"text-default-500": status === "inactive",
},
)}
>
{step.title}
</div>
<div
className={cn(
"text-tiny text-default-600 transition-[color,opacity] duration-300 group-active:opacity-70 lg:text-small",
{
"text-default-500": status === "inactive",
},
)}
>
{step.description}
</div>
</div>
</div>
</button>
</div>
{stepIdx < steps.length - 1 && !hideProgressBars && (
<div
aria-hidden="true"
className={cn(
"pointer-events-none absolute left-3 top-[calc(64px_*_var(--idx)_+_1)] flex h-1/2 -translate-y-1/3 items-center px-4",
)}
style={{
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
"--idx": stepIdx,
}}
>
<div
className={cn(
"relative h-full w-0.5 bg-[var(--inactive-bar-color)] transition-colors duration-300",
"after:absolute after:block after:h-0 after:w-full after:bg-[var(--active-border-color)] after:transition-[height] after:duration-300 after:content-['']",
{
"after:h-full": stepIdx < currentStep,
},
)}
/>
</div>
)}
</li>
);
})}
</ol>
</nav>
);
},
);
VerticalSteps.displayName = "VerticalSteps";
@@ -0,0 +1,64 @@
"use client";
import { Progress, Spacer } from "@nextui-org/react";
import { usePathname } from "next/navigation";
import React from "react";
import { VerticalSteps } from "./vertical-steps";
const steps = [
{
title: "Send Invitation",
description:
"Enter the email address of the person you want to invite and send the invitation.",
href: "/invitations/new",
},
{
title: "Review Invitation Details",
description:
"Review the invitation details and share the information required for the person to accept the invitation.",
href: "/invitations/check-details",
},
];
export const WorkflowSendInvite = () => {
const pathname = usePathname();
// Calculate current step based on pathname
const currentStepIndex = steps.findIndex((step) =>
pathname.endsWith(step.href),
);
const currentStep = currentStepIndex === -1 ? 0 : currentStepIndex;
return (
<section className="max-w-sm">
<h1 className="mb-2 text-xl font-medium" id="getting-started">
Send invitation
</h1>
<p className="mb-5 text-small text-default-500">
Follow the steps to send an invitation to the users.
</p>
<Progress
classNames={{
base: "px-0.5 mb-5",
label: "text-small",
value: "text-small text-default-400",
}}
label="Steps"
maxValue={steps.length - 1}
minValue={0}
showValueLabel={true}
size="md"
value={currentStep}
valueLabel={`${currentStep + 1} of ${steps.length}`}
/>
<VerticalSteps
hideProgressBars
currentStep={currentStep}
stepClassName="border border-default-200 dark:border-default-50 aria-[current]:bg-default-100 dark:aria-[current]:bg-prowler-blue-800 cursor-default"
steps={steps}
/>
<Spacer y={4} />
</section>
);
};
+44
View File
@@ -0,0 +1,44 @@
import React from "react";
import { ActionCard } from "@/components/ui";
const cardData = [
{
findings: 3,
title: "Internet Exposed Resources",
},
{
findings: 15,
title: "Exposed Secrets",
},
{
findings: 0,
title: "IAM Policies Leading to Privilege Escalation",
},
{
findings: 0,
title: "EC2 with Metadata Service V1 (IMDSv1)",
},
];
export const AttackSurface = () => {
return (
<div className="flex flex-col gap-2">
{cardData.map((card, index) => (
<ActionCard
key={index}
color={card.findings > 0 ? "fail" : "success"}
icon={
card.findings > 0
? "solar:danger-triangle-bold"
: "heroicons:shield-check-solid"
}
title={card.title}
description={
card.findings > 0 ? "Review Required" : "No Issues Found"
}
/>
))}
</div>
);
};
+3
View File
@@ -0,0 +1,3 @@
export * from "./AttackSurface";
export * from "./provider-overview/provider-overview";
export * from "./provider-overview/skeleton-provider-overview";
@@ -0,0 +1,150 @@
"use client";
import { Card, CardBody, CardHeader } from "@nextui-org/react";
import { AddIcon } from "@/components/icons/Icons";
import {
AWSProviderBadge,
AzureProviderBadge,
GCPProviderBadge,
KS8ProviderBadge,
} from "@/components/icons/providers-badge";
import { CustomButton } from "@/components/ui/custom/custom-button";
import { ProviderOverviewProps } from "@/types";
export const ProvidersOverview = ({
providersOverview,
}: {
providersOverview: ProviderOverviewProps;
}) => {
console.log(providersOverview);
if (!providersOverview || !Array.isArray(providersOverview.data)) {
return <p>No provider data available</p>;
}
const calculatePassingPercentage = (pass: number, total: number) =>
total > 0 ? ((pass / total) * 100).toFixed(2) : "0.00";
const renderProviderBadge = (providerId: string) => {
switch (providerId) {
case "aws":
return <AWSProviderBadge width={30} height={30} />;
case "azure":
return <AzureProviderBadge width={30} height={30} />;
case "gcp":
return <GCPProviderBadge width={30} height={30} />;
case "kubernetes":
return <KS8ProviderBadge width={30} height={30} />;
default:
return null;
}
};
const providers = [
{ id: "aws", name: "AWS" },
{ id: "azure", name: "Azure" },
{ id: "gcp", name: "GCP" },
{ id: "kubernetes", name: "Kubernetes" },
];
return (
<Card className="dark:bg-prowler-blue-400">
<CardHeader>
<h3 className="text-sm font-bold">Providers Overview</h3>
</CardHeader>
<CardBody>
<div className="grid grid-cols-1 gap-3">
<div className="grid grid-cols-4 border-b pb-2 text-xs font-semibold">
<span className="text-center">Provider</span>
<span className="flex flex-col items-center text-center">
<span>Percent</span>
<span>Passing</span>
</span>
<span className="flex flex-col items-center text-center">
<span>Failing</span>
<span>Checks</span>
</span>
<span className="flex flex-col items-center text-center">
<span>Total</span>
<span>Resources</span>
</span>
</div>
{providers.map((providerTemplate) => {
const providerData = providersOverview.data.find(
(p) => p.id === providerTemplate.id,
);
return (
<div
key={providerTemplate.id}
className="grid grid-cols-4 items-center border-b py-2 text-sm"
>
<span className="flex items-center justify-center px-4">
{renderProviderBadge(providerTemplate.id)}
</span>
<span className="text-center">
{providerData
? calculatePassingPercentage(
providerData.attributes.findings.pass,
providerData.attributes.findings.total,
)
: "0.00"}
%
</span>
<span className="text-center">
{providerData ? providerData.attributes.findings.fail : "-"}
</span>
<span className="text-center">
{providerData ? providerData.attributes.resources.total : "-"}
</span>
</div>
);
})}
{/* Totals row */}
<div className="grid grid-cols-4 items-center border-b py-2 text-sm font-semibold">
<span className="flex items-center justify-center px-4">Total</span>
<span className="text-center">
{calculatePassingPercentage(
providersOverview.data.reduce(
(sum, provider) => sum + provider.attributes.findings.pass,
0,
),
providersOverview.data.reduce(
(sum, provider) => sum + provider.attributes.findings.total,
0,
),
)}
%
</span>
<span className="text-center">
{providersOverview.data.reduce(
(sum, provider) => sum + provider.attributes.findings.fail,
0,
)}
</span>
<span className="text-center">
{providersOverview.data.reduce(
(sum, provider) => sum + provider.attributes.resources.total,
0,
)}
</span>
</div>
</div>
<div className="mt-4 flex w-full items-center justify-end">
<CustomButton
asLink="/providers/connect-account"
ariaLabel="Go to Providers page"
variant="solid"
color="action"
size="sm"
endContent={<AddIcon size={20} />}
>
Add Provider
</CustomButton>
</div>
</CardBody>
</Card>
);
};
@@ -0,0 +1,64 @@
import { Card, CardBody, CardHeader, Skeleton } from "@nextui-org/react";
export const SkeletonProvidersOverview = () => {
const rows = 4;
return (
<Card>
<CardHeader>
<Skeleton className="h-6 w-1/3 rounded-lg">
<div className="h-6 bg-default-200"></div>
</Skeleton>
</CardHeader>
<CardBody>
<div className="grid grid-cols-1 gap-4">
{/* Header Skeleton */}
<div className="grid grid-cols-4 border-b pb-2 text-sm font-semibold">
<Skeleton className="h-5 w-full rounded-lg">
<div className="h-5 bg-default-200"></div>
</Skeleton>
<Skeleton className="h-5 w-full rounded-lg">
<div className="h-5 bg-default-200"></div>
</Skeleton>
<Skeleton className="h-5 w-full rounded-lg">
<div className="h-5 bg-default-200"></div>
</Skeleton>
<Skeleton className="h-5 w-full rounded-lg">
<div className="h-5 bg-default-200"></div>
</Skeleton>
</div>
{/* Row Skeletons */}
{Array.from({ length: rows }).map((_, index) => (
<div
key={index}
className="grid grid-cols-4 items-center border-b py-2 text-sm"
>
{/* Provider Name */}
<div className="flex items-center space-x-2">
<Skeleton className="h-5 w-5 rounded-lg">
<div className="h-5 w-5 bg-default-200"></div>
</Skeleton>
<Skeleton className="h-5 w-1/3 rounded-lg">
<div className="h-5 bg-default-200"></div>
</Skeleton>
</div>
{/* Percent Passing */}
<Skeleton className="h-5 w-1/4 rounded-lg">
<div className="h-5 bg-default-200"></div>
</Skeleton>
{/* Failing Checks */}
<Skeleton className="h-5 w-1/4 rounded-lg">
<div className="h-5 bg-default-200"></div>
</Skeleton>
{/* Total Resources */}
<Skeleton className="h-5 w-1/4 rounded-lg">
<div className="h-5 bg-default-200"></div>
</Skeleton>
</div>
))}
</div>
</CardBody>
</Card>
);
};
+53
View File
@@ -0,0 +1,53 @@
import { tv } from "tailwind-variants";
export const title = tv({
base: "tracking-tight inline font-semibold",
variants: {
color: {
violet: "from-[#FF1CF7] to-[#b249f8]",
yellow: "from-[#FF705B] to-[#FFB457]",
blue: "from-[#5EA2EF] to-[#0072F5]",
cyan: "from-[#00b7fa] to-[#01cfea]",
green: "from-[#6FEE8D] to-[#17c964]",
pink: "from-[#FF72E1] to-[#F54C7A]",
foreground: "dark:from-[#FFFFFF] dark:to-[#4B4B4B]",
},
size: {
sm: "text-3xl lg:text-4xl",
md: "text-[2.3rem] lg:text-5xl leading-9",
lg: "text-4xl lg:text-6xl",
},
fullWidth: {
true: "w-full block",
},
},
defaultVariants: {
size: "md",
},
compoundVariants: [
{
color: [
"violet",
"yellow",
"blue",
"cyan",
"green",
"pink",
"foreground",
],
class: "bg-clip-text text-transparent bg-gradient-to-b",
},
],
});
export const subtitle = tv({
base: "w-full md:w-1/2 my-2 text-lg lg:text-xl text-default-600 block max-w-full",
variants: {
fullWidth: {
true: "!w-full",
},
},
defaultVariants: {
fullWidth: true,
},
});
+21
View File
@@ -0,0 +1,21 @@
"use client";
import { AddIcon } from "../icons";
import { CustomButton } from "../ui/custom";
export const AddProvider = () => {
return (
<div className="flex w-full items-center justify-end">
<CustomButton
asLink="/providers/connect-account"
ariaLabel="Add Account"
variant="solid"
color="action"
size="md"
endContent={<AddIcon size={20} />}
>
Add Account
</CustomButton>
</div>
);
};
@@ -0,0 +1,87 @@
"use client";
import { zodResolver } from "@hookform/resolvers/zod";
import React, { Dispatch, SetStateAction } from "react";
import { useForm } from "react-hook-form";
import * as z from "zod";
import { deleteProvider } from "@/actions/providers";
import { DeleteIcon } from "@/components/icons";
import { useToast } from "@/components/ui";
import { CustomButton } from "@/components/ui/custom";
import { Form } from "@/components/ui/form";
const formSchema = z.object({
providerId: z.string(),
});
export const DeleteForm = ({
providerId,
setIsOpen,
}: {
providerId: string;
setIsOpen: Dispatch<SetStateAction<boolean>>;
}) => {
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
});
const { toast } = useToast();
const isLoading = form.formState.isSubmitting;
async function onSubmitClient(formData: FormData) {
// client-side validation
const data = await deleteProvider(formData);
if (data?.errors && data.errors.length > 0) {
const error = data.errors[0];
const errorMessage = `${error.detail}`;
// show error
toast({
variant: "destructive",
title: "Oops! Something went wrong",
description: errorMessage,
});
} else {
toast({
title: "Success!",
description: "The provider was removed successfully.",
});
}
setIsOpen(false); // Close the modal on success
}
return (
<Form {...form}>
<form action={onSubmitClient}>
<input type="hidden" name="id" value={providerId} />
<div className="flex w-full justify-center sm:space-x-6">
<CustomButton
type="button"
ariaLabel="Cancel"
className="w-full bg-transparent"
variant="faded"
size="lg"
radius="lg"
onPress={() => setIsOpen(false)}
isDisabled={isLoading}
>
<span>Cancel</span>
</CustomButton>
<CustomButton
type="submit"
ariaLabel="Delete"
className="w-full"
variant="solid"
color="danger"
size="lg"
isLoading={isLoading}
startContent={!isLoading && <DeleteIcon size={24} />}
>
{isLoading ? <>Loading</> : <span>Delete</span>}
</CustomButton>
</div>
</form>
</Form>
);
};
+119
View File
@@ -0,0 +1,119 @@
"use client";
import { zodResolver } from "@hookform/resolvers/zod";
import { Dispatch, SetStateAction } from "react";
import { useForm } from "react-hook-form";
import * as z from "zod";
import { updateProvider } from "@/actions/providers";
import { SaveIcon } from "@/components/icons";
import { useToast } from "@/components/ui";
import { CustomButton, CustomInput } from "@/components/ui/custom";
import { Form } from "@/components/ui/form";
import { editProviderFormSchema } from "@/types";
export const EditForm = ({
providerId,
providerAlias,
setIsOpen,
}: {
providerId: string;
providerAlias?: string;
setIsOpen: Dispatch<SetStateAction<boolean>>;
}) => {
const formSchema = editProviderFormSchema(providerAlias ?? "");
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
providerId: providerId,
alias: providerAlias,
},
});
const { toast } = useToast();
const isLoading = form.formState.isSubmitting;
const onSubmitClient = async (values: z.infer<typeof formSchema>) => {
const formData = new FormData();
Object.entries(values).forEach(
([key, value]) => value !== undefined && formData.append(key, value),
);
const data = await updateProvider(formData);
if (data?.errors && data.errors.length > 0) {
const error = data.errors[0];
const errorMessage = `${error.detail}`;
// show error
toast({
variant: "destructive",
title: "Oops! Something went wrong",
description: errorMessage,
});
} else {
toast({
title: "Success!",
description: "The provider was updated successfully.",
});
setIsOpen(false); // Close the modal on success
}
};
return (
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmitClient)}
className="flex flex-col space-y-4"
>
<div className="text-md">
Current alias: <span className="font-bold">{providerAlias}</span>
</div>
<div>
<CustomInput
control={form.control}
name="alias"
type="text"
label="Alias"
labelPlacement="outside"
placeholder={providerAlias}
variant="bordered"
isRequired={false}
isInvalid={!!form.formState.errors.alias}
/>
</div>
<input type="hidden" name="providerId" value={providerId} />
<div className="flex w-full justify-center sm:space-x-6">
<CustomButton
type="button"
ariaLabel="Cancel"
className="w-full bg-transparent"
variant="faded"
size="lg"
radius="lg"
onPress={() => setIsOpen(false)}
isDisabled={isLoading}
>
<span>Cancel</span>
</CustomButton>
<CustomButton
type="submit"
ariaLabel="Save"
className="w-full"
variant="solid"
color="action"
size="lg"
isLoading={isLoading}
startContent={!isLoading && <SaveIcon size={24} />}
>
{isLoading ? <>Loading</> : <span>Save</span>}
</CustomButton>
</div>
</form>
</Form>
);
};
+2
View File
@@ -0,0 +1,2 @@
export * from "./delete-form";
export * from "./edit-form";
+4
View File
@@ -0,0 +1,4 @@
export * from "./add-provider";
export * from "./forms/delete-form";
export * from "./provider-info";
export * from "./radio-group-provider";
+78
View File
@@ -0,0 +1,78 @@
import React from "react";
import { ConnectionFalse, ConnectionPending, ConnectionTrue } from "../icons";
import {
AWSProviderBadge,
AzureProviderBadge,
GCPProviderBadge,
KS8ProviderBadge,
} from "../icons/providers-badge";
interface ProviderInfoProps {
connected: boolean | null;
provider: "aws" | "azure" | "gcp" | "kubernetes";
providerAlias: string;
}
export const ProviderInfo: React.FC<ProviderInfoProps> = ({
connected,
provider,
providerAlias,
}) => {
const getIcon = () => {
switch (connected) {
case true:
return (
<div className="flex items-center justify-center rounded-medium border-2 border-system-success bg-system-success-lighter p-1">
<ConnectionTrue className="text-system-success" size={24} />
</div>
);
case false:
return (
<div className="flex items-center justify-center rounded-medium border-2 border-danger bg-system-error-lighter p-1">
<ConnectionFalse className="text-danger" size={24} />
</div>
);
case null:
return (
<div className="bg-info-lighter border-info-lighter flex items-center justify-center rounded-medium border p-1">
<ConnectionPending className="text-info" size={24} />
</div>
);
default:
return <ConnectionPending size={24} />;
}
};
const getProviderLogo = () => {
switch (provider) {
case "aws":
return <AWSProviderBadge width={35} height={35} />;
case "azure":
return <AzureProviderBadge width={35} height={35} />;
case "gcp":
return <GCPProviderBadge width={35} height={35} />;
case "kubernetes":
return <KS8ProviderBadge width={35} height={35} />;
default:
return null;
}
};
return (
<div className="max-w-48">
<div className="flex items-center justify-between space-x-4">
<div className="flex items-center space-x-4">
<div className="flex-shrink-0">{getProviderLogo()}</div>
<div className="flex-shrink-0">{getIcon()}</div>
<div className="flex flex-col">
<span className="text-md max-w-24 overflow-hidden text-ellipsis font-semibold lg:max-w-36">
{providerAlias}
</span>
{/* <CustomLoader size="small" /> */}
</div>
</div>
</div>
</div>
);
};
@@ -0,0 +1,75 @@
"use client";
import { RadioGroup } from "@nextui-org/react";
import React from "react";
import { Control, Controller } from "react-hook-form";
import { z } from "zod";
import { addProviderFormSchema } from "@/types";
import { AWSProviderBadge, AzureProviderBadge } from "../icons/providers-badge";
import { GCPProviderBadge } from "../icons/providers-badge/GCPProviderBadge";
import { KS8ProviderBadge } from "../icons/providers-badge/KS8ProviderBadge";
import { CustomRadio } from "../ui/custom";
import { FormMessage } from "../ui/form";
interface RadioGroupProviderProps {
control: Control<z.infer<typeof addProviderFormSchema>>;
isInvalid: boolean;
errorMessage?: string;
}
export const RadioGroupProvider: React.FC<RadioGroupProviderProps> = ({
control,
isInvalid,
errorMessage,
}) => {
return (
<Controller
name="providerType"
control={control}
render={({ field }) => (
<>
<RadioGroup
className="flex flex-wrap"
isInvalid={isInvalid}
{...field}
value={field.value || ""}
>
<div className="flex flex-col gap-4">
<CustomRadio description="Amazon Web Services" value="aws">
<div className="flex items-center">
<AWSProviderBadge size={26} />
<span className="ml-2">Amazon Web Services</span>
</div>
</CustomRadio>
<CustomRadio description="Google Cloud Platform" value="gcp">
<div className="flex items-center">
<GCPProviderBadge size={26} />
<span className="ml-2">Google Cloud Platform</span>
</div>
</CustomRadio>
<CustomRadio description="Microsoft Azure" value="azure">
<div className="flex items-center">
<AzureProviderBadge size={26} />
<span className="ml-2">Microsoft Azure</span>
</div>
</CustomRadio>
<CustomRadio description="Kubernetes" value="kubernetes">
<div className="flex items-center">
<KS8ProviderBadge size={26} />
<span className="ml-2">Kubernetes</span>
</div>
</CustomRadio>
</div>
</RadioGroup>
{errorMessage && (
<FormMessage className="text-system-error dark:text-system-error">
{errorMessage}
</FormMessage>
)}
</>
)}
/>
);
};
@@ -0,0 +1,97 @@
"use client";
import { ColumnDef } from "@tanstack/react-table";
import { DateWithTime, SnippetId } from "@/components/ui/entities";
import { DataTableColumnHeader, StatusBadge } from "@/components/ui/table";
import { ProviderProps } from "@/types";
import { ProviderInfo } from "../provider-info";
import { DataTableRowActions } from "./data-table-row-actions";
const getProviderData = (row: { original: ProviderProps }) => {
return row.original;
};
export const ColumnProviders: ColumnDef<ProviderProps>[] = [
// {
// header: " ",
// cell: ({ row }) => <p className="text-medium">{row.index + 1}</p>,
// },
{
accessorKey: "account",
header: ({ column }) => (
<DataTableColumnHeader column={column} title={"Account"} param="alias" />
),
cell: ({ row }) => {
const {
attributes: { connection, provider, alias },
} = getProviderData(row);
return (
<ProviderInfo
connected={connection.connected}
provider={provider}
providerAlias={alias}
/>
);
},
},
{
accessorKey: "uid",
header: ({ column }) => (
<DataTableColumnHeader column={column} title={"Id"} param="uid" />
),
cell: ({ row }) => {
const {
attributes: { uid },
} = getProviderData(row);
return <SnippetId className="h-7 max-w-48" entityId={uid} />;
},
},
{
accessorKey: "status",
header: "Scan Status",
cell: () => {
// Temporarily overwriting the value until the API is functional.
return <StatusBadge status={"completed"} />;
},
},
{
accessorKey: "lastScan",
header: ({ column }) => (
<DataTableColumnHeader
column={column}
title={"Last Scan"}
param="updated_at"
/>
),
cell: ({ row }) => {
const {
attributes: { updated_at },
} = getProviderData(row);
return <DateWithTime dateTime={updated_at} />;
},
},
{
accessorKey: "added",
header: ({ column }) => (
<DataTableColumnHeader
column={column}
title={"Added"}
param="inserted_at"
/>
),
cell: ({ row }) => {
const {
attributes: { inserted_at },
} = getProviderData(row);
return <DateWithTime dateTime={inserted_at} showTime={false} />;
},
},
{
id: "actions",
cell: ({ row }) => {
return <DataTableRowActions row={row} />;
},
},
];
@@ -0,0 +1,121 @@
"use client";
import {
Button,
Dropdown,
DropdownItem,
DropdownMenu,
DropdownSection,
DropdownTrigger,
} from "@nextui-org/react";
import {
AddNoteBulkIcon,
DeleteDocumentBulkIcon,
EditDocumentBulkIcon,
} from "@nextui-org/shared-icons";
import { Row } from "@tanstack/react-table";
import clsx from "clsx";
import { useState } from "react";
import { VerticalDotsIcon } from "@/components/icons";
import { CustomAlertModal } from "@/components/ui/custom";
import { EditForm } from "../forms";
import { DeleteForm } from "../forms/delete-form";
interface DataTableRowActionsProps<ProviderProps> {
row: Row<ProviderProps>;
}
const iconClasses =
"text-2xl text-default-500 pointer-events-none flex-shrink-0";
export function DataTableRowActions<ProviderProps>({
row,
}: DataTableRowActionsProps<ProviderProps>) {
const [isEditOpen, setIsEditOpen] = useState(false);
const [isDeleteOpen, setIsDeleteOpen] = useState(false);
const providerId = (row.original as { id: string }).id;
const providerType = (row.original as any).attributes?.provider;
const providerAlias = (row.original as any).attributes?.alias;
return (
<>
<CustomAlertModal
isOpen={isEditOpen}
onOpenChange={setIsEditOpen}
title="Edit Provider"
description={"Edit the provider details"}
>
<EditForm
providerId={providerId}
providerAlias={providerAlias}
setIsOpen={setIsEditOpen}
/>
</CustomAlertModal>
<CustomAlertModal
isOpen={isDeleteOpen}
onOpenChange={setIsDeleteOpen}
title="Are you absolutely sure?"
description="This action cannot be undone. This will permanently delete your provider account and remove your data from the server."
>
<DeleteForm providerId={providerId} setIsOpen={setIsDeleteOpen} />
</CustomAlertModal>
<div className="relative flex items-center justify-end gap-2">
<Dropdown
className="shadow-xl dark:bg-prowler-blue-800"
placement="bottom"
>
<DropdownTrigger>
<Button isIconOnly radius="full" size="sm" variant="light">
<VerticalDotsIcon className="text-default-400" />
</Button>
</DropdownTrigger>
<DropdownMenu
closeOnSelect
aria-label="Actions"
color="default"
variant="flat"
>
<DropdownSection title="Actions">
<DropdownItem
href={`/providers/test-connection?type=${providerType}&id=${providerId}`}
key="new"
description="Check the connection to the provider"
textValue="Check Connection"
startContent={<AddNoteBulkIcon className={iconClasses} />}
>
Test Connection
</DropdownItem>
<DropdownItem
key="edit"
description="Allows you to edit the provider"
textValue="Edit Provider"
startContent={<EditDocumentBulkIcon className={iconClasses} />}
onClick={() => setIsEditOpen(true)}
>
Edit Provider
</DropdownItem>
</DropdownSection>
<DropdownSection title="Danger zone">
<DropdownItem
key="delete"
className="text-danger"
color="danger"
description="Delete the provider permanently"
textValue="Delete Provider"
startContent={
<DeleteDocumentBulkIcon
className={clsx(iconClasses, "!text-danger")}
/>
}
onClick={() => setIsDeleteOpen(true)}
>
Delete Provider
</DropdownItem>
</DropdownSection>
</DropdownMenu>
</Dropdown>
</div>
</>
);
}
+3
View File
@@ -0,0 +1,3 @@
export * from "./column-providers";
export * from "./data-table-row-actions";
export * from "./skeleton-table-provider";
@@ -0,0 +1,65 @@
import { Card, Skeleton } from "@nextui-org/react";
import React from "react";
export const SkeletonTableProviders = () => {
return (
<Card className="h-full w-full space-y-5 p-4" radius="sm">
{/* Table headers */}
<div className="hidden justify-between md:flex">
<Skeleton className="w-1/12 rounded-lg">
<div className="h-8 bg-default-200"></div>
</Skeleton>
<Skeleton className="w-2/12 rounded-lg">
<div className="h-8 bg-default-200"></div>
</Skeleton>
<Skeleton className="w-2/12 rounded-lg">
<div className="h-8 bg-default-200"></div>
</Skeleton>
<Skeleton className="w-2/12 rounded-lg">
<div className="h-8 bg-default-200"></div>
</Skeleton>
<Skeleton className="w-2/12 rounded-lg">
<div className="h-8 bg-default-200"></div>
</Skeleton>
<Skeleton className="w-1/12 rounded-lg">
<div className="h-8 bg-default-200"></div>
</Skeleton>
<Skeleton className="w-1/12 rounded-lg">
<div className="h-8 bg-default-200"></div>
</Skeleton>
</div>
{/* Table body */}
<div className="space-y-3">
{[...Array(3)].map((_, index) => (
<div
key={index}
className="flex flex-col items-center justify-between space-x-0 md:flex-row md:space-x-4"
>
<Skeleton className="mb-2 w-full rounded-lg md:mb-0 md:w-1/12">
<div className="h-12 bg-default-300"></div>
</Skeleton>
<Skeleton className="mb-2 w-full rounded-lg md:mb-0 md:w-2/12">
<div className="h-12 bg-default-300"></div>
</Skeleton>
<Skeleton className="mb-2 hidden rounded-lg sm:flex md:mb-0 md:w-2/12">
<div className="h-12 bg-default-300"></div>
</Skeleton>
<Skeleton className="mb-2 hidden rounded-lg sm:flex md:mb-0 md:w-2/12">
<div className="h-12 bg-default-300"></div>
</Skeleton>
<Skeleton className="mb-2 hidden rounded-lg sm:flex md:mb-0 md:w-2/12">
<div className="h-12 bg-default-300"></div>
</Skeleton>
<Skeleton className="mb-2 hidden rounded-lg sm:flex md:mb-0 md:w-1/12">
<div className="h-12 bg-default-300"></div>
</Skeleton>
<Skeleton className="mb-2 hidden rounded-lg sm:flex md:mb-0 md:w-1/12">
<div className="h-12 bg-default-300"></div>
</Skeleton>
</div>
))}
</div>
</Card>
);
};
@@ -0,0 +1,226 @@
"use client";
import { zodResolver } from "@hookform/resolvers/zod";
import { ChevronLeftIcon, ChevronRightIcon, SaveIcon } from "lucide-react";
import { useRouter } from "next/navigation";
import { useState } from "react";
import { useForm } from "react-hook-form";
import * as z from "zod";
import { useToast } from "@/components/ui";
import { CustomButton, CustomInput } from "@/components/ui/custom";
import { Form } from "@/components/ui/form";
import { addProvider } from "../../../../actions/providers/providers";
import { addProviderFormSchema, ApiError } from "../../../../types";
import { RadioGroupProvider } from "../../radio-group-provider";
import { RadioGroupAWSViaCredentialsForm } from "./radio-group-aws-via-credentials-form";
export type FormValues = z.infer<typeof addProviderFormSchema>;
export const ConnectAccountForm = () => {
const { toast } = useToast();
const [prevStep, setPrevStep] = useState(1);
const router = useRouter();
const formSchema = addProviderFormSchema;
const form = useForm<FormValues>({
resolver: zodResolver(formSchema),
defaultValues: {
providerType: undefined,
providerUid: "",
providerAlias: "",
awsCredentialsType: "",
},
});
const providerType = form.watch("providerType");
const isLoading = form.formState.isSubmitting;
const onSubmitClient = async (values: FormValues) => {
const formValues = { ...values };
// If providerAlias is empty, set default value
if (!formValues.providerAlias.trim()) {
const date = new Date();
const month = (date.getMonth() + 1).toString().padStart(2, "0");
const day = date.getDate().toString().padStart(2, "0");
const year = date.getFullYear();
formValues.providerAlias = `${formValues.providerType}:${month}/${day}/${year}`;
}
const formData = new FormData();
Object.entries(formValues).forEach(
([key, value]) => value !== undefined && formData.append(key, value),
);
const data = await addProvider(formData);
if (data?.errors && data.errors.length > 0) {
data.errors.forEach((error: ApiError) => {
const errorMessage = error.detail;
const pointer = error.source?.pointer;
switch (pointer) {
case "/data/attributes/provider":
form.setError("providerType", {
type: "server",
message: errorMessage,
});
break;
case "/data/attributes/uid":
case "/data/attributes/__all__":
form.setError("providerUid", {
type: "server",
message: errorMessage,
});
break;
case "/data/attributes/alias":
form.setError("providerAlias", {
type: "server",
message: errorMessage,
});
break;
default:
toast({
variant: "destructive",
title: "Oops! Something went wrong",
description: errorMessage,
});
}
});
setPrevStep(1);
} else {
const {
id,
attributes: { provider: providerType },
} = data.data;
const credentialsParam = values.awsCredentialsType
? `&via=${values.awsCredentialsType}`
: "";
router.push(
`/providers/add-credentials?type=${providerType}&id=${id}${credentialsParam}`,
);
}
};
const handleNextStep = () => {
setPrevStep((prev) => prev + 1);
};
const handleBackStep = () => {
setPrevStep((prev) => prev - 1);
};
return (
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmitClient)}
className="flex flex-col space-y-4"
>
{prevStep === 1 && (
<>
{/* Select a provider */}
<RadioGroupProvider
control={form.control}
isInvalid={!!form.formState.errors.providerType}
errorMessage={form.formState.errors.providerType?.message}
/>
{/* Provider UID */}
<CustomInput
control={form.control}
name="providerUid"
type="text"
label="Provider UID"
labelPlacement="inside"
placeholder={"Enter the provider UID"}
variant="bordered"
isRequired
isInvalid={!!form.formState.errors.providerUid}
/>
{/* Provider alias */}
<CustomInput
control={form.control}
name="providerAlias"
type="text"
label="Provider alias (optional)"
labelPlacement="inside"
placeholder={"Enter the provider alias"}
variant="bordered"
isRequired={false}
isInvalid={!!form.formState.errors.providerAlias}
/>
</>
)}
{prevStep === 2 && (
<>
{/* Select AWS credentials type */}
<RadioGroupAWSViaCredentialsForm
control={form.control}
isInvalid={!!form.formState.errors.awsCredentialsType}
errorMessage={form.formState.errors.awsCredentialsType?.message}
/>
</>
)}
<div className="flex w-full justify-end sm:space-x-6">
{prevStep === 2 && (
<CustomButton
type="button"
ariaLabel="Back"
className="w-1/2 bg-transparent"
variant="faded"
size="lg"
radius="lg"
onPress={handleBackStep}
startContent={!isLoading && <ChevronLeftIcon size={24} />}
isDisabled={isLoading}
>
<span>Back</span>
</CustomButton>
)}
<CustomButton
type="button"
ariaLabel={
prevStep === 1 && providerType === "aws" ? "Next" : "Save"
}
className="w-1/2"
variant="solid"
color="action"
size="lg"
isLoading={isLoading}
startContent={
!isLoading &&
!(prevStep === 1 && providerType === "aws") && (
<SaveIcon size={24} />
)
}
endContent={
!isLoading &&
prevStep === 1 &&
providerType === "aws" && <ChevronRightIcon size={24} />
}
onPress={() => {
if (prevStep === 1 && providerType === "aws") {
handleNextStep();
} else {
form.handleSubmit(onSubmitClient)();
}
}}
>
{isLoading ? (
<>Loading</>
) : (
<span>
{prevStep === 1 && providerType === "aws" ? "Next" : "Save"}
</span>
)}
</CustomButton>
</div>
</form>
</Form>
);
};
@@ -0,0 +1,6 @@
export * from "./connect-account-form";
export * from "./launch-scan-form";
export * from "./radio-group-aws-via-credentials-form";
export * from "./test-connection-form";
export * from "./via-credentials-form";
export * from "./via-role-form";
@@ -0,0 +1,162 @@
"use client";
import { zodResolver } from "@hookform/resolvers/zod";
import { useRouter } from "next/navigation";
import { useState } from "react";
import { useForm } from "react-hook-form";
import { z } from "zod";
import { scanOnDemand } from "@/actions/scans/scans";
import { AddIcon } from "@/components/icons";
import { CustomButton } from "@/components/ui/custom";
import { Form } from "@/components/ui/form";
import { ProviderProps } from "@/types"; // Asegúrate de importar la interfaz correcta
import { launchScanFormSchema } from "@/types/formSchemas";
import { ProviderInfo } from "../../provider-info";
type FormValues = z.infer<ReturnType<typeof launchScanFormSchema>>;
interface LaunchScanFormProps {
searchParams: { type: string; id: string };
providerData: {
data: {
type: string;
id: string;
attributes: ProviderProps["attributes"];
};
};
}
export const LaunchScanForm = ({
searchParams,
providerData,
}: LaunchScanFormProps) => {
const providerType = searchParams.type;
const providerId = searchParams.id;
const [apiErrorMessage, setApiErrorMessage] = useState<string | null>(null);
const router = useRouter();
const formSchema = launchScanFormSchema();
const form = useForm<FormValues>({
resolver: zodResolver(formSchema),
defaultValues: {
providerId,
providerType,
scannerArgs: {
checksToExecute: [],
},
},
});
// const isLoading = form.formState.isSubmitting;
const onSubmitClient = async (values: FormValues) => {
const formData = new FormData();
formData.append("providerId", values.providerId);
// Generate default scan name using provider type and current date
const date = new Date();
const month = (date.getMonth() + 1).toString().padStart(2, "0");
const day = date.getDate().toString().padStart(2, "0");
const year = date.getFullYear();
const defaultScanName = `${providerType}:${month}/${day}/${year}`;
formData.append("scanName", defaultScanName);
try {
const data = await scanOnDemand(formData);
if (data.error) {
setApiErrorMessage(data.error);
form.setError("providerId", {
type: "server",
message: data.error,
});
} else {
router.push("/scans");
}
} catch (error) {
form.setError("providerId", {
type: "server",
message: "An unexpected error occurred. Please try again.",
});
}
};
return (
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmitClient)}
className="flex flex-col space-y-4"
>
<div className="text-left">
<div className="text-2xl font-bold leading-9 text-default-foreground">
Scan started
</div>
<div className="py-2 text-default-500">
The scan has just started. From now on, a new scan will be launched
every 24 hours, starting from this moment.
</div>
</div>
{apiErrorMessage && (
<div className="mt-4 rounded-md bg-red-100 p-3 text-red-700">
<p>{apiErrorMessage.toLowerCase()}</p>
</div>
)}
<ProviderInfo
connected={providerData.data.attributes.connection.connected}
provider={providerData.data.attributes.provider}
providerAlias={providerData.data.attributes.alias}
/>
<input type="hidden" name="providerId" value={providerId} />
<input type="hidden" name="providerType" value={providerType} />
{/* <div className="flex w-full justify-end sm:space-x-6">
<CustomButton
type="submit"
ariaLabel={"Save"}
className="w-1/2"
variant="solid"
color="action"
size="lg"
isLoading={isLoading}
startContent={!isLoading && <ScheduleIcon size={24} />}
isDisabled={true}
>
<span>Schedule</span>
</CustomButton>
<CustomButton
type="submit"
ariaLabel={"Save"}
className="w-1/2"
variant="solid"
color="action"
size="lg"
isLoading={isLoading}
endContent={!isLoading && <RocketIcon size={24} />}
>
{isLoading ? <>Loading</> : <span>Start now</span>}
</CustomButton>
</div> */}
<div className="flex w-full items-center justify-end">
<CustomButton
asLink="/scans"
ariaLabel="Go to Scans page"
variant="solid"
color="action"
size="md"
endContent={<AddIcon size={20} />}
>
Go to Scans
</CustomButton>
</div>
</form>
</Form>
);
};
@@ -0,0 +1,64 @@
"use client";
import { RadioGroup } from "@nextui-org/react";
import React from "react";
import { Control, Controller } from "react-hook-form";
import { CustomRadio } from "@/components/ui/custom";
import { FormMessage } from "@/components/ui/form";
import { FormValues } from "./connect-account-form";
type RadioGroupAWSViaCredentialsFormProps = {
control: Control<FormValues>;
isInvalid: boolean;
errorMessage?: string;
};
export const RadioGroupAWSViaCredentialsForm = ({
control,
isInvalid,
errorMessage,
}: RadioGroupAWSViaCredentialsFormProps) => {
return (
<Controller
name="awsCredentialsType"
control={control}
render={({ field }) => (
<>
<RadioGroup
className="flex flex-wrap"
isInvalid={isInvalid}
{...field}
value={field.value || ""}
>
<div className="flex flex-col gap-4">
<span className="text-sm text-default-500">Using IAM Role</span>
<CustomRadio description="Connect assuming IAM Role" value="role">
<div className="flex items-center">
<span className="ml-2">Connect assuming IAM Role</span>
</div>
</CustomRadio>
<span className="text-sm text-default-500">
Using Credentials
</span>
<CustomRadio
description="Connect via Credentials"
value="credentials"
>
<div className="flex items-center">
<span className="ml-2">Connect via Credentials</span>
</div>
</CustomRadio>
</div>
</RadioGroup>
{errorMessage && (
<FormMessage className="text-system-error dark:text-system-error">
{errorMessage}
</FormMessage>
)}
</>
)}
/>
);
};
@@ -0,0 +1,286 @@
"use client";
import { zodResolver } from "@hookform/resolvers/zod";
import { Icon } from "@iconify/react";
import Link from "next/link";
import { useRouter } from "next/navigation";
import { useState } from "react";
import { useForm } from "react-hook-form";
import { z } from "zod";
import {
checkConnectionProvider,
deleteCredentials,
} from "@/actions/providers";
import { scanOnDemand } from "@/actions/scans";
import { getTask } from "@/actions/task/tasks";
import { CheckIcon, SaveIcon } from "@/components/icons";
import { useToast } from "@/components/ui";
import { CustomButton } from "@/components/ui/custom";
import { Form } from "@/components/ui/form";
import { checkTaskStatus } from "@/lib/helper";
import { ApiError, testConnectionFormSchema } from "@/types";
import { ProviderInfo } from "../..";
type FormValues = z.infer<typeof testConnectionFormSchema>;
export const TestConnectionForm = ({
searchParams,
providerData,
}: {
searchParams: { type: string; id: string };
providerData: {
data: {
id: string;
type: string;
attributes: {
connection: {
connected: boolean | null;
last_checked_at: string | null;
};
provider: "aws" | "azure" | "gcp" | "kubernetes";
alias: string;
scanner_args: Record<string, any>;
};
relationships: {
secret: {
data: {
type: string;
id: string;
} | null;
};
};
};
};
}) => {
const { toast } = useToast();
const router = useRouter();
const providerType = searchParams.type;
const providerId = searchParams.id;
console.log({ providerData }, "providerData from test connection form");
const formSchema = testConnectionFormSchema;
const [apiErrorMessage, setApiErrorMessage] = useState<string | null>(null);
const [connectionStatus, setConnectionStatus] = useState<{
connected: boolean;
error: string | null;
} | null>(null);
const [isResettingCredentials, setIsResettingCredentials] = useState(false);
const form = useForm<FormValues>({
resolver: zodResolver(formSchema),
defaultValues: {
providerId,
},
});
const isLoading = form.formState.isSubmitting;
const onSubmitClient = async (values: FormValues) => {
console.log({ values }, "values from test connection form");
const formData = new FormData();
formData.append("providerId", values.providerId);
const data = await checkConnectionProvider(formData);
if (data?.errors && data.errors.length > 0) {
data.errors.forEach((error: ApiError) => {
const errorMessage = error.detail;
switch (errorMessage) {
case "Not found.":
setApiErrorMessage(errorMessage);
break;
default:
toast({
variant: "destructive",
title: `Error ${error.status}`,
description: errorMessage,
});
}
});
} else {
const taskId = data.data.id;
setApiErrorMessage(null);
// Use the helper function to check the task status
const taskResult = await checkTaskStatus(taskId);
if (taskResult.completed) {
const task = await getTask(taskId);
const { connected, error } = task.data.attributes.result;
setConnectionStatus({
connected,
error: connected ? null : error || "Unknown error",
});
if (connected) {
try {
const data = await scanOnDemand(formData);
if (data.error) {
setApiErrorMessage(data.error);
form.setError("providerId", {
type: "server",
message: data.error,
});
} else {
router.push(
`/providers/launch-scan?type=${providerType}&id=${providerId}`,
);
}
} catch (error) {
form.setError("providerId", {
type: "server",
message: "An unexpected error occurred. Please try again.",
});
}
} else {
setConnectionStatus({
connected: false,
error: error || "Connection failed, please review credentials.",
});
}
} else {
setConnectionStatus({
connected: false,
error: taskResult.error || "Unknown error",
});
}
}
};
const onResetCredentials = async () => {
setIsResettingCredentials(true);
// Check if provider the provider has no credentials
const providerSecretId =
providerData?.data?.relationships?.secret?.data?.id;
const hasNoCredentials = !providerSecretId;
if (hasNoCredentials) {
// If no credentials, redirect to add credentials page
router.push(
`/providers/add-credentials?type=${providerType}&id=${providerId}`,
);
return;
}
// If provider has credentials, delete them first
try {
await deleteCredentials(providerSecretId);
// After successful deletion, redirect to add credentials page
router.push(
`/providers/add-credentials?type=${providerType}&id=${providerId}`,
);
} catch (error) {
console.error("Failed to delete credentials:", error);
} finally {
setIsResettingCredentials(false);
}
};
return (
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmitClient)}
className="flex flex-col space-y-4"
>
<div className="text-left">
<div className="text-2xl font-bold leading-9 text-default-foreground">
Test connection
</div>
<p className="py-2 text-default-500">
Ensure all required credentials and configurations are completed
accurately. A successful connection will enable the option to
initiate a scan in the following step.
</p>
</div>
{apiErrorMessage && (
<div className="mt-4 rounded-md bg-red-100 p-3 text-danger">
<p>{`Provider ID ${apiErrorMessage.toLowerCase()}. Please check and try again.`}</p>
</div>
)}
{connectionStatus && !connectionStatus.connected && (
<>
<div className="flex items-center gap-4 rounded-lg border border-red-200 bg-red-50 p-4">
<div className="flex items-center">
<Icon
icon="heroicons:exclamation-circle"
className="h-5 w-5 text-danger"
/>
</div>
<div className="flex items-center">
<p className="text-danger">
{connectionStatus.error || "Unknown error"}
</p>
</div>
</div>
<p className="text-md text-danger">
It seems there was an issue with your credentials. Please review
your credentials and try again.
</p>
</>
)}
<ProviderInfo
connected={providerData.data.attributes.connection.connected}
provider={providerData.data.attributes.provider}
providerAlias={providerData.data.attributes.alias}
/>
<input type="hidden" name="providerId" value={providerId} />
<div className="flex w-full justify-end sm:space-x-6">
{apiErrorMessage ? (
<Link
href="/providers"
className="mr-3 flex w-fit items-center justify-center space-x-2 rounded-lg border border-solid border-gray-200 px-4 py-2 hover:bg-gray-200 dark:hover:bg-gray-700"
>
<Icon
icon="icon-park-outline:close-small"
className="h-5 w-5 text-gray-600 dark:text-gray-400"
/>
<span>Back to providers</span>
</Link>
) : connectionStatus?.error ? (
<CustomButton
onPress={onResetCredentials}
type="button"
ariaLabel={"Save"}
className="w-1/2"
variant="solid"
color="warning"
size="lg"
isLoading={isResettingCredentials}
startContent={!isResettingCredentials && <CheckIcon size={24} />}
isDisabled={isResettingCredentials}
>
{isResettingCredentials ? (
<>Loading</>
) : (
<span>Reset credentials</span>
)}
</CustomButton>
) : (
<CustomButton
type="submit"
ariaLabel={"Save"}
className="w-1/2"
variant="solid"
color="action"
size="lg"
isLoading={isLoading}
startContent={!isLoading && <SaveIcon size={24} />}
>
{isLoading ? <>Loading</> : <span>Test connection</span>}
</CustomButton>
)}
</div>
</form>
</Form>
);
};
@@ -0,0 +1,207 @@
"use client";
import { zodResolver } from "@hookform/resolvers/zod";
import { SaveIcon } from "lucide-react";
import { useRouter } from "next/navigation";
import { Control, useForm } from "react-hook-form";
import * as z from "zod";
import { addCredentialsProvider } from "@/actions/providers/providers";
import { useToast } from "@/components/ui";
import { CustomButton } from "@/components/ui/custom";
import { Form } from "@/components/ui/form";
import {
addCredentialsFormSchema,
ApiError,
AWSCredentials,
AzureCredentials,
GCPCredentials,
KubernetesCredentials,
} from "@/types";
import { AWScredentialsForm } from "./via-credentials/aws-credentials-form";
import { AzureCredentialsForm } from "./via-credentials/azure-credentials-form";
import { GCPcredentialsForm } from "./via-credentials/gcp-credentials-form";
import { KubernetesCredentialsForm } from "./via-credentials/k8s-credentials-form";
type CredentialsFormSchema = z.infer<
ReturnType<typeof addCredentialsFormSchema>
>;
// Add this type intersection to include all fields
type FormType = CredentialsFormSchema &
AWSCredentials &
AzureCredentials &
GCPCredentials &
KubernetesCredentials;
export const ViaCredentialsForm = ({
searchParams,
}: {
searchParams: { type: string; id: string };
}) => {
const router = useRouter();
const { toast } = useToast();
const providerType = searchParams.type;
const providerId = searchParams.id;
const formSchema = addCredentialsFormSchema(providerType);
const form = useForm<FormType>({
resolver: zodResolver(formSchema),
defaultValues: {
providerId,
providerType,
...(providerType === "aws"
? {
aws_access_key_id: "",
aws_secret_access_key: "",
aws_session_token: "",
}
: providerType === "azure"
? {
client_id: "",
client_secret: "",
tenant_id: "",
}
: providerType === "gcp"
? {
client_id: "",
client_secret: "",
refresh_token: "",
}
: providerType === "kubernetes"
? {
kubeconfig_content: "",
}
: {}),
},
});
const isLoading = form.formState.isSubmitting;
const onSubmitClient = async (values: FormType) => {
console.log("via credentials form", values);
const formData = new FormData();
Object.entries(values).forEach(
([key, value]) => value !== undefined && formData.append(key, value),
);
const data = await addCredentialsProvider(formData);
if (data?.errors && data.errors.length > 0) {
data.errors.forEach((error: ApiError) => {
const errorMessage = error.detail;
switch (error.source.pointer) {
case "/data/attributes/secret/aws_access_key_id":
form.setError("aws_access_key_id", {
type: "server",
message: errorMessage,
});
break;
case "/data/attributes/secret/aws_secret_access_key":
form.setError("aws_secret_access_key", {
type: "server",
message: errorMessage,
});
break;
case "/data/attributes/secret/aws_session_token":
form.setError("aws_session_token", {
type: "server",
message: errorMessage,
});
break;
case "/data/attributes/secret/client_id":
form.setError("client_id", {
type: "server",
message: errorMessage,
});
break;
case "/data/attributes/secret/client_secret":
form.setError("client_secret", {
type: "server",
message: errorMessage,
});
break;
case "/data/attributes/secret/tenant_id":
form.setError("tenant_id", {
type: "server",
message: errorMessage,
});
break;
case "/data/attributes/secret/kubeconfig_content":
form.setError("kubeconfig_content", {
type: "server",
message: errorMessage,
});
break;
case "/data/attributes/name":
form.setError("secretName", {
type: "server",
message: errorMessage,
});
break;
default:
toast({
variant: "destructive",
title: "Oops! Something went wrong",
description: errorMessage,
});
}
});
} else {
router.push(
`/providers/test-connection?type=${providerType}&id=${providerId}`,
);
}
};
return (
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmitClient)}
className="flex flex-col space-y-4"
>
<input type="hidden" name="providerId" value={providerId} />
<input type="hidden" name="providerType" value={providerType} />
{providerType === "aws" && (
<AWScredentialsForm
control={form.control as unknown as Control<AWSCredentials>}
/>
)}
{providerType === "azure" && (
<AzureCredentialsForm
control={form.control as unknown as Control<AzureCredentials>}
/>
)}
{providerType === "gcp" && (
<GCPcredentialsForm
control={form.control as unknown as Control<GCPCredentials>}
/>
)}
{providerType === "kubernetes" && (
<KubernetesCredentialsForm
control={form.control as unknown as Control<KubernetesCredentials>}
/>
)}
<div className="flex w-full justify-end sm:space-x-6">
<CustomButton
type="submit"
ariaLabel={"Save"}
className="w-1/2"
variant="solid"
color="action"
size="lg"
isLoading={isLoading}
startContent={!isLoading && <SaveIcon size={24} />}
>
{isLoading ? <>Loading</> : <span>Save</span>}
</CustomButton>
</div>
</form>
</Form>
);
};
@@ -0,0 +1,56 @@
import { Control } from "react-hook-form";
import { CustomInput } from "@/components/ui/custom";
import { AWSCredentials } from "@/types";
export const AWScredentialsForm = ({
control,
}: {
control: Control<AWSCredentials>;
}) => {
return (
<>
<div className="text-left">
<div className="text-2xl font-bold leading-9 text-default-foreground">
Connect via Credentials
</div>
<div className="py-2 text-default-500">
Please provide the information for your AWS credentials.
</div>
</div>
<CustomInput
control={control}
name="aws_access_key_id"
type="password"
label="AWS Access Key ID"
labelPlacement="inside"
placeholder="Enter the AWS Access Key ID"
variant="bordered"
isRequired
isInvalid={!!control._formState.errors.aws_access_key_id}
/>
<CustomInput
control={control}
name="aws_secret_access_key"
type="password"
label="AWS Secret Access Key"
labelPlacement="inside"
placeholder="Enter the AWS Secret Access Key"
variant="bordered"
isRequired
isInvalid={!!control._formState.errors.aws_secret_access_key}
/>
<CustomInput
control={control}
name="aws_session_token"
type="password"
label="AWS Session Token"
labelPlacement="inside"
placeholder="Enter the AWS Session Token"
variant="bordered"
isRequired={false}
isInvalid={!!control._formState.errors.aws_session_token}
/>
</>
);
};
@@ -0,0 +1,56 @@
import { Control } from "react-hook-form";
import { CustomInput } from "@/components/ui/custom";
import { AzureCredentials } from "@/types";
export const AzureCredentialsForm = ({
control,
}: {
control: Control<AzureCredentials>;
}) => {
return (
<>
<div className="text-left">
<div className="text-2xl font-bold leading-9 text-default-foreground">
Connect via Credentials
</div>
<div className="py-2 text-default-500">
Please provide the information for your Azure credentials.
</div>
</div>
<CustomInput
control={control}
name="client_id"
type="text"
label="Client ID"
labelPlacement="inside"
placeholder="Enter the Client ID"
variant="bordered"
isRequired
isInvalid={!!control._formState.errors.client_id}
/>
<CustomInput
control={control}
name="client_secret"
type="password"
label="Client Secret"
labelPlacement="inside"
placeholder="Enter the Client Secret"
variant="bordered"
isRequired
isInvalid={!!control._formState.errors.client_secret}
/>
<CustomInput
control={control}
name="tenant_id"
type="text"
label="Tenant ID"
labelPlacement="inside"
placeholder="Enter the Tenant ID"
variant="bordered"
isRequired
isInvalid={!!control._formState.errors.tenant_id}
/>
</>
);
};
@@ -0,0 +1,56 @@
import { Control } from "react-hook-form";
import { CustomInput } from "@/components/ui/custom";
import { GCPCredentials } from "@/types";
export const GCPcredentialsForm = ({
control,
}: {
control: Control<GCPCredentials>;
}) => {
return (
<>
<div className="text-left">
<div className="text-2xl font-bold leading-9 text-default-foreground">
Connect via Credentials
</div>
<div className="py-2 text-default-500">
Please provide the information for your GCP credentials.
</div>
</div>
<CustomInput
control={control}
name="client_id"
type="text"
label="Client ID"
labelPlacement="inside"
placeholder="Enter the Client ID"
variant="bordered"
isRequired
isInvalid={!!control._formState.errors.client_id}
/>
<CustomInput
control={control}
name="client_secret"
type="password"
label="Client Secret"
labelPlacement="inside"
placeholder="Enter the Client Secret"
variant="bordered"
isRequired
isInvalid={!!control._formState.errors.client_secret}
/>
<CustomInput
control={control}
name="refresh_token"
type="password"
label="Refresh Token"
labelPlacement="inside"
placeholder="Enter the Refresh Token"
variant="bordered"
isRequired
isInvalid={!!control._formState.errors.refresh_token}
/>
</>
);
};
@@ -0,0 +1,4 @@
export * from "./aws-credentials-form";
export * from "./azure-credentials-form";
export * from "./gcp-credentials-form";
export * from "./k8s-credentials-form";
@@ -0,0 +1,34 @@
import { Control } from "react-hook-form";
import { CustomInput } from "@/components/ui/custom";
import { KubernetesCredentials } from "@/types";
export const KubernetesCredentialsForm = ({
control,
}: {
control: Control<KubernetesCredentials>;
}) => {
return (
<>
<div className="text-left">
<div className="text-2xl font-bold leading-9 text-default-foreground">
Connect via Credentials
</div>
<div className="py-2 text-default-500">
Please provide the information for your Kubernetes credentials.
</div>
</div>
<CustomInput
control={control}
name="kubeconfig_content"
type="text"
label="Kubeconfig Content"
labelPlacement="inside"
placeholder="Enter the Kubeconfig Content"
variant="bordered"
isRequired
isInvalid={!!control._formState.errors.kubeconfig_content}
/>
</>
);
};
@@ -0,0 +1,125 @@
"use client";
import { zodResolver } from "@hookform/resolvers/zod";
import { SaveIcon } from "lucide-react";
import { useRouter } from "next/navigation";
import { Control, useForm } from "react-hook-form";
import * as z from "zod";
import { addCredentialsProvider } from "@/actions/providers/providers";
import { useToast } from "@/components/ui";
import { CustomButton } from "@/components/ui/custom";
import { Form } from "@/components/ui/form";
import {
addCredentialsRoleFormSchema,
ApiError,
AWSCredentialsRole,
} from "@/types";
import { AWSCredentialsRoleForm } from "./via-role/aws-role-form";
export const ViaRoleForm = ({
searchParams,
}: {
searchParams: { type: string; id: string };
}) => {
const router = useRouter();
const { toast } = useToast();
const providerType = searchParams.type;
const providerId = searchParams.id;
const formSchema = addCredentialsRoleFormSchema(providerType);
type FormSchemaType = z.infer<typeof formSchema>;
const form = useForm<FormSchemaType>({
resolver: zodResolver(formSchema),
defaultValues: {
providerId,
providerType,
...(providerType === "aws"
? {
role_arn: "",
aws_access_key_id: "",
aws_secret_access_key: "",
aws_session_token: "",
session_duration: 3600,
external_id: "",
role_session_name: "",
}
: {}),
},
});
const isLoading = form.formState.isSubmitting;
const onSubmitClient = async (values: FormSchemaType) => {
console.log("via ROLE form", values);
const formData = new FormData();
Object.entries(values).forEach(
([key, value]) =>
value !== undefined && formData.append(key, String(value)),
);
const data = await addCredentialsProvider(formData);
if (data?.errors && data.errors.length > 0) {
data.errors.forEach((error: ApiError) => {
const errorMessage = error.detail;
switch (error.source.pointer) {
case "/data/attributes/secret/role_arn":
form.setError("role_arn" as keyof FormSchemaType, {
type: "server",
message: errorMessage,
});
break;
default:
toast({
variant: "destructive",
title: "Oops! Something went wrong",
description: errorMessage,
});
}
});
} else {
router.push(
`/providers/test-connection?type=${providerType}&id=${providerId}`,
);
}
};
return (
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmitClient)}
className="flex flex-col space-y-4"
>
<input type="hidden" name="providerId" value={providerId} />
<input type="hidden" name="providerType" value={providerType} />
{providerType === "aws" && (
<AWSCredentialsRoleForm
control={form.control as unknown as Control<AWSCredentialsRole>}
/>
)}
<div className="flex w-full justify-end sm:space-x-6">
<CustomButton
type="submit"
ariaLabel={"Save"}
className="w-1/2"
variant="solid"
color="action"
size="lg"
isLoading={isLoading}
startContent={!isLoading && <SaveIcon size={24} />}
>
{isLoading ? <>Loading</> : <span>Save</span>}
</CustomButton>
</div>
</form>
</Form>
);
};
@@ -0,0 +1,105 @@
import { Control } from "react-hook-form";
import { CustomInput } from "@/components/ui/custom";
import { AWSCredentialsRole } from "@/types";
export const AWSCredentialsRoleForm = ({
control,
}: {
control: Control<AWSCredentialsRole>;
}) => {
return (
<>
<div className="mb-4 text-left">
<div className="text-2xl font-bold leading-9 text-default-foreground">
Connect assuming IAM Role
</div>
<div className="py-2 text-default-500">
Please provide the information for your AWS credentials.
</div>
</div>
<CustomInput
control={control}
name="role_arn"
type="text"
label="Role ARN"
labelPlacement="inside"
placeholder="Enter the Role ARN"
variant="bordered"
isRequired
isInvalid={!!control._formState.errors.role_arn}
/>
<span className="text-sm text-default-500">Optional fields</span>
<CustomInput
control={control}
name="aws_access_key_id"
type="password"
label="AWS Access Key ID"
labelPlacement="inside"
placeholder="Enter the AWS Access Key ID"
variant="bordered"
isRequired={false}
isInvalid={!!control._formState.errors.aws_access_key_id}
/>
<CustomInput
control={control}
name="aws_secret_access_key"
type="password"
label="AWS Secret Access Key"
labelPlacement="inside"
placeholder="Enter the AWS Secret Access Key"
variant="bordered"
isRequired={false}
isInvalid={!!control._formState.errors.aws_secret_access_key}
/>
<CustomInput
control={control}
name="aws_session_token"
type="password"
label="AWS Session Token"
labelPlacement="inside"
placeholder="Enter the AWS Session Token"
variant="bordered"
isRequired={false}
isInvalid={!!control._formState.errors.aws_session_token}
/>
<CustomInput
control={control}
name="external_id"
type="text"
label="External ID"
labelPlacement="inside"
placeholder="Enter the External ID"
variant="bordered"
isRequired={false}
isInvalid={!!control._formState.errors.external_id}
/>
<div className="mt-4 grid grid-cols-1 gap-4 md:grid-cols-2">
<CustomInput
control={control}
name="role_session_name"
type="text"
label="Role Session Name"
labelPlacement="inside"
placeholder="Enter the Role Session Name"
variant="bordered"
isRequired={false}
isInvalid={!!control._formState.errors.role_session_name}
/>
<CustomInput
control={control}
name="session_duration"
type="number"
label="Session Duration (seconds)"
labelPlacement="inside"
placeholder="Enter the session duration (default: 3600)"
variant="bordered"
isRequired={false}
isInvalid={!!control._formState.errors.session_duration}
/>
</div>
</>
);
};
@@ -0,0 +1 @@
export * from "./aws-role-form";
@@ -0,0 +1,2 @@
export * from "./vertical-steps";
export * from "./workflow-add-provider";
Loaded 100 of 274 files, more files were not shown because too many files have changed in this diff. Show more