import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { FileStack, KeyRound, Pencil, Trash2, UserPlus, X } from 'lucide-react'; import { useState } from 'react'; import { AppTile } from '#/components/atoms/app-tile.jsx'; import { Badge } from '#/components/atoms/badge.jsx'; import { Button } from '#/components/atoms/button.jsx'; import { Card } from '#/components/atoms/card.jsx'; import { Link } from '#/components/atoms/link.jsx'; import { Notice } from '#/components/atoms/notice.jsx'; import { Skeleton } from '#/components/atoms/skeleton.jsx'; import { ActorAvatar } from '#/components/molecules/actor-avatar.jsx'; import { AtUri } from '#/components/molecules/at-uri.jsx'; import { Copyable } from '#/components/molecules/copyable.jsx'; import { EmptyState } from '#/components/molecules/empty-state.jsx'; import { Identity } from '#/components/molecules/identity.jsx'; import { RepoScope } from '#/components/molecules/repo-scope.jsx'; import { SourceDialog } from '#/components/molecules/source-dialog.jsx'; import { TextField } from '#/components/molecules/text-field.jsx'; import { accessLabel, ago, appAccessLabel, bytesToHex, collectionsOf, policyLabel, spaceParts, } from '#/lib/format.js'; import { href, useNavigate } from '#/lib/navigation.jsx'; import { ACCESS_KINDS, ACCESS_LABELS, accessKind, allowedOf, isPublic, managingAppOf, POLICY_KINDS, POLICY_LABELS, policyKind, policyUnion, publishes, } from '#/lib/policy.js'; import { useSession } from '#/lib/session.jsx'; import { deleteSpace, getLatestCommit, getSpace, listAllRecords, listMembers, listRepos, putMember, removeMember, updateSpace, } from '#/lib/spaces.js'; /** A labelled line in a summary card. */ function Fact({ label, children }) { return (
{label} {children}
); } /** One labelled `select`, in the shape of a TextField. */ function Choice({ label, value, onChange, options, labels }) { return ( ); } /** A labelled checkbox, for one flag of a policy. */ function Flag({ label, checked, onChange }) { return ( ); } /** * Change a space's policy. The endpoint replaces the policy whole, so a save * sends every field this form shows. A policy of another kind discards the * member list. */ function ConfigurationEditor({ space, config, onDone }) { const { session } = useSession(); const [kind, setKind] = useState(() => policyKind(config) ?? 'memberspace'); const [publicRead, setPublicRead] = useState(() => isPublic(config, 'read')); const [publicWrite, setPublicWrite] = useState(() => isPublic(config, 'write'), ); const [managingApp, setManagingApp] = useState( () => managingAppOf(config) ?? '', ); const [access, setAccess] = useState(() => accessKind(config)); const [allowed, setAllowed] = useState(() => allowedOf(config).join('\n')); const [publishPolicy, setPublishPolicy] = useState(() => publishes(config, 'publishPolicy'), ); const [publishMembers, setPublishMembers] = useState(() => publishes(config, 'publishMembers'), ); const clients = allowed .split('\n') .map((entry) => entry.trim()) .filter(Boolean); const save = useMutation({ mutationFn: () => updateSpace(session, { space, policy: policyUnion(kind, { publicRead, publicWrite, allowedApps: access === 'allowList' ? clients : null, managingApp: managingApp.trim() || null, publishPolicy, publishMembers, }), }), onSuccess: onDone, }); return ( {kind !== policyKind(config) && kind !== 'memberspace' && ( Changing the policy discards the member list. Changing back does not restore it. )} {kind === 'memberspace' && ( <> {access === 'allowList' && (