import { useState } from "react"; import { useMutation, useQueryClient, useSuspenseQuery } from "@tanstack/react-query"; import * as stylex from "@stylexjs/stylex"; import { blocklistsQuery, groupCreateMutation, groupDeleteMutation, groupsQuery, groupUpdateMutation, } from "@/lib/queries"; import type { Blocklist, Group } from "@/lib/types"; import GroupSourcesEditor from "./GroupSourcesEditor"; import InlineError from "@/lib/InlineError"; import { styles as shared } from "@/ui/styles"; import { colors } from "@/ui/tokens.stylex"; import { DEFAULT_GROUP_ID } from "@/lib/defaultGroup"; import { READ_ONLY_HINT, useReadOnlyConfig } from "@/features/settings/authority"; const DEFAULT_GROUP_NOTE = "The default group cannot be renamed or deleted."; const styles = stylex.create({ heading: { fontSize: "1.5rem", lineHeight: "2rem", fontWeight: 600, }, createForm: { marginTop: "1rem", display: "flex", flexWrap: "wrap", alignItems: "center", gap: "0.5rem", }, fieldLabel: { fontSize: "0.875rem", lineHeight: "1.25rem", fontWeight: 500, }, list: { display: "flex", flexDirection: "column", gap: "1rem", marginTop: "1.5rem", }, row: { borderRadius: "0.25rem", borderWidth: 1, borderStyle: "solid", borderColor: colors.border, padding: "1rem", }, rowControls: { display: "flex", flexWrap: "wrap", alignItems: "center", gap: "0.75rem", }, renameForm: { display: "flex", alignItems: "center", gap: "0.5rem", }, name: { fontWeight: 500, }, checkboxLabel: { display: "inline-flex", alignItems: "center", gap: "0.5rem", fontSize: "0.875rem", lineHeight: "1.25rem", }, actions: { marginLeft: "auto", display: "inline-flex", flexWrap: "wrap", alignItems: "center", gap: "0.5rem", }, groupButton: { opacity: { default: 1, ":disabled": 0.5 }, }, destructive: { color: colors.danger, }, lockNote: { marginTop: "0.5rem", fontSize: "0.75rem", lineHeight: "1rem", color: colors.textMuted, }, }); export default function GroupsPage() { const { data: groups } = useSuspenseQuery(groupsQuery()); const { data: blocklists } = useSuspenseQuery(blocklistsQuery()); const queryClient = useQueryClient(); const createMutation = useMutation(groupCreateMutation(queryClient)); const [newName, setNewName] = useState(""); const readOnly = useReadOnlyConfig(); return (

Groups

{ event.preventDefault(); const name = newName.trim(); if (name === "") return; createMutation.mutate({ name }, { onSuccess: () => setNewName("") }); }} > setNewName(event.target.value)} disabled={readOnly} {...stylex.props(shared.smallInput, shared.focusRing)} />
); } function GroupRow({ group, blocklists }: { group: Group; blocklists: Blocklist[] }) { const queryClient = useQueryClient(); const updateMutation = useMutation(groupUpdateMutation(queryClient)); const deleteMutation = useMutation(groupDeleteMutation(queryClient)); const [renaming, setRenaming] = useState(false); const [name, setName] = useState(group.name); const [confirming, setConfirming] = useState(false); const [expanded, setExpanded] = useState(false); const isDefault = group.id === DEFAULT_GROUP_ID; const readOnly = useReadOnlyConfig(); const lockNote = isDefault ? DEFAULT_GROUP_NOTE : readOnly ? READ_ONLY_HINT : undefined; return (
  • {renaming ? (
    { event.preventDefault(); const trimmed = name.trim(); if (trimmed === "") return; updateMutation.mutate( { id: group.id, input: { name: trimmed, safe_search: group.safe_search } }, { onSuccess: () => setRenaming(false) }, ); }} > setName(event.target.value)} {...stylex.props(shared.smallInput, shared.focusRing)} autoFocus />
    ) : ( {group.name} )} {!renaming && ( )} {confirming ? ( <> ) : ( )}
    {isDefault &&

    {DEFAULT_GROUP_NOTE}

    } {expanded && }
  • ); }