"use client";
import { RefreshCw } from "lucide-react";
import { SlackInlineCode } from "@/components/integrations/slack/slack-inline-code";
import {
Alert,
AlertDescription,
AlertTitle,
Badge,
Button,
Label,
} from "@/components/shadcn";
import { CustomLink } from "@/components/shadcn/custom/custom-link";
import {
MultiSelect,
MultiSelectContent,
MultiSelectItem,
MultiSelectTrigger,
MultiSelectValue,
} from "@/components/shadcn/select/multiselect";
import { DOCS_URLS } from "@/lib/external-urls";
import type { SlackChannelOption } from "@/types/integrations";
const INVITE_HINT = (
<>
A private channel only appears here after someone invites{" "}
@Prowler Cloud to it in Slack. Invite it,
then refresh.{" "}
Learn more
>
);
interface SlackChannelMultiSelectProps {
options: SlackChannelOption[];
values: string[];
onChange: (channelIds: string[]) => void;
isLoading?: boolean;
/** Why the channels could not be read — Slack's own reason, when it gave one. */
error?: string | null;
/** Why the list is partial. Shown with the picker, not instead of it. */
incompleteNotice?: string | null;
onRefresh?: () => void;
disabled?: boolean;
id?: string;
/** Element explaining the picker — a caller's reason for disabling it. */
describedBy?: string;
}
/** Marked exactly as the listing marks it, so the chip is the row it came from. */
const chipLabel = (option: SlackChannelOption) => (
#{option.name}
{option.is_private && (
Private
)}
);
/** Driven entirely by props (design D1) so any consumer can reuse it. */
export const SlackChannelMultiSelect = ({
options,
values,
onChange,
isLoading = false,
error = null,
incompleteNotice = null,
onRefresh,
disabled = false,
id = "slack-channels",
describedBy,
}: SlackChannelMultiSelectProps) => {
const isEmpty = !isLoading && !error && options.length === 0;
// `htmlFor` may only name an element that exists, and the trigger is only
// rendered in the picker branch.
const hasPicker = !error && !isEmpty;
// A copy: the list belongs to the caller. Sorted here rather than upstream so
// every consumer of the picker offers the same order.
const listed = [...options].sort((left, right) =>
left.name.localeCompare(right.name, undefined, {
sensitivity: "base",
numeric: true,
}),
);
return (
{onRefresh && (
)}
{error ? (
Could not read the workspace's channels{error}
) : isEmpty ? (
No channels available yet
Prowler cannot see a single channel in this workspace. Create a
public channel, or invite{" "}
@Prowler Cloud to a private one
in Slack with{" "}
/invite @Prowler Cloud, then
refresh.
) : (
<>
{incompleteNotice && (
Not every channel is listed{incompleteNotice}
)}
{listed.map((option) => (
#{option.name}
{option.is_private && (
Private
)}
))}
>
)}