import { useRef } from "react";
import { Check, ChevronDown, Copy, Link2, Loader2, Trash2, Users } from "lucide-react";
import { Button } from "@/client/components/ui/button";
import { DropdownPortal } from "@/client/components/ui/dropdown-portal";
import { isActionEnabled, isActionVisible } from "@/client/lib/affordance/action-state";
import { deriveShareDialogRowAffordance } from "@/client/lib/affordance/share-dialog";
import type { PageShare } from "@/shared/types";
import { useShareDialogShell, useShareLink, useSharePeople } from "./context";
import type { SharePermission, WorkspaceRole } from "./types";
function PermissionSelect({ value, onChange }: { value: SharePermission; onChange: (v: SharePermission) => void }) {
return (
);
}
export function SharePeopleSection() {
const { creating, dialogAffordance, workspaceRole, online, currentUserId, deleteShare } = useShareDialogShell();
const {
peopleInput,
peoplePermission,
showSuggestions,
userShares,
members,
filteredSuggestions,
hasMatchedMember,
memberName,
granteeName,
setPeopleInput,
setPeoplePermission,
openSuggestions,
dismissSuggestions,
selectMember,
submitPeopleShare,
} = useSharePeople();
// Member role cannot invite by free-form email; the worker rejects it.
// Surface that as an affordance: placeholder says "search", and submit
// stays disabled until the input resolves to an actual workspace member.
const placeholder = dialogAffordance.shareByEmail ? "Name or email..." : "Teammate...";
const ariaLabel = dialogAffordance.shareByEmail
? "Search people to share with"
: "Search workspace members to share with";
const submitGuardedByMemberMatch = !dialogAffordance.shareByEmail && !hasMatchedMember;
const submitDisabled =
creating || !peopleInput.trim() || !isActionEnabled(dialogAffordance.createUserShare) || submitGuardedByMemberMatch;
const submitTitle =
dialogAffordance.createUserShare.kind === "disabled"
? dialogAffordance.createUserShare.reason
: submitGuardedByMemberMatch
? "Members only"
: undefined;
const inputRef = useRef(null);
const suggestionsOpen = showSuggestions && filteredSuggestions.length > 0 && peopleInput.trim().length > 0;
return (
People
{suggestionsOpen && (
{filteredSuggestions.map((member) => (
))}
)}
{userShares.length > 0 ? (
{userShares.map((share) => (
member.user_id === share.grantee_id)}
onDelete={() => void deleteShare(share.id)}
/>
))}
) : (
No people shares
)}
);
}
export function ShareLinkSection() {
const { creating, dialogAffordance, workspaceRole, online, currentUserId, deleteShare } = useShareDialogShell();
const { linkPermission, linkShares, copiedId, setLinkPermission, createLinkShare, copyLink } = useShareLink();
return (
Link
{isActionVisible(dialogAffordance.createLinkShare) && (
)}
{linkShares.length > 0 ? (
{linkShares.map((share) => (
copyLink(share)}
onDelete={() => void deleteShare(share.id)}
/>
))}
) : (
isActionVisible(dialogAffordance.createLinkShare) && (
No link shares
)
)}
);
}
function ShareUserRow({
share,
label,
workspaceRole,
online,
currentUserId,
granteeIsWorkspaceMember,
onDelete,
}: {
share: PageShare;
label: string;
workspaceRole: WorkspaceRole;
online: boolean;
currentUserId: string | undefined;
granteeIsWorkspaceMember: boolean;
onDelete: () => void;
}) {
const rowAffordance = deriveShareDialogRowAffordance({
workspaceRole,
online,
currentUserId,
share,
granteeIsWorkspaceMember,
});
return (
{label}
{share.permission}
{isActionVisible(rowAffordance.revoke) && (
}
className="hover:text-red-400"
/>
)}
);
}
function ShareLinkRow({
share,
copiedId,
workspaceRole,
online,
currentUserId,
onCopy,
onDelete,
}: {
share: PageShare;
copiedId: string | null;
workspaceRole: WorkspaceRole;
online: boolean;
currentUserId: string | undefined;
onCopy: () => void;
onDelete: () => void;
}) {
const rowAffordance = deriveShareDialogRowAffordance({
workspaceRole,
online,
currentUserId,
share,
granteeIsWorkspaceMember: false,
});
return (
{share.permission} link
{isActionVisible(rowAffordance.copyLink) && (
) : (
)
}
/>
)}
{isActionVisible(rowAffordance.revoke) && (
}
className="hover:text-red-400"
/>
)}
);
}