mirror of
https://github.com/lukevella/rallly.git
synced 2025-04-28 09:46:39 +02:00
♻️ Rename space folder (#1666)
This commit is contained in:
parent
aa721d9369
commit
5f76285f10
48 changed files with 58 additions and 448 deletions
|
@ -1,75 +0,0 @@
|
|||
"use client";
|
||||
import { Button } from "@rallly/ui/button";
|
||||
import { Icon } from "@rallly/ui/icon";
|
||||
import { PlusIcon } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
|
||||
import {
|
||||
AppCard,
|
||||
AppCardContent,
|
||||
AppCardDescription,
|
||||
AppCardFooter,
|
||||
AppCardIcon,
|
||||
AppCardName,
|
||||
GroupPollIcon,
|
||||
} from "@/app/[locale]/(admin)/app-card";
|
||||
import { Spinner } from "@/components/spinner";
|
||||
import { Trans } from "@/components/trans";
|
||||
import { trpc } from "@/trpc/client";
|
||||
|
||||
export default function Dashboard() {
|
||||
const { data } = trpc.dashboard.info.useQuery();
|
||||
|
||||
if (!data) {
|
||||
return <Spinner />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="grid md:flex">
|
||||
<AppCard className="basis-96">
|
||||
<AppCardIcon>
|
||||
<GroupPollIcon size="lg" />
|
||||
</AppCardIcon>
|
||||
<AppCardContent>
|
||||
<div>
|
||||
<AppCardName>
|
||||
<Trans i18nKey="groupPoll" defaults="Group Poll" />
|
||||
</AppCardName>
|
||||
<AppCardDescription>
|
||||
<Trans
|
||||
i18nKey="groupPollDescription"
|
||||
defaults="Share your availability with a group of people and find the best time to meet."
|
||||
/>
|
||||
</AppCardDescription>
|
||||
</div>
|
||||
</AppCardContent>
|
||||
<AppCardFooter className="flex items-center justify-between gap-4">
|
||||
<div className="inline-flex items-center gap-1 text-sm">
|
||||
<Link
|
||||
className="text-primary font-medium hover:underline"
|
||||
href="/polls?status=live"
|
||||
>
|
||||
<Trans
|
||||
i18nKey="activePollCount"
|
||||
defaults="{activePollCount} Live"
|
||||
values={{
|
||||
activePollCount: data.activePollCount,
|
||||
}}
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
<Button asChild>
|
||||
<Link href="/new">
|
||||
<Icon>
|
||||
<PlusIcon />
|
||||
</Icon>
|
||||
<Trans i18nKey="create" defaults="Create" />
|
||||
</Link>
|
||||
</Button>
|
||||
</AppCardFooter>
|
||||
</AppCard>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
"use server";
|
||||
|
||||
import { prisma } from "@rallly/database";
|
||||
import { revalidatePath } from "next/cache";
|
||||
|
||||
import { auth } from "@/next-auth";
|
||||
|
||||
/**
|
||||
* Deletes multiple polls by their IDs
|
||||
* Only allows deletion of polls owned by the current user
|
||||
*/
|
||||
export async function deletePolls(pollIds: string[]) {
|
||||
try {
|
||||
// Get the current user session
|
||||
const session = await auth();
|
||||
|
||||
if (!session?.user?.id) {
|
||||
throw new Error("Unauthorized");
|
||||
}
|
||||
|
||||
// Delete polls that belong to the current user
|
||||
const result = await prisma.poll.updateMany({
|
||||
where: {
|
||||
id: {
|
||||
in: pollIds,
|
||||
},
|
||||
userId: session.user.id,
|
||||
},
|
||||
data: {
|
||||
deleted: true,
|
||||
deletedAt: new Date(),
|
||||
},
|
||||
});
|
||||
|
||||
// Revalidate the polls page to reflect the changes
|
||||
revalidatePath("/polls");
|
||||
|
||||
return {
|
||||
success: true,
|
||||
count: result.count,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Failed to delete polls:", error);
|
||||
return {
|
||||
success: false,
|
||||
error: "Failed to delete polls",
|
||||
};
|
||||
}
|
||||
}
|
|
@ -3,11 +3,12 @@
|
|||
import { getCoreRowModel, useReactTable } from "@tanstack/react-table";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
import type { ScheduledEvent } from "@/app/[locale]/(admin)/events/types";
|
||||
import { Trans } from "@/components/trans";
|
||||
import { generateGradient } from "@/utils/color-hash";
|
||||
import { useDayjs } from "@/utils/dayjs";
|
||||
|
||||
import type { ScheduledEvent } from "./types";
|
||||
|
||||
export function EventList({ data }: { data: ScheduledEvent[] }) {
|
||||
const table = useReactTable({
|
||||
data,
|
|
@ -1,4 +1,3 @@
|
|||
import { UserScheduledEvents } from "@/app/[locale]/(admin)/events/user-scheduled-events";
|
||||
import type { Params } from "@/app/[locale]/types";
|
||||
import { EventPageIcon } from "@/app/components/page-icons";
|
||||
import {
|
||||
|
@ -11,6 +10,8 @@ import {
|
|||
import { Trans } from "@/components/trans";
|
||||
import { getTranslation } from "@/i18n/server";
|
||||
|
||||
import { UserScheduledEvents } from "./user-scheduled-events";
|
||||
|
||||
export default async function Page({ params }: { params: Params }) {
|
||||
await getTranslation(params.locale);
|
||||
return (
|
|
@ -1,7 +1,6 @@
|
|||
"use client";
|
||||
import { CalendarPlusIcon } from "lucide-react";
|
||||
|
||||
import { EventList } from "@/app/[locale]/(admin)/events/event-list";
|
||||
import {
|
||||
EmptyState,
|
||||
EmptyStateDescription,
|
||||
|
@ -12,6 +11,8 @@ import { Spinner } from "@/components/spinner";
|
|||
import { Trans } from "@/components/trans";
|
||||
import { trpc } from "@/trpc/client";
|
||||
|
||||
import { EventList } from "./event-list";
|
||||
|
||||
export function PastEvents() {
|
||||
const { data } = trpc.scheduledEvents.list.useQuery({
|
||||
period: "past",
|
|
@ -1,7 +1,6 @@
|
|||
"use client";
|
||||
import { CalendarPlusIcon } from "lucide-react";
|
||||
|
||||
import { EventList } from "@/app/[locale]/(admin)/events/event-list";
|
||||
import {
|
||||
EmptyState,
|
||||
EmptyStateDescription,
|
||||
|
@ -12,6 +11,8 @@ import { Spinner } from "@/components/spinner";
|
|||
import { Trans } from "@/components/trans";
|
||||
import { trpc } from "@/trpc/client";
|
||||
|
||||
import { EventList } from "./event-list";
|
||||
|
||||
export function UpcomingEvents() {
|
||||
const { data } = trpc.scheduledEvents.list.useQuery({ period: "upcoming" });
|
||||
|
|
@ -4,10 +4,11 @@ import { Tabs, TabsList, TabsTrigger } from "@rallly/ui/page-tabs";
|
|||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import { z } from "zod";
|
||||
|
||||
import { PastEvents } from "@/app/[locale]/(admin)/events/past-events";
|
||||
import { UpcomingEvents } from "@/app/[locale]/(admin)/events/upcoming-events";
|
||||
import { Trans } from "@/components/trans";
|
||||
|
||||
import { PastEvents } from "./past-events";
|
||||
import { UpcomingEvents } from "./upcoming-events";
|
||||
|
||||
const eventPeriodSchema = z.enum(["upcoming", "past"]).catch("upcoming");
|
||||
|
||||
export function UserScheduledEvents() {
|
|
@ -3,12 +3,12 @@ import { Button } from "@rallly/ui/button";
|
|||
import { SidebarInset, SidebarTrigger } from "@rallly/ui/sidebar";
|
||||
import Link from "next/link";
|
||||
|
||||
import { AppSidebar } from "@/app/[locale]/(admin)/components/sidebar/app-sidebar";
|
||||
import { AppSidebarProvider } from "@/app/[locale]/(admin)/components/sidebar/app-sidebar-provider";
|
||||
import { OptimizedAvatarImage } from "@/components/optimized-avatar-image";
|
||||
import { getUser } from "@/data/get-user";
|
||||
import { CommandMenu } from "@/features/navigation/command-menu";
|
||||
|
||||
import { AppSidebar } from "./components/sidebar/app-sidebar";
|
||||
import { AppSidebarProvider } from "./components/sidebar/app-sidebar-provider";
|
||||
import { TopBar, TopBarLeft, TopBarRight } from "./components/top-bar";
|
||||
|
||||
export default async function Layout({
|
|
@ -15,10 +15,6 @@ import {
|
|||
import Link from "next/link";
|
||||
import { notFound, redirect } from "next/navigation";
|
||||
|
||||
import {
|
||||
SettingsContent,
|
||||
SettingsSection,
|
||||
} from "@/app/[locale]/(admin)/settings/components/settings-layout";
|
||||
import {
|
||||
DescriptionDetails,
|
||||
DescriptionList,
|
||||
|
@ -37,6 +33,10 @@ import { Trans } from "@/components/trans";
|
|||
import { requireUser } from "@/next-auth";
|
||||
import { isSelfHosted } from "@/utils/constants";
|
||||
|
||||
import {
|
||||
SettingsContent,
|
||||
SettingsSection,
|
||||
} from "../components/settings-layout";
|
||||
import { PaymentMethod } from "./components/payment-method";
|
||||
import { SubscriptionPrice } from "./components/subscription-price";
|
||||
import { SubscriptionStatus } from "./components/subscription-status";
|
|
@ -1,12 +1,13 @@
|
|||
"use client";
|
||||
|
||||
import { DateTimePreferences } from "@/app/[locale]/(admin)/settings/components/date-time-preferences";
|
||||
import { LanguagePreference } from "@/app/[locale]/(admin)/settings/components/language-preference";
|
||||
import { Trans } from "@/components/trans";
|
||||
|
||||
import { DateTimePreferences } from "../components/date-time-preferences";
|
||||
import { LanguagePreference } from "../components/language-preference";
|
||||
import {
|
||||
SettingsContent,
|
||||
SettingsSection,
|
||||
} from "@/app/[locale]/(admin)/settings/components/settings-layout";
|
||||
import { Trans } from "@/components/trans";
|
||||
} from "../components/settings-layout";
|
||||
|
||||
export function PreferencesPage() {
|
||||
return (
|
|
@ -3,16 +3,16 @@ import { Button } from "@rallly/ui/button";
|
|||
import { DialogTrigger } from "@rallly/ui/dialog";
|
||||
import { TrashIcon } from "lucide-react";
|
||||
|
||||
import {
|
||||
SettingsContent,
|
||||
SettingsSection,
|
||||
} from "@/app/[locale]/(admin)/settings/components/settings-layout";
|
||||
import { DeleteAccountDialog } from "@/app/[locale]/(admin)/settings/profile/delete-account-dialog";
|
||||
import { ProfileSettings } from "@/app/[locale]/(admin)/settings/profile/profile-settings";
|
||||
import { Trans } from "@/components/trans";
|
||||
import { useUser } from "@/components/user-provider";
|
||||
|
||||
import {
|
||||
SettingsContent,
|
||||
SettingsSection,
|
||||
} from "../components/settings-layout";
|
||||
import { DeleteAccountDialog } from "./delete-account-dialog";
|
||||
import { ProfileEmailAddress } from "./profile-email-address";
|
||||
import { ProfileSettings } from "./profile-settings";
|
||||
|
||||
export const ProfilePage = () => {
|
||||
const { user } = useUser();
|
|
@ -12,11 +12,12 @@ import { Input } from "@rallly/ui/input";
|
|||
import { useForm } from "react-hook-form";
|
||||
import { z } from "zod";
|
||||
|
||||
import { ProfilePicture } from "@/app/[locale]/(admin)/settings/profile/profile-picture";
|
||||
import { Trans } from "@/components/trans";
|
||||
import { useUser } from "@/components/user-provider";
|
||||
import { trpc } from "@/trpc/client";
|
||||
|
||||
import { ProfilePicture } from "./profile-picture";
|
||||
|
||||
const profileSettingsFormData = z.object({
|
||||
name: z.string().min(1).max(100),
|
||||
});
|
|
@ -17,30 +17,29 @@ import {
|
|||
} from "lucide-react";
|
||||
import React from "react";
|
||||
|
||||
const pageIconVariants = cva(
|
||||
"inline-flex size-7 items-center justify-center rounded-lg",
|
||||
{
|
||||
variants: {
|
||||
color: {
|
||||
darkGray: "bg-gray-700 text-white",
|
||||
indigo: "bg-indigo-500 text-white",
|
||||
gray: "bg-gray-200 text-gray-600",
|
||||
lime: "bg-lime-500 text-white",
|
||||
blue: "bg-blue-500 text-white",
|
||||
rose: "bg-rose-500 text-white",
|
||||
purple: "bg-purple-500 text-white",
|
||||
},
|
||||
size: {
|
||||
sm: "size-5",
|
||||
md: "size-7",
|
||||
},
|
||||
const pageIconVariants = cva("inline-flex items-center justify-center", {
|
||||
variants: {
|
||||
color: {
|
||||
darkGray: "bg-gray-700 text-white",
|
||||
indigo: "bg-indigo-500 text-white",
|
||||
gray: "bg-gray-200 text-gray-600",
|
||||
lime: "bg-lime-500 text-white",
|
||||
blue: "bg-blue-500 text-white",
|
||||
rose: "bg-rose-500 text-white",
|
||||
purple: "bg-purple-500 text-white",
|
||||
},
|
||||
defaultVariants: {
|
||||
color: "gray",
|
||||
size: "md",
|
||||
size: {
|
||||
sm: "size-6 [&_svg]:size-3 rounded-md",
|
||||
md: "size-7 [&_svg]:size-4 rounded-lg",
|
||||
lg: "size-9 [&_svg]:size-5 rounded-xl",
|
||||
xl: "size-10 [&_svg]:size-5 rounded-xl",
|
||||
},
|
||||
},
|
||||
);
|
||||
defaultVariants: {
|
||||
color: "gray",
|
||||
size: "md",
|
||||
},
|
||||
});
|
||||
|
||||
type PageIconVariantProps = VariantProps<typeof pageIconVariants>;
|
||||
|
||||
|
@ -53,7 +52,7 @@ export function PageIcon({
|
|||
} & PageIconVariantProps) {
|
||||
return (
|
||||
<span className={pageIconVariants({ color, size })}>
|
||||
<Slot className="size-4">{children}</Slot>
|
||||
<Slot>{children}</Slot>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
@ -111,9 +110,9 @@ export function CreatePageIcon() {
|
|||
);
|
||||
}
|
||||
|
||||
export function PollPageIcon() {
|
||||
export function PollPageIcon(props: PageIconVariantProps) {
|
||||
return (
|
||||
<PageIcon color="purple" size="md">
|
||||
<PageIcon color="purple" size="md" {...props}>
|
||||
<BarChart2Icon />
|
||||
</PageIcon>
|
||||
);
|
||||
|
|
|
@ -13,8 +13,8 @@ import Link from "next/link";
|
|||
import { useParams, usePathname } from "next/navigation";
|
||||
import React from "react";
|
||||
|
||||
import { GroupPollIcon } from "@/app/[locale]/(admin)/app-card";
|
||||
import { LogoutButton } from "@/app/components/logout-button";
|
||||
import { PollPageIcon } from "@/app/components/page-icons";
|
||||
import { InviteDialog } from "@/components/invite-dialog";
|
||||
import { LoginLink } from "@/components/login-link";
|
||||
import {
|
||||
|
@ -67,7 +67,7 @@ const Layout = ({ children }: React.PropsWithChildren) => {
|
|||
</Button>
|
||||
)}
|
||||
<div>
|
||||
<GroupPollIcon size="xs" />
|
||||
<PollPageIcon size="sm" />
|
||||
</div>
|
||||
<h1 className="truncate text-sm font-semibold">{poll.title}</h1>
|
||||
</div>
|
||||
|
|
|
@ -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;
|
||||
};
|
|
@ -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>
|
||||
);
|
||||
}
|
|
@ -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>
|
||||
|
|
Loading…
Add table
Reference in a new issue