♻️ Rename space folder (#1666)

This commit is contained in:
Luke Vella 2025-04-14 17:50:17 +01:00 committed by GitHub
parent aa721d9369
commit 5f76285f10
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
48 changed files with 58 additions and 448 deletions

View file

@ -1,130 +0,0 @@
"use client";
import type { ReactNode } from "react";
import { createContext, useCallback, useMemo, useState } from "react";
import { useRequiredContext } from "@/components/use-required-context";
import { PollSelectionActionBar } from "./poll-selection-action-bar";
type RowSelectionState = Record<string, boolean>;
type PollSelectionContextType = {
selectedPolls: RowSelectionState;
setSelectedPolls: (selection: RowSelectionState) => void;
selectPolls: (pollIds: string[]) => void;
unselectPolls: (pollIds: string[]) => void;
togglePollSelection: (pollId: string) => void;
clearSelection: () => void;
isSelected: (pollId: string) => boolean;
getSelectedPollIds: () => string[];
selectedCount: number;
};
const PollSelectionContext = createContext<PollSelectionContextType | null>(
null,
);
type PollSelectionProviderProps = {
children: ReactNode;
};
export const PollSelectionProvider = ({
children,
}: PollSelectionProviderProps) => {
const [selectedPolls, setSelectedPolls] = useState<RowSelectionState>({});
const selectPolls = useCallback((pollIds: string[]) => {
setSelectedPolls((prev) => {
const newSelection = { ...prev };
pollIds.forEach((id) => {
newSelection[id] = true;
});
return newSelection;
});
}, []);
const unselectPolls = useCallback(
(pollIds: string[]) =>
setSelectedPolls((prev) => {
const newSelection = { ...prev };
pollIds.forEach((id) => {
delete newSelection[id];
});
return newSelection;
}),
[],
);
const togglePollSelection = useCallback(
(pollId: string) =>
setSelectedPolls((prev) => {
const newSelection = { ...prev };
if (newSelection[pollId]) {
delete newSelection[pollId];
} else {
newSelection[pollId] = true;
}
return newSelection;
}),
[],
);
const clearSelection = useCallback(() => setSelectedPolls({}), []);
const isSelected = useCallback(
(pollId: string) => Boolean(selectedPolls[pollId]),
[selectedPolls],
);
const getSelectedPollIds = useCallback(
() => Object.keys(selectedPolls),
[selectedPolls],
);
const selectedCount = useMemo(
() => Object.keys(selectedPolls).length,
[selectedPolls],
);
const value = useMemo(
() => ({
selectedPolls,
setSelectedPolls,
selectPolls,
unselectPolls,
togglePollSelection,
clearSelection,
isSelected,
getSelectedPollIds,
selectedCount,
}),
[
selectedPolls,
setSelectedPolls,
selectPolls,
unselectPolls,
togglePollSelection,
clearSelection,
isSelected,
getSelectedPollIds,
selectedCount,
],
);
return (
<PollSelectionContext.Provider value={value}>
{children}
<PollSelectionActionBar />
</PollSelectionContext.Provider>
);
};
export const usePollSelection = () => {
const context = useRequiredContext(
PollSelectionContext,
"usePollSelection must be used within a PollSelectionProvider",
);
return context;
};

View file

@ -1,142 +0,0 @@
"use client";
import {
ActionBarContainer,
ActionBarContent,
ActionBarGroup,
ActionBarPortal,
} from "@rallly/ui/action-bar";
import { Button } from "@rallly/ui/button";
import {
Dialog,
DialogContent,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@rallly/ui/dialog";
import { AnimatePresence, motion } from "framer-motion";
import { TrashIcon } from "lucide-react";
import * as React from "react";
import { deletePolls } from "@/app/[locale]/(admin)/polls/actions";
import { Trans } from "@/components/trans";
import { usePollSelection } from "./context";
const MActionBar = motion(ActionBarContainer);
export function PollSelectionActionBar() {
const { selectedCount, clearSelection, getSelectedPollIds } =
usePollSelection();
const [isDeleteDialogOpen, setIsDeleteDialogOpen] = React.useState(false);
const [isDeleting, setIsDeleting] = React.useState(false);
const handleDelete = async () => {
const selectedPollIds = getSelectedPollIds();
if (selectedPollIds.length === 0) {
return;
}
setIsDeleting(true);
try {
const result = await deletePolls(selectedPollIds);
if (result.success) {
setIsDeleteDialogOpen(false);
clearSelection();
} else {
// Handle error case
console.error("Failed to delete polls:", result.error);
}
} finally {
setIsDeleting(false);
}
};
return (
<ActionBarPortal>
<AnimatePresence>
{selectedCount > 0 && (
<MActionBar
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: 20 }}
transition={{
type: "spring",
stiffness: 500,
damping: 30,
mass: 0.5,
}}
>
<ActionBarContent>
<span className="text-sm font-medium">
<Trans
i18nKey="selectedPolls"
defaults="{count} {count, plural, one {poll} other {polls}} selected"
values={{ count: selectedCount }}
/>
</span>
</ActionBarContent>
<ActionBarGroup>
<Button
variant="actionBar"
onClick={clearSelection}
className="text-action-bar-foreground"
>
<Trans i18nKey="unselectAll" defaults="Unselect All" />
</Button>
<Button
variant="destructive"
onClick={() => setIsDeleteDialogOpen(true)}
>
<TrashIcon className="size-4" />
<Trans i18nKey="delete" defaults="Delete" />
</Button>
</ActionBarGroup>
</MActionBar>
)}
</AnimatePresence>
{/* Delete Polls Dialog */}
<Dialog open={isDeleteDialogOpen} onOpenChange={setIsDeleteDialogOpen}>
<DialogContent size="sm">
<DialogHeader>
<DialogTitle>
<Trans i18nKey="deletePolls" defaults="Delete Polls" />
</DialogTitle>
</DialogHeader>
<p className="text-sm">
{selectedCount === 1 ? (
<Trans
i18nKey="deletePollDescription"
defaults="Are you sure you want to delete this poll? This action cannot be undone."
/>
) : (
<Trans
i18nKey="deletePollsDescription"
defaults="Are you sure you want to delete these {count} polls? This action cannot be undone."
values={{ count: selectedCount }}
/>
)}
</p>
<DialogFooter>
<Button
onClick={() => {
setIsDeleteDialogOpen(false);
}}
>
<Trans i18nKey="cancel" defaults="Cancel" />
</Button>
<Button
variant="destructive"
onClick={handleDelete}
loading={isDeleting}
>
<Trans i18nKey="delete" defaults="Delete" />
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</ActionBarPortal>
);
}

View file

@ -4,7 +4,7 @@ import { CheckIcon, PlusIcon, ZapIcon } from "lucide-react";
import Link from "next/link";
import { Trans } from "react-i18next/TransWithoutContext";
import { GroupPollIcon } from "@/app/[locale]/(admin)/app-card";
import { PollPageIcon } from "@/app/components/page-icons";
import { getGuestPolls } from "@/features/quick-create/lib/get-guest-polls";
import { getTranslation } from "@/i18n/server";
@ -50,10 +50,10 @@ export async function QuickCreateWidget() {
<li key={poll.id}>
<Link
href={`/poll/${poll.id}`}
className="flex items-center gap-3 rounded-xl border bg-white p-3 hover:bg-gray-50 active:bg-gray-100"
className="flex items-center gap-3 rounded-2xl border bg-white p-3 hover:bg-gray-50 active:bg-gray-100"
>
<div>
<GroupPollIcon size="lg" />
<PollPageIcon size="xl" />
</div>
<div className="min-w-0 flex-1">
<div className="truncate font-medium">{poll.title}</div>