⬆️ Upgrade to biome 2 (#1825)

This commit is contained in:
Luke Vella 2025-07-15 11:18:28 +01:00 committed by GitHub
parent 56c74d5024
commit 9be12f28d9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
141 changed files with 663 additions and 790 deletions

View file

@ -1,8 +1,8 @@
import { absoluteUrl } from "@rallly/utils/absolute-url";
import { ArrowLeftIcon } from "lucide-react";
import { MDXRemote } from "next-mdx-remote/rsc";
import Image from "next/image";
import Link from "next/link";
import { MDXRemote } from "next-mdx-remote/rsc";
import PostHeader from "@/components/blog/post-header";
import { getAllPosts, getPostBySlug } from "@/lib/api";

View file

@ -1,45 +1,43 @@
export default function CookiePolicy() {
return (
<>
<div className="prose mx-auto max-w-3xl">
<h1>Cookie Policy</h1>
<p>Last updated: 19 April 2023</p>
<p>
This Policy explains how we use cookies and other similar technologies
on our website, and your options to control them.
</p>
<div className="prose mx-auto max-w-3xl">
<h1>Cookie Policy</h1>
<p>Last updated: 19 April 2023</p>
<p>
This Policy explains how we use cookies and other similar technologies
on our website, and your options to control them.
</p>
<h2>What are cookies?</h2>
<p>
Cookies are small text files that are placed on your device (e.g.
computer, tablet, or smartphone) when you visit a website. Cookies are
widely used by website owners to make their websites work, or to work
more efficiently, as well as to provide reporting information.
</p>
<h2>What are cookies?</h2>
<p>
Cookies are small text files that are placed on your device (e.g.
computer, tablet, or smartphone) when you visit a website. Cookies are
widely used by website owners to make their websites work, or to work
more efficiently, as well as to provide reporting information.
</p>
<h2>How we use cookies</h2>
<p>
We use only essential cookies on our website, which are necessary for
our website to function properly and enable you to access secure areas
of the website.
</p>
<h2>How we use cookies</h2>
<p>
We use only essential cookies on our website, which are necessary for
our website to function properly and enable you to access secure areas
of the website.
</p>
<h2>Your options</h2>
<p>
Most web browsers allow you to control cookies through their settings
preferences. However, please be aware that disabling essential cookies
may prevent you from accessing certain parts of our website.
</p>
<h2>Your options</h2>
<p>
Most web browsers allow you to control cookies through their settings
preferences. However, please be aware that disabling essential cookies
may prevent you from accessing certain parts of our website.
</p>
<h2>Changes to this policy</h2>
<p>
We may update this Cookie Policy from time to time to reflect changes
in our website or relevant regulations. We encourage you to review
this policy regularly to stay informed about how we use cookies on our
website.
</p>
</div>
</>
<h2>Changes to this policy</h2>
<p>
We may update this Cookie Policy from time to time to reflect changes in
our website or relevant regulations. We encourage you to review this
policy regularly to stay informed about how we use cookies on our
website.
</p>
</div>
);
}

View file

@ -14,12 +14,10 @@ import type { Viewport } from "next";
import Image from "next/image";
import Link from "next/link";
import { Trans } from "react-i18next/TransWithoutContext";
import { getTranslation } from "@/i18n/server";
import { linkToApp } from "@/lib/linkToApp";
import { LoginButton } from "@/components/login-button";
import { SignUpButton } from "@/components/sign-up-button";
import { getTranslation } from "@/i18n/server";
import { linkToApp } from "@/lib/linkToApp";
import { Footer } from "./footer";
import { NavLink } from "./nav-link";

View file

@ -1,11 +1,10 @@
"use client";
import { cn } from "@rallly/ui";
import { ArrowUpRightIcon } from "lucide-react";
import React from "react";
import GithubIcon from "@/assets/github.svg";
import { Trans } from "@/i18n/client/trans";
import { cn } from "@rallly/ui";
import React from "react";
export function OpenSourceBanner() {
const [visible, setVisible] = React.useState(true);

View file

@ -1,125 +1,122 @@
export default function PrivacyPolicy() {
return (
<>
<div className="prose mx-auto max-w-3xl">
<h1>Privacy Policy</h1>
<p>Last updated: 1 August 2023</p>
<p>
At rallly.co, we take your privacy seriously. This privacy policy
explains how we collect, use, and disclose your personal data, and
your rights in relation to your personal data under the General Data
Protection Regulation (GDPR).
</p>
<div className="prose mx-auto max-w-3xl">
<h1>Privacy Policy</h1>
<p>Last updated: 1 August 2023</p>
<p>
At rallly.co, we take your privacy seriously. This privacy policy
explains how we collect, use, and disclose your personal data, and your
rights in relation to your personal data under the General Data
Protection Regulation (GDPR).
</p>
<h2>Information we collect</h2>
<h2>Information we collect</h2>
<p>
We store personal data (names and email addresses) on
DigitalOcean&apos;s servers, which are located in the United States.
The reason for storing data in the US is to improve performance for
users by having the data stored closer to where our compute services
are running. By using our services, you acknowledge that your personal
data may be transferred to and stored in the United States.
</p>
<p>
We store personal data (names and email addresses) on
DigitalOcean&apos;s servers, which are located in the United States. The
reason for storing data in the US is to improve performance for users by
having the data stored closer to where our compute services are running.
By using our services, you acknowledge that your personal data may be
transferred to and stored in the United States.
</p>
<p>
We collect this information to enable the functionality of our
website, and to provide support and communication to our users. We
also use Posthog as a data processor to analyze trends and debug
issues.
</p>
<p>
We collect this information to enable the functionality of our website,
and to provide support and communication to our users. We also use
Posthog as a data processor to analyze trends and debug issues.
</p>
<p>
Posthog collects certain properties automatically, such as device
information and IP address, to help us understand how the website is
being used and to identify and resolve any issues. This information is
stored securely on Posthog&apos;s EU based servers and is used solely
for the purpose of providing and improving the functionality of the
website.
</p>
<p>
Posthog collects certain properties automatically, such as device
information and IP address, to help us understand how the website is
being used and to identify and resolve any issues. This information is
stored securely on Posthog&apos;s EU based servers and is used solely
for the purpose of providing and improving the functionality of the
website.
</p>
<h2>Legal basis for processing</h2>
<h2>Legal basis for processing</h2>
<p>
We process your personal data on the legal bases of consent and
contract. By using our website, you consent to the collection and use
of your personal data as described in this privacy policy. We process
your personal data to provide you with our services, and to fulfill
our contractual obligations to you.
</p>
<p>
We process your personal data on the legal bases of consent and
contract. By using our website, you consent to the collection and use of
your personal data as described in this privacy policy. We process your
personal data to provide you with our services, and to fulfill our
contractual obligations to you.
</p>
<h2>Retention of personal data</h2>
<h2>Retention of personal data</h2>
<p>
We retain your personal data only for as long as necessary to provide
our services to you, and for as long as required by law. We will
delete your personal data when you delete your account or when it is
no longer necessary for the purposes for which it was collected.
</p>
<p>
We retain your personal data only for as long as necessary to provide
our services to you, and for as long as required by law. We will delete
your personal data when you delete your account or when it is no longer
necessary for the purposes for which it was collected.
</p>
<h2>Sharing of personal data</h2>
<h2>Sharing of personal data</h2>
<p>
We do not share your personal data with any third parties for
marketing or commercial purposes. We may share your personal data with
third parties to provide you with our services, to comply with
applicable laws and regulations, to respond to a subpoena, search
warrant or other lawful request for information we receive, or to
otherwise protect our rights.
</p>
<p>
We do not share your personal data with any third parties for marketing
or commercial purposes. We may share your personal data with third
parties to provide you with our services, to comply with applicable laws
and regulations, to respond to a subpoena, search warrant or other
lawful request for information we receive, or to otherwise protect our
rights.
</p>
<p>
For example, we use Featurebase to make it easy for users to submit
feedback. Your name and email may be shared with Featurbase to provide
a seamless transition between the two services.
</p>
<p>
For example, we use Featurebase to make it easy for users to submit
feedback. Your name and email may be shared with Featurbase to provide a
seamless transition between the two services.
</p>
<h2>Your rights</h2>
<h2>Your rights</h2>
<p>You have the following rights in relation to your personal data:</p>
<p>You have the following rights in relation to your personal data:</p>
<ul>
<li>
Right to access: You have the right to access the personal data we
hold about you.
</li>
<li>
Right to rectification: You have the right to have inaccurate
personal data corrected or completed if it is incomplete.
</li>
<li>
Right to erasure: You have the right to request that we delete your
personal data.
</li>
<li>
Right to restrict processing: You have the right to request that we
restrict the processing of your personal data.
</li>
<li>
Right to data portability: You have the right to receive the
personal data we hold about you in a structured, commonly used, and
machine-readable format, and to transmit it to another controller.
</li>
<li>
Right to object: You have the right to object to the processing of
your personal data in certain circumstances.
</li>
</ul>
<ul>
<li>
Right to access: You have the right to access the personal data we
hold about you.
</li>
<li>
Right to rectification: You have the right to have inaccurate personal
data corrected or completed if it is incomplete.
</li>
<li>
Right to erasure: You have the right to request that we delete your
personal data.
</li>
<li>
Right to restrict processing: You have the right to request that we
restrict the processing of your personal data.
</li>
<li>
Right to data portability: You have the right to receive the personal
data we hold about you in a structured, commonly used, and
machine-readable format, and to transmit it to another controller.
</li>
<li>
Right to object: You have the right to object to the processing of
your personal data in certain circumstances.
</li>
</ul>
<p>
To exercise any of these rights, please contact us at{" "}
<a href="mailto:support@rallly.co">support@rallly.co</a>.
</p>
<p>
To exercise any of these rights, please contact us at{" "}
<a href="mailto:support@rallly.co">support@rallly.co</a>.
</p>
<h2>Contact</h2>
<h2>Contact</h2>
<p>
If you have any questions or concerns about our privacy policy or our
practices with regards to your personal data, please contact us at{" "}
<a href="mailto:support@rallly.co">support@rallly.co</a>.
</p>
</div>
</>
<p>
If you have any questions or concerns about our privacy policy or our
practices with regards to your personal data, please contact us at{" "}
<a href="mailto:support@rallly.co">support@rallly.co</a>.
</p>
</div>
);
}

View file

@ -1,14 +1,13 @@
import "../../style.css";
import languages from "@rallly/languages";
import { PostHogProvider } from "@rallly/posthog/client";
import { Analytics } from "@vercel/analytics/react";
import { LazyMotion, domAnimation } from "motion/react";
import { domAnimation, LazyMotion } from "motion/react";
import type { Viewport } from "next";
import { PostHogPageView } from "@/components/posthog-page-view";
import { sans } from "@/fonts/sans";
import { I18nProvider } from "@/i18n/client/i18n-provider";
import { PostHogProvider } from "@rallly/posthog/client";
export async function generateStaticParams() {
return Object.keys(languages).map((locale) => ({ locale }));

View file

@ -1,6 +1,6 @@
import { Trans } from "@/i18n/client/trans";
import Image from "next/image";
import Link from "next/link";
import { Trans } from "@/i18n/client/trans";
export default function LicensingThankYouPage() {
return (

View file

@ -36,6 +36,7 @@ export async function GET(req: NextRequest) {
<div tw="bg-gray-50 h-full flex-col w-full px-18 py-16 flex">
<div tw="mb-16 flex justify-between">
<div tw="flex justify-between items-center w-full">
{/** biome-ignore lint/performance/noImgElement: it's ok to use img here */}
<img
alt="Rallly"
src="https://rallly.co/static/images/logo-color.svg"

View file

@ -1,4 +1,5 @@
"use client";
import { usePostHog } from "@rallly/posthog/client";
import { cn } from "@rallly/ui";
import { Badge } from "@rallly/ui/badge";
import { Button } from "@rallly/ui/button";
@ -7,11 +8,9 @@ import * as m from "motion/react-m";
import Image from "next/image";
import Link from "next/link";
import * as React from "react";
import { handwritten } from "@/fonts/handwritten";
import { Trans } from "@/i18n/client/trans";
import { linkToApp } from "@/lib/linkToApp";
import { usePostHog } from "@rallly/posthog/client";
const Screenshot = () => {
const [isLoaded, setIsLoaded] = React.useState(false);

View file

@ -1,10 +1,10 @@
"use client";
import { Trans } from "@/i18n/client/trans";
import { linkToApp } from "@/lib/linkToApp";
import { usePostHog } from "@rallly/posthog/client";
import { Button } from "@rallly/ui/button";
import Link from "next/link";
import { Trans } from "@/i18n/client/trans";
import { linkToApp } from "@/lib/linkToApp";
export const LoginButton = () => {
const posthog = usePostHog();

View file

@ -1,10 +1,10 @@
"use client";
import { Trans } from "@/i18n/client/trans";
import { linkToApp } from "@/lib/linkToApp";
import { usePostHog } from "@rallly/posthog/client";
import { Button } from "@rallly/ui/button";
import Link from "next/link";
import { Trans } from "@/i18n/client/trans";
import { linkToApp } from "@/lib/linkToApp";
export function SignUpButton() {
const posthog = usePostHog();

View file

@ -21,7 +21,6 @@ export function getPostBySlug(slug: string, fields: string[] = []) {
const items: Items = {};
// Ensure only the minimal needed data is exposed
// biome-ignore lint/complexity/noForEach: Fix this later
fields.forEach((field) => {
if (field === "slug") {
items[field] = realSlug;

View file

@ -2,9 +2,9 @@ import { cn } from "@rallly/ui";
import { DotPattern } from "@rallly/ui/dot-pattern";
import {
isQuickCreateEnabled,
QuickCreateButton,
QuickCreateWidget,
isQuickCreateEnabled,
} from "@/features/quick-create";
export default async function Layout({

View file

@ -10,8 +10,8 @@ import {
FormMessage,
} from "@rallly/ui/form";
import { Input } from "@rallly/ui/input";
import { signIn } from "next-auth/react";
import { useRouter, useSearchParams } from "next/navigation";
import { signIn } from "next-auth/react";
import React from "react";
import { useForm } from "react-hook-form";
import { useTranslation } from "react-i18next";
@ -70,7 +70,7 @@ export function LoginWithEmailForm() {
: ""
}`,
);
} catch (e) {
} catch (_e) {
form.setError("identifier", {
message: t("userNotFound", {
defaultValue: "A user with that email doesn't exist",

View file

@ -2,8 +2,8 @@
import { Button } from "@rallly/ui/button";
import { Icon } from "@rallly/ui/icon";
import { UserIcon } from "lucide-react";
import { signIn } from "next-auth/react";
import Image from "next/image";
import { signIn } from "next-auth/react";
import { Trans } from "@/components/trans";
import { useTranslation } from "@/i18n/client";

View file

@ -3,9 +3,8 @@ import { Trans } from "react-i18next/TransWithoutContext";
import { GoogleProvider } from "@/auth/providers/google";
import { MicrosoftProvider } from "@/auth/providers/microsoft";
import { OIDCProvider } from "@/auth/providers/oidc";
import { getTranslation } from "@/i18n/server";
import { getInstanceSettings } from "@/features/instance-settings/queries";
import { getTranslation } from "@/i18n/server";
import {
AuthPageContainer,
AuthPageContent,

View file

@ -1,9 +1,8 @@
import { notFound } from "next/navigation";
import { Trans } from "react-i18next/TransWithoutContext";
import { getTranslation } from "@/i18n/server";
import { getInstanceSettings } from "@/features/instance-settings/queries";
import { notFound } from "next/navigation";
import { getTranslation } from "@/i18n/server";
import {
AuthPageContainer,
AuthPageContent,

View file

@ -10,8 +10,8 @@ import {
FormItem,
FormMessage,
} from "@rallly/ui/form";
import { signIn } from "next-auth/react";
import { useSearchParams } from "next/navigation";
import { signIn } from "next-auth/react";
import { useForm } from "react-hook-form";
import { z } from "zod";

View file

@ -14,7 +14,7 @@ import {
} from "lucide-react";
import Link from "next/link";
import { notFound, redirect } from "next/navigation";
import { requireUser } from "@/auth/queries";
import {
DescriptionDetails,
DescriptionList,
@ -30,10 +30,8 @@ import {
import { PayWallDialog } from "@/components/pay-wall-dialog";
import { Trans } from "@/components/trans";
import { FormattedDateTime } from "@/features/timezone/client/formatted-date-time";
import { isSelfHosted } from "@/utils/constants";
import { requireUser } from "@/auth/queries";
import { getTranslation } from "@/i18n/server";
import { isSelfHosted } from "@/utils/constants";
import {
SettingsContent,
SettingsSection,

View file

@ -1,11 +1,11 @@
"use client";
import { Trans } from "@/components/trans";
import { usePostHog } from "@rallly/posthog/client";
import { Button } from "@rallly/ui/button";
import { Icon } from "@rallly/ui/icon";
import { LogOutIcon } from "lucide-react";
import { signOut } from "next-auth/react";
import { Trans } from "@/components/trans";
export const SignOutButton = () => {
const posthog = usePostHog();

View file

@ -1,9 +1,9 @@
"use server";
import { ActionError, authActionClient } from "@/features/safe-action/server";
import { signOut } from "@/next-auth";
import { subject } from "@casl/ability";
import { prisma } from "@rallly/database";
import { ActionError, authActionClient } from "@/features/safe-action/server";
import { signOut } from "@/next-auth";
export const deleteCurrentUserAction = authActionClient
.metadata({ actionName: "delete_current_user" })

View file

@ -3,10 +3,9 @@ import { DialogTrigger } from "@rallly/ui/dialog";
import { TrashIcon } from "lucide-react";
import type { Params } from "@/app/[locale]/types";
import { requireUser } from "@/auth/queries";
import { Trans } from "@/components/trans";
import { getTranslation } from "@/i18n/server";
import { requireUser } from "@/auth/queries";
import {
SettingsContent,
SettingsSection,

View file

@ -13,17 +13,16 @@ import {
FormMessage,
} from "@rallly/ui/form";
import { Input } from "@rallly/ui/input";
import { toast } from "@rallly/ui/sonner";
import Cookies from "js-cookie";
import { InfoIcon } from "lucide-react";
import React from "react";
import { useForm } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { z } from "zod";
import { Trans } from "@/components/trans";
import { useUser } from "@/components/user-provider";
import { trpc } from "@/trpc/client";
import { toast } from "@rallly/ui/sonner";
const emailChangeFormData = z.object({
email: z.string().email(),

View file

@ -2,16 +2,15 @@
import { usePostHog } from "@rallly/posthog/client";
import { Button } from "@rallly/ui/button";
import { toast } from "@rallly/ui/sonner";
import React, { useState } from "react";
import { z } from "zod";
import { OptimizedAvatarImage } from "@/components/optimized-avatar-image";
import { Trans } from "@/components/trans";
import { useUser } from "@/components/user-provider";
import { useFeatureFlag } from "@/features/feature-flags/client";
import { useTranslation } from "@/i18n/client";
import { trpc } from "@/trpc/client";
import { toast } from "@rallly/ui/sonner";
const allowedMimeTypes = z.enum(["image/jpeg", "image/png"]);

View file

@ -1,6 +1,5 @@
"use client";
import { useTranslation } from "@/i18n/client";
import { Icon } from "@rallly/ui/icon";
import {
SidebarMenu,
@ -10,6 +9,7 @@ import {
import { BarChart2Icon, CalendarIcon, HomeIcon } from "lucide-react";
import Link from "next/link";
import { usePathname } from "next/navigation";
import { useTranslation } from "@/i18n/client";
const useSpaceMenuItems = () => {
const { t } = useTranslation();

View file

@ -1,12 +1,3 @@
import { SpaceSidebarMenu } from "@/app/[locale]/(space)/components/sidebar/space-sidebar-menu";
import { LogoLink } from "@/app/components/logo-link";
import { getActiveSpace, requireUserAbility } from "@/auth/queries";
import { Trans } from "@/components/trans";
import { FeedbackToggle } from "@/features/feedback/components/feedback-toggle";
import { SpaceDropdown } from "@/features/spaces/components/space-dropdown";
import { SpaceIcon } from "@/features/spaces/components/space-icon";
import { isSpacesEnabled } from "@/features/spaces/constants";
import { loadSpaces } from "@/features/spaces/queries";
import { Button } from "@rallly/ui/button";
import { Icon } from "@rallly/ui/icon";
import {
@ -21,6 +12,15 @@ import { Tooltip, TooltipContent, TooltipTrigger } from "@rallly/ui/tooltip";
import { ChevronsUpDownIcon, PlusIcon, SparklesIcon } from "lucide-react";
import Link from "next/link";
import type React from "react";
import { SpaceSidebarMenu } from "@/app/[locale]/(space)/components/sidebar/space-sidebar-menu";
import { LogoLink } from "@/app/components/logo-link";
import { getActiveSpace, requireUserAbility } from "@/auth/queries";
import { Trans } from "@/components/trans";
import { FeedbackToggle } from "@/features/feedback/components/feedback-toggle";
import { SpaceDropdown } from "@/features/spaces/components/space-dropdown";
import { SpaceIcon } from "@/features/spaces/components/space-icon";
import { isSpacesEnabled } from "@/features/spaces/constants";
import { loadSpaces } from "@/features/spaces/queries";
import { UpgradeButton } from "../upgrade-button";
import { NavUser } from "./nav-user";

View file

@ -10,6 +10,7 @@ import {
PageTitle,
} from "@/app/components/page-layout";
import { SearchInput } from "@/app/components/search-input";
import { getActiveSpace } from "@/auth/queries";
import {
EmptyState,
EmptyStateDescription,
@ -24,8 +25,6 @@ import { getScheduledEvents } from "@/features/scheduled-event/queries";
import type { Status } from "@/features/scheduled-event/schema";
import { statusSchema } from "@/features/scheduled-event/schema";
import { getTranslation } from "@/i18n/server";
import { getActiveSpace } from "@/auth/queries";
import { EventsTabbedView } from "./events-tabbed-view";
async function loadData({

View file

@ -1,11 +1,10 @@
"use client";
import { useLocalStorage } from "react-use";
import { Trans } from "@/components/trans";
import { usePostHog } from "@rallly/posthog/client";
import { Button } from "@rallly/ui/button";
import { HeartIcon } from "lucide-react";
import Link from "next/link";
import { useLocalStorage } from "react-use";
import { Trans } from "@/components/trans";
export function FeedbackAlert() {
const [value, setValue] = useLocalStorage<string>("home_feedback_dismissed");

View file

@ -3,11 +3,10 @@ import { SidebarInset, SidebarTrigger } from "@rallly/ui/sidebar";
import Link from "next/link";
import { OptimizedAvatarImage } from "@/components/optimized-avatar-image";
import { LicenseLimitWarning } from "@/features/licensing/components/license-limit-warning";
import { CommandMenu } from "@/features/navigation/command-menu";
import { getOnboardedUser } from "@/features/setup/queries";
import { TimezoneProvider } from "@/features/timezone/client/context";
import { LicenseLimitWarning } from "@/features/licensing/components/license-limit-warning";
import { SpaceSidebar } from "./components/sidebar/space-sidebar";
import { SpaceSidebarProvider } from "./components/sidebar/space-sidebar-provider";
import { TopBar, TopBarLeft, TopBarRight } from "./components/top-bar";

View file

@ -1,6 +1,6 @@
import { prisma } from "@rallly/database";
import { Tile, TileDescription, TileGrid, TileTitle } from "@rallly/ui/tile";
import Link from "next/link";
import {
BillingPageIcon,
CreatePageIcon,
@ -22,7 +22,6 @@ import { Trans } from "@/components/trans";
import { IfCloudHosted } from "@/contexts/environment";
import { getUpcomingEventsCount } from "@/features/scheduled-event/queries";
import { getTranslation } from "@/i18n/server";
import { prisma } from "@rallly/database";
import { FeedbackAlert } from "./feedback-alert";
async function loadData() {

View file

@ -1,11 +1,10 @@
"use client";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@rallly/ui/page-tabs";
import { useRouter, useSearchParams } from "next/navigation";
import { Trans } from "@/components/trans";
import { cn } from "@rallly/ui";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@rallly/ui/page-tabs";
import { useRouter, useSearchParams } from "next/navigation";
import React from "react";
import { Trans } from "@/components/trans";
import { statusSchema } from "./schema";
export function PollsTabbedView({ children }: { children: React.ReactNode }) {

View file

@ -1,5 +1,5 @@
import { pollStatusSchema } from "@/features/poll/schema";
import { z } from "zod";
import { pollStatusSchema } from "@/features/poll/schema";
export const DEFAULT_PAGE_SIZE = 10;

View file

@ -1,9 +1,8 @@
"use server";
import { authActionClient } from "@/features/safe-action/server";
import { ActionError } from "@/features/safe-action/server";
import { subject } from "@casl/ability";
import { prisma } from "@rallly/database";
import { redirect } from "next/navigation";
import { ActionError, authActionClient } from "@/features/safe-action/server";
export const makeMeAdminAction = authActionClient
.metadata({ actionName: "make_admin" })

View file

@ -1,8 +1,8 @@
"use client";
import { Button } from "@rallly/ui/button";
import { Trans } from "@/components/trans";
import { useSafeAction } from "@/features/safe-action/client";
import { Button } from "@rallly/ui/button";
import { makeMeAdminAction } from "./actions";
export function MakeMeAdminButton() {

View file

@ -1,3 +1,8 @@
import { subject } from "@casl/ability";
import { Button } from "@rallly/ui/button";
import { CrownIcon } from "lucide-react";
import Link from "next/link";
import { notFound, redirect } from "next/navigation";
import { requireUserAbility } from "@/auth/queries";
import {
EmptyState,
@ -8,11 +13,6 @@ import {
} from "@/components/empty-state";
import { Trans } from "@/components/trans";
import { getTranslation } from "@/i18n/server";
import { subject } from "@casl/ability";
import { Button } from "@rallly/ui/button";
import { CrownIcon } from "lucide-react";
import Link from "next/link";
import { notFound, redirect } from "next/navigation";
import { MakeMeAdminButton } from "./make-me-admin-button";
export default async function AdminSetupPage() {

View file

@ -1,14 +1,16 @@
import { SidebarInset } from "@rallly/ui/sidebar";
import { requireAdmin } from "@/auth/queries";
import { LicenseLimitWarning } from "@/features/licensing/components/license-limit-warning";
import { CommandMenu } from "@/features/navigation/command-menu";
import { getTranslation } from "@/i18n/server";
import { SidebarInset } from "@rallly/ui/sidebar";
import { ControlPanelSidebarProvider } from "./control-panel-sidebar-provider";
import { ControlPanelSidebar } from "./sidebar";
export default async function AdminLayout({
children,
}: { children: React.ReactNode }) {
}: {
children: React.ReactNode;
}) {
await requireAdmin();
return (

View file

@ -1,3 +1,14 @@
import { Button } from "@rallly/ui/button";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@rallly/ui/dialog";
import { Icon } from "@rallly/ui/icon";
import dayjs from "dayjs";
import { KeySquareIcon, PlusIcon, ShoppingBagIcon } from "lucide-react";
import { PageIcon } from "@/app/components/page-icons";
import { requireAdmin } from "@/auth/queries";
import {
@ -18,17 +29,6 @@ import { LicenseKeyForm } from "@/features/licensing/components/license-key-form
import { RemoveLicenseButton } from "@/features/licensing/components/remove-license-button";
import { getLicense } from "@/features/licensing/queries";
import { getTranslation } from "@/i18n/server";
import { Button } from "@rallly/ui/button";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@rallly/ui/dialog";
import { Icon } from "@rallly/ui/icon";
import dayjs from "dayjs";
import { KeySquareIcon, PlusIcon, ShoppingBagIcon } from "lucide-react";
async function loadData() {
await requireAdmin();
@ -40,19 +40,11 @@ function DescriptionList({ children }: { children: React.ReactNode }) {
return <dl>{children}</dl>;
}
function DescriptionListTitle({
children,
}: {
children: React.ReactNode;
}) {
function DescriptionListTitle({ children }: { children: React.ReactNode }) {
return <dt className="mb-1 text-muted-foreground text-xs">{children}</dt>;
}
function DescriptionListValue({
children,
}: {
children: React.ReactNode;
}) {
function DescriptionListValue({ children }: { children: React.ReactNode }) {
return <dd className="mb-4 font-mono text-sm">{children}</dd>;
}

View file

@ -1,13 +1,3 @@
import { PageIcon } from "@/app/components/page-icons";
import { requireAdmin } from "@/auth/queries";
import {
FullWidthLayout,
FullWidthLayoutContent,
FullWidthLayoutHeader,
FullWidthLayoutTitle,
} from "@/components/full-width-layout";
import { Trans } from "@/components/trans";
import { getLicense } from "@/features/licensing/queries";
import { prisma } from "@rallly/database";
import { cn } from "@rallly/ui";
import { Icon } from "@rallly/ui/icon";
@ -20,6 +10,16 @@ import {
UsersIcon,
} from "lucide-react";
import Link from "next/link";
import { PageIcon } from "@/app/components/page-icons";
import { requireAdmin } from "@/auth/queries";
import {
FullWidthLayout,
FullWidthLayoutContent,
FullWidthLayoutHeader,
FullWidthLayoutTitle,
} from "@/components/full-width-layout";
import { Trans } from "@/components/trans";
import { getLicense } from "@/features/licensing/queries";
async function loadData() {
await requireAdmin();

View file

@ -1,19 +1,5 @@
"use client";
import {
SettingsGroup,
SettingsGroupContent,
SettingsGroupDescription,
SettingsGroupHeader,
SettingsGroupTitle,
} from "@/components/settings-group";
import { Trans } from "@/components/trans";
import { updateInstanceSettings } from "@/features/instance-settings/mutations";
import {
type InstanceSettings,
instanceSettingsSchema,
} from "@/features/instance-settings/schema";
import { useTranslation } from "@/i18n/client";
import { zodResolver } from "@hookform/resolvers/zod";
import {
ActionBar,
@ -32,6 +18,20 @@ import {
import { toast } from "@rallly/ui/sonner";
import { Switch } from "@rallly/ui/switch";
import { useForm } from "react-hook-form";
import {
SettingsGroup,
SettingsGroupContent,
SettingsGroupDescription,
SettingsGroupHeader,
SettingsGroupTitle,
} from "@/components/settings-group";
import { Trans } from "@/components/trans";
import { updateInstanceSettings } from "@/features/instance-settings/mutations";
import {
type InstanceSettings,
instanceSettingsSchema,
} from "@/features/instance-settings/schema";
import { useTranslation } from "@/i18n/client";
export function InstanceSettingsForm({
defaultValue,

View file

@ -1,3 +1,4 @@
import { SettingsIcon } from "lucide-react";
import { PageIcon } from "@/app/components/page-icons";
import { requireAdmin } from "@/auth/queries";
import {
@ -8,7 +9,6 @@ import {
} from "@/components/full-width-layout";
import { Trans } from "@/components/trans";
import { getInstanceSettings } from "@/features/instance-settings/queries";
import { SettingsIcon } from "lucide-react";
import { InstanceSettingsForm } from "./instance-settings-form";
async function loadData() {

View file

@ -1,8 +1,4 @@
"use client";
import { Trans } from "@/components/trans";
import { useSafeAction } from "@/features/safe-action/client";
import { deleteUserAction } from "@/features/user/actions";
import { useTranslation } from "@/i18n/client";
import { zodResolver } from "@hookform/resolvers/zod";
import { Button } from "@rallly/ui/button";
import {
@ -26,6 +22,10 @@ import {
import { Input } from "@rallly/ui/input";
import { useForm } from "react-hook-form";
import { z } from "zod";
import { Trans } from "@/components/trans";
import { useSafeAction } from "@/features/safe-action/client";
import { deleteUserAction } from "@/features/user/actions";
import { useTranslation } from "@/i18n/client";
const useSchema = (email: string) => {
const { t } = useTranslation();

View file

@ -1,3 +1,6 @@
import { type Prisma, prisma } from "@rallly/database";
import { UsersIcon } from "lucide-react";
import z from "zod";
import { PageIcon } from "@/app/components/page-icons";
import { requireAdmin } from "@/auth/queries";
import {
@ -16,9 +19,6 @@ import { Pagination } from "@/components/pagination";
import { StackedList } from "@/components/stacked-list";
import { Trans } from "@/components/trans";
import { getTranslation } from "@/i18n/server";
import { type Prisma, prisma } from "@rallly/database";
import { UsersIcon } from "lucide-react";
import z from "zod";
import { UserRow } from "./user-row";
import { UserSearchInput } from "./user-search-input";
import { UsersTabbedView } from "./users-tabbed-view";
@ -102,7 +102,7 @@ export default async function AdminPage(props: {
const searchParams = await props.searchParams;
const { page, pageSize } = searchParamsSchema.parse(searchParams);
const { adminUser, allUsers, totalUsers } = await loadData({
const { allUsers, totalUsers } = await loadData({
page,
pageSize,
q: searchParams.q ? String(searchParams.q) : undefined,

View file

@ -1,11 +1,4 @@
"use client";
import { OptimizedAvatarImage } from "@/components/optimized-avatar-image";
import { StackedListItem } from "@/components/stacked-list";
import { Trans } from "@/components/trans";
import { useUser } from "@/components/user-provider";
import { useSafeAction } from "@/features/safe-action/client";
import { changeRoleAction } from "@/features/user/actions";
import { userRoleSchema } from "@/features/user/schema";
import { cn } from "@rallly/ui";
import { Button } from "@rallly/ui/button";
import { useDialog } from "@rallly/ui/dialog";
@ -24,6 +17,13 @@ import {
import { Icon } from "@rallly/ui/icon";
import { MoreHorizontal, TrashIcon, UserPenIcon } from "lucide-react";
import { useTransition } from "react";
import { OptimizedAvatarImage } from "@/components/optimized-avatar-image";
import { StackedListItem } from "@/components/stacked-list";
import { Trans } from "@/components/trans";
import { useUser } from "@/components/user-provider";
import { useSafeAction } from "@/features/safe-action/client";
import { changeRoleAction } from "@/features/user/actions";
import { userRoleSchema } from "@/features/user/schema";
import { DeleteUserDialog } from "./dialogs/delete-user-dialog";
export function UserRow({

View file

@ -1,11 +1,10 @@
"use client";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@rallly/ui/page-tabs";
import { useRouter, useSearchParams } from "next/navigation";
import { Trans } from "@/components/trans";
import { cn } from "@rallly/ui";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@rallly/ui/page-tabs";
import { useRouter, useSearchParams } from "next/navigation";
import React from "react";
import { Trans } from "@/components/trans";
export function UsersTabbedView({ children }: { children: React.ReactNode }) {
const searchParams = useSearchParams();

View file

@ -1,6 +1,6 @@
import { prisma } from "@rallly/database";
import { absoluteUrl } from "@rallly/utils/absolute-url";
import { HydrationBoundary, dehydrate } from "@tanstack/react-query";
import { dehydrate, HydrationBoundary } from "@tanstack/react-query";
import { notFound } from "next/navigation";
import { InvitePage } from "@/app/[locale]/invite/[urlId]/invite-page";

View file

@ -4,7 +4,7 @@ import { supportedLngs } from "@rallly/languages";
import { PostHogProvider } from "@rallly/posthog/client";
import { Toaster } from "@rallly/ui/sonner";
import { TooltipProvider } from "@rallly/ui/tooltip";
import { LazyMotion, domAnimation } from "motion/react";
import { domAnimation, LazyMotion } from "motion/react";
import type { Viewport } from "next";
import { Inter } from "next/font/google";
import type React from "react";
@ -12,16 +12,15 @@ import type React from "react";
import { TimeZoneChangeDetector } from "@/app/[locale]/timezone-change-detector";
import { UserProvider } from "@/components/user-provider";
import { PreferencesProvider } from "@/contexts/preferences";
import { FeatureFlagsProvider } from "@/features/feature-flags/client";
import { isStorageEnabled } from "@/features/storage";
import { TimezoneProvider } from "@/features/timezone/client/context";
import { getUser } from "@/features/user/queries";
import { I18nProvider } from "@/i18n/client";
import { getLocale } from "@/i18n/server/get-locale";
import { auth } from "@/next-auth";
import { TRPCProvider } from "@/trpc/client/provider";
import { ConnectedDayjsProvider } from "@/utils/dayjs";
import { FeatureFlagsProvider } from "@/features/feature-flags/client";
import { isStorageEnabled } from "@/features/storage";
import { getUser } from "@/features/user/queries";
import { PostHogPageView } from "../posthog-page-view";
const inter = Inter({

View file

@ -5,10 +5,9 @@ import { PollPageIcon } from "@/app/components/page-icons";
import { CreatePoll } from "@/components/create-poll";
import { Trans } from "@/components/trans";
import { UserDropdown } from "@/components/user-dropdown";
import { getInstanceSettings } from "@/features/instance-settings/queries";
import { getTranslation } from "@/i18n/server";
import { getLoggedIn } from "@/next-auth";
import { getInstanceSettings } from "@/features/instance-settings/queries";
import { BackButton } from "./back-button";
export default async function Page() {

View file

@ -15,8 +15,8 @@ import { useForm } from "react-hook-form";
import type { PollDetailsData } from "@/components/forms/poll-details-form";
import { PollDetailsForm } from "@/components/forms/poll-details-form";
import { usePoll } from "@/components/poll-context";
import { useUpdatePollMutation } from "@/components/poll/mutations";
import { usePoll } from "@/components/poll-context";
import { Trans } from "@/components/trans";
const Page = () => {

View file

@ -10,8 +10,8 @@ import { useForm } from "react-hook-form";
import type { PollOptionsData } from "@/components/forms";
import PollOptionsForm from "@/components/forms/poll-options-form";
import { useModalContext } from "@/components/modal/modal-provider";
import { usePoll } from "@/components/poll-context";
import { useUpdatePollMutation } from "@/components/poll/mutations";
import { usePoll } from "@/components/poll-context";
import { Trans } from "@/components/trans";
import { useTranslation } from "@/i18n/client";
import { encodeDateOption } from "@/utils/date-time-utils";

View file

@ -1,5 +1,5 @@
import { prisma } from "@rallly/database";
import { HydrationBoundary, dehydrate } from "@tanstack/react-query";
import { dehydrate, HydrationBoundary } from "@tanstack/react-query";
import { notFound, redirect } from "next/navigation";
import { PollLayout } from "@/components/layouts/poll-layout";

View file

@ -5,8 +5,8 @@ import Link from "next/link";
import { notFound } from "next/navigation";
import {
QuickCreateWidget,
isQuickCreateEnabled,
QuickCreateWidget,
} from "@/features/quick-create";
import { getTranslation } from "@/i18n/server";

View file

@ -1,7 +1,7 @@
import type { NextRequest } from "next/server";
import { rateLimit } from "@/features/rate-limit";
import { handlers } from "@/next-auth";
import { withPosthog } from "@/utils/posthog";
import type { NextRequest } from "next/server";
export const GET = withPosthog(async (req: NextRequest) => {
if (req.nextUrl.pathname.includes("callback/email")) {

View file

@ -1,20 +1,20 @@
import { zValidator } from "@hono/zod-validator";
import { RedisStore } from "@hono-rate-limiter/redis";
import { prisma } from "@rallly/database";
import { kv } from "@vercel/kv";
import { Hono } from "hono";
import { bearerAuth } from "hono/bearer-auth";
import { handle } from "hono/vercel";
import { rateLimiter } from "hono-rate-limiter";
import { env } from "@/env";
import { generateLicenseKey } from "@/features/licensing/helpers/generate-license-key";
import {
type CreateLicenseResponse,
type ValidateLicenseKeyResponse,
createLicenseInputSchema,
type ValidateLicenseKeyResponse,
validateLicenseKeyInputSchema,
} from "@/features/licensing/schema";
import { isSelfHosted } from "@/utils/constants";
import { RedisStore } from "@hono-rate-limiter/redis";
import { zValidator } from "@hono/zod-validator";
import { prisma } from "@rallly/database";
import { kv } from "@vercel/kv";
import { Hono } from "hono";
import { rateLimiter } from "hono-rate-limiter";
import { bearerAuth } from "hono/bearer-auth";
import { handle } from "hono/vercel";
const isKvAvailable =
process.env.KV_REST_API_URL && process.env.KV_REST_API_TOKEN;

View file

@ -1,8 +1,8 @@
import type { LicenseCheckoutMetadata } from "@/features/licensing/schema";
import type { LicenseType } from "@prisma/client";
import { stripe } from "@rallly/billing";
import { type NextRequest, NextResponse } from "next/server";
import { z } from "zod";
import type { LicenseCheckoutMetadata } from "@/features/licensing/schema";
const productSchema = z.enum(["plus", "organization"]);

View file

@ -1,11 +1,11 @@
import type { Stripe } from "@rallly/billing";
import { stripe } from "@rallly/billing";
import { posthog } from "@rallly/posthog/server";
import { env } from "@/env";
import { licensingClient } from "@/features/licensing/client";
import { licenseCheckoutMetadataSchema } from "@/features/licensing/schema";
import { subscriptionCheckoutMetadataSchema } from "@/features/subscription/schema";
import { getEmailClient } from "@/utils/emails";
import type { Stripe } from "@rallly/billing";
import { stripe } from "@rallly/billing";
import { posthog } from "@rallly/posthog/server";
async function handleSubscriptionCheckoutSessionCompleted(
checkoutSession: Stripe.Checkout.Session,

View file

@ -3,10 +3,8 @@ import { stripe } from "@rallly/billing";
import * as Sentry from "@sentry/nextjs";
import type { NextRequest } from "next/server";
import { NextResponse } from "next/server";
import { withPosthog } from "@/utils/posthog";
import { isSelfHosted } from "@/utils/constants";
import { withPosthog } from "@/utils/posthog";
import { getEventHandler } from "./handlers";
export const POST = withPosthog(async (request: NextRequest) => {

View file

@ -1,7 +1,7 @@
"use client";
import { Slot } from "@radix-ui/react-slot";
import { type VariantProps, cva } from "class-variance-authority";
import { cva, type VariantProps } from "class-variance-authority";
import {
BarChart2Icon,
CalendarIcon,

View file

@ -9,10 +9,11 @@
*
* See: https://github.com/lukevella/rallly/issues/949
*/
import { createUser } from "@/features/user/mutations";
import { PrismaAdapter } from "@auth/prisma-adapter";
import { prisma } from "@rallly/database";
import type { Adapter } from "next-auth/adapters";
import { createUser } from "@/features/user/mutations";
export function CustomPrismaAdapter(options: {
migrateData: (userId: string) => Promise<void>;

View file

@ -1,6 +1,6 @@
import type { NextResponse } from "next/server";
import type { NextAuthRequest, Session } from "next-auth";
import NextAuth from "next-auth";
import type { NextResponse } from "next/server";
import { nextAuthConfig } from "@/next-auth.config";

View file

@ -1,15 +1,11 @@
import { defineAbilityFor } from "@/features/ability-manager";
import { getUser } from "@/features/user/queries";
import { accessibleBy } from "@casl/prisma";
import { prisma } from "@rallly/database";
import { posthog } from "@rallly/posthog/server";
import * as Sentry from "@sentry/nextjs";
import { defineAbilityFor } from "@/features/ability-manager";
import { getUser } from "@/features/user/queries";
const getActiveSpaceForUser = async ({
userId,
}: {
userId: string;
}) => {
const getActiveSpaceForUser = async ({ userId }: { userId: string }) => {
const user = await getUser(userId);
if (!user) {

View file

@ -1,9 +1,9 @@
import { notFound, redirect } from "next/navigation";
import { cache } from "react";
import { defineAbilityFor } from "@/features/ability-manager";
import { getDefaultSpace, getSpace } from "@/features/spaces/queries";
import { getUser } from "@/features/user/queries";
import { auth } from "@/next-auth";
import { notFound, redirect } from "next/navigation";
import { cache } from "react";
/**
* @deprecated - Use requireUserAbility() instead

View file

@ -9,18 +9,16 @@ import {
CardTitle,
} from "@rallly/ui/card";
import { Form } from "@rallly/ui/form";
import { toast } from "@rallly/ui/sonner";
import { useRouter } from "next/navigation";
import type React from "react";
import { useForm } from "react-hook-form";
import useFormPersist from "react-hook-form-persist";
import { useUnmount } from "react-use";
import { PollSettingsForm } from "@/components/forms/poll-settings";
import { Trans } from "@/components/trans";
import { useUser } from "@/components/user-provider";
import { trpc } from "@/trpc/client";
import { toast } from "@rallly/ui/sonner";
import type { NewEventData } from "./forms";
import { PollDetailsForm, PollOptionsForm } from "./forms";

View file

@ -18,6 +18,7 @@ import {
} from "@rallly/ui/dropdown-menu";
import { Icon } from "@rallly/ui/icon";
import { Input } from "@rallly/ui/input";
import { toast } from "@rallly/ui/sonner";
import { Textarea } from "@rallly/ui/textarea";
import dayjs from "dayjs";
import {
@ -27,7 +28,6 @@ import {
} from "lucide-react";
import * as React from "react";
import { Controller, useForm } from "react-hook-form";
import { OptimizedAvatarImage } from "@/components/optimized-avatar-image";
import { Participant, ParticipantName } from "@/components/participant";
import { useParticipants } from "@/components/participants-provider";
@ -36,8 +36,6 @@ import { usePoll } from "@/contexts/poll";
import { useRole } from "@/contexts/role";
import { useTranslation } from "@/i18n/client";
import { trpc } from "@/trpc/client";
import { toast } from "@rallly/ui/sonner";
import { requiredString } from "../../utils/form-validation";
import TruncatedLinkify from "../poll/truncated-linkify";
import { useUser } from "../user-provider";

View file

@ -4,10 +4,9 @@ import { Card, CardContent, CardDescription } from "@rallly/ui/card";
import { Icon } from "@rallly/ui/icon";
import dayjs from "dayjs";
import { DotIcon, MapPinIcon, PauseIcon } from "lucide-react";
import { PollStatusBadge } from "@/components/poll-status";
import TruncatedLinkify from "@/components/poll/truncated-linkify";
import VoteIcon from "@/components/poll/vote-icon";
import { PollStatusBadge } from "@/components/poll-status";
import { RandomGradientBar } from "@/components/random-gradient-bar";
import { Trans } from "@/components/trans";
import { usePoll } from "@/contexts/poll";

View file

@ -2,7 +2,6 @@
import { DateLocalizer } from "react-big-calendar";
const weekRangeFormat = ({ start, end }, culture, local) =>
// biome-ignore lint/style/useTemplate: Fix this later
local.format(start, "MMMM DD", culture) +
" " +
local.format(end, local.eq(start, end, "month") ? "DD" : "MMMM DD", culture);
@ -245,7 +244,7 @@ export default function (dayjs) {
return mStart.isBefore(mFirst, "day");
}
function continuesAfter(start, end, last) {
function continuesAfter(_start, end, last) {
const mEnd = dayjs(end);
const mLast = dayjs(last);
return mEnd.isSameOrAfter(mLast, "minutes");

View file

@ -25,8 +25,6 @@ import { useFormContext } from "react-hook-form";
import type { NewEventData } from "@/components/forms";
import { Trans } from "@/components/trans";
import { useTranslation } from "@/i18n/client";
import type { DateTimeOption } from "..";
import {
expectTimeOption,
getBrowserTimeZone,
@ -35,6 +33,7 @@ import {
} from "../../../../utils/date-time-utils";
import DateCard from "../../../date-card";
import { useHeadlessDatePicker } from "../../../headless-date-picker";
import type { DateTimeOption } from "..";
import type { DateTimePickerProps } from "../types";
import { formatDateWithoutTime, formatDateWithoutTz } from "../utils";
import TimePicker from "./time-picker";
@ -412,10 +411,8 @@ const MonthCalendar: React.FunctionComponent<DateTimePickerProps> = ({
},
);
const newOptions: DateTimeOption[] = [];
// biome-ignore lint/complexity/noForEach: Fix this later
Object.keys(optionsByDay).forEach(
(dateString) => {
// biome-ignore lint/complexity/noForEach: Fix this later
times.forEach((time) => {
const start =
dateString + time.startTime;

View file

@ -96,196 +96,194 @@ const PollOptionsForm = ({
const navigationDate = new Date(watchNavigationDate ?? Date.now());
return (
<>
<Card>
<CardHeader>
<div className="flex flex-col justify-between gap-4 sm:flex-row">
<div>
<CardTitle>
<Trans i18nKey="calendar">Calendar</Trans>
</CardTitle>
<CardDescription>
<Trans i18nKey="selectPotentialDates">
Select potential dates for your event
</Trans>
</CardDescription>
</div>
<div>
<FormField
control={form.control}
name="view"
render={({ field }) => (
<Tabs value={field.value} onValueChange={field.onChange}>
<TabsList className="w-full">
<TabsTrigger className="grow" value="month">
<CalendarIcon className="mr-2 size-4" />
<Trans i18nKey="monthView" />
</TabsTrigger>
<TabsTrigger className="grow" value="week">
<TableIcon className="mr-2 size-4" />
<Trans i18nKey="weekView" />
</TabsTrigger>
</TabsList>
</Tabs>
)}
/>
</div>
<Card>
<CardHeader>
<div className="flex flex-col justify-between gap-4 sm:flex-row">
<div>
<CardTitle>
<Trans i18nKey="calendar">Calendar</Trans>
</CardTitle>
<CardDescription>
<Trans i18nKey="selectPotentialDates">
Select potential dates for your event
</Trans>
</CardDescription>
</div>
</CardHeader>
<Dialog {...dateOrTimeRangeDialog.dialogProps}>
<DialogContent>
<DialogHeader>
<DialogTitle>
<Trans i18nKey="mixedOptionsTitle" />
</DialogTitle>
</DialogHeader>
<p className="text-sm">
<Trans i18nKey="mixedOptionsDescription" />
</p>
<DialogFooter>
<Button
onClick={() => {
setValue(
"options",
watchOptions.filter((option) => option.type === "date"),
);
setValue("timeZone", "");
dateOrTimeRangeDialog.dismiss();
<div>
<FormField
control={form.control}
name="view"
render={({ field }) => (
<Tabs value={field.value} onValueChange={field.onChange}>
<TabsList className="w-full">
<TabsTrigger className="grow" value="month">
<CalendarIcon className="mr-2 size-4" />
<Trans i18nKey="monthView" />
</TabsTrigger>
<TabsTrigger className="grow" value="week">
<TableIcon className="mr-2 size-4" />
<Trans i18nKey="weekView" />
</TabsTrigger>
</TabsList>
</Tabs>
)}
/>
</div>
</div>
</CardHeader>
<Dialog {...dateOrTimeRangeDialog.dialogProps}>
<DialogContent>
<DialogHeader>
<DialogTitle>
<Trans i18nKey="mixedOptionsTitle" />
</DialogTitle>
</DialogHeader>
<p className="text-sm">
<Trans i18nKey="mixedOptionsDescription" />
</p>
<DialogFooter>
<Button
onClick={() => {
setValue(
"options",
watchOptions.filter((option) => option.type === "date"),
);
setValue("timeZone", "");
dateOrTimeRangeDialog.dismiss();
}}
>
<Trans i18nKey="mixedOptionsKeepDates" />
</Button>
<Button
onClick={() => {
setValue(
"options",
watchOptions.filter((option) => option.type === "timeSlot"),
);
if (!watchTimeZone) {
setValue("timeZone", getBrowserTimeZone());
}
dateOrTimeRangeDialog.dismiss();
}}
variant="primary"
>
<Trans i18nKey="mixedOptionsKeepTimes" />
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
<div>
<FormField
control={form.control}
name="options"
rules={{
validate: (options) => {
return options.length > 0
? true
: t("calendarHelp", {
defaultValue:
"You can't create a poll without any options. Add at least one option to continue.",
});
},
}}
render={({ field }) => (
<div>
<selectedView.Component
options={field.value}
date={navigationDate}
onNavigate={(date) => {
setValue("navigationDate", date.toISOString());
}}
>
<Trans i18nKey="mixedOptionsKeepDates" />
</Button>
<Button
onClick={() => {
setValue(
"options",
watchOptions.filter((option) => option.type === "timeSlot"),
);
if (!watchTimeZone) {
setValue("timeZone", getBrowserTimeZone());
}
dateOrTimeRangeDialog.dismiss();
onChange={(options) => {
field.onChange(options);
}}
variant="primary"
>
<Trans i18nKey="mixedOptionsKeepTimes" />
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
<div>
<FormField
control={form.control}
name="options"
rules={{
validate: (options) => {
return options.length > 0
? true
: t("calendarHelp", {
defaultValue:
"You can't create a poll without any options. Add at least one option to continue.",
});
},
}}
render={({ field }) => (
<div>
<selectedView.Component
options={field.value}
date={navigationDate}
onNavigate={(date) => {
setValue("navigationDate", date.toISOString());
}}
onChange={(options) => {
field.onChange(options);
}}
duration={watchDuration}
onChangeDuration={(duration) => {
setValue("duration", duration);
duration={watchDuration}
onChangeDuration={(duration) => {
setValue("duration", duration);
}}
/>
{formState.errors.options ? (
<div className="border-t bg-red-50 p-3 text-center">
<FormMessage />
</div>
) : null}
</div>
)}
/>
</div>
{!datesOnly ? (
<FormField
control={form.control}
name="timeZone"
render={({ field }) => (
<div
className={cn(
"grid items-center justify-between gap-2.5 border-t bg-gray-50 p-4 md:flex",
)}
>
<div className="flex h-9 items-center gap-x-2.5 p-2">
<Switch
id="timeZone"
disabled={disableTimeZoneChange}
checked={!!field.value}
onCheckedChange={(checked) => {
if (checked) {
field.onChange(getBrowserTimeZone());
} else {
field.onChange("");
}
}}
/>
{formState.errors.options ? (
<div className="border-t bg-red-50 p-3 text-center">
<FormMessage />
</div>
) : null}
</div>
)}
/>
</div>
{!datesOnly ? (
<FormField
control={form.control}
name="timeZone"
render={({ field }) => (
<div
className={cn(
"grid items-center justify-between gap-2.5 border-t bg-gray-50 p-4 md:flex",
)}
>
<div className="flex h-9 items-center gap-x-2.5 p-2">
<Switch
id="timeZone"
disabled={disableTimeZoneChange}
checked={!!field.value}
onCheckedChange={(checked) => {
if (checked) {
field.onChange(getBrowserTimeZone());
} else {
field.onChange("");
}
}}
<Label htmlFor="timeZone">
<Trans
i18nKey="autoTimeZone"
defaults="Automatic Time Zone Conversion"
/>
<Label htmlFor="timeZone">
</Label>
<Tooltip>
<TooltipTrigger type="button">
<InfoIcon className="size-4 text-muted-foreground" />
</TooltipTrigger>
<TooltipContent className="w-72">
<Trans
i18nKey="autoTimeZone"
defaults="Automatic Time Zone Conversion"
i18nKey="autoTimeZoneHelp"
defaults="Enable this setting to automatically adjust event times to each participant's local time zone."
/>
</Label>
<Tooltip>
<TooltipTrigger type="button">
<InfoIcon className="size-4 text-muted-foreground" />
</TooltipTrigger>
<TooltipContent className="w-72">
<Trans
i18nKey="autoTimeZoneHelp"
defaults="Enable this setting to automatically adjust event times to each participant's local time zone."
/>
</TooltipContent>
</Tooltip>
</div>
{field.value ? (
<div>
<Button
disabled={disableTimeZoneChange}
onClick={() => {
showTimeZoneCommandModal(true);
}}
variant="ghost"
>
<GlobeIcon className="size-4 text-muted-foreground" />
{field.value}
</Button>
<CommandDialog
open={isTimeZoneCommandModalOpen}
onOpenChange={showTimeZoneCommandModal}
>
<TimeZoneCommand
value={field.value}
onSelect={(newValue) => {
field.onChange(newValue);
showTimeZoneCommandModal(false);
}}
/>
</CommandDialog>
</div>
) : null}
</TooltipContent>
</Tooltip>
</div>
)}
/>
) : null}
{children}
</Card>
</>
{field.value ? (
<div>
<Button
disabled={disableTimeZoneChange}
onClick={() => {
showTimeZoneCommandModal(true);
}}
variant="ghost"
>
<GlobeIcon className="size-4 text-muted-foreground" />
{field.value}
</Button>
<CommandDialog
open={isTimeZoneCommandModalOpen}
onOpenChange={showTimeZoneCommandModal}
>
<TimeZoneCommand
value={field.value}
onSelect={(newValue) => {
field.onChange(newValue);
showTimeZoneCommandModal(false);
}}
/>
</CommandDialog>
</div>
) : null}
</div>
)}
/>
) : null}
{children}
</Card>
);
};

View file

@ -6,13 +6,10 @@
@apply bg-gray-50 bg-opacity-50;
}
.rbc-day-slot .rbc-time-slot {
@apply border-gray-100;
}
.rbc-time-content > * + * > * {
@apply border-gray-200;
}
.rbc-time-header.rbc-overflowing,
.rbc-time-header-content,
.rbc-header {
@ -34,6 +31,7 @@
.rbc-header + .rbc-header {
@apply border-l-0;
}
.rbc-time-slot {
@apply pl-2 pt-1;
}
@ -42,6 +40,10 @@
@apply border-gray-100;
}
.rbc-day-slot .rbc-time-slot {
@apply border-gray-100;
}
.rbc-day-slot .rbc-time-slot {
@apply border-dashed border-gray-50;
}
@ -49,9 +51,11 @@
.rbc-day-slot .rbc-events-container {
@apply mr-2;
}
.rbc-slot-selection {
@apply bg-gray-100/50 leading-tight text-gray-600;
}
.rbc-header.rbc-today {
@apply bg-white text-rose-600;
}
@ -59,6 +63,7 @@
.rbc-button-link {
@apply pointer-events-none m-1 w-full;
}
.rbc-time-content > * + * > * {
@apply border-gray-100;
}

View file

@ -98,6 +98,7 @@ const WeekCalendar: React.FunctionComponent<DateTimePickerProps> = ({
const start = dayjs(props.event.start);
const end = dayjs(props.event.end);
return (
// biome-ignore lint/a11y/noStaticElementInteractions: fix later
<div
// onClick prop doesn't work properly. Seems like some other element is cancelling the event before it reaches this element
onMouseUp={props.onClick}

View file

@ -6,7 +6,9 @@ export function FullWidthLayout({ children }: { children: React.ReactNode }) {
export function FullWidthLayoutHeader({
children,
}: { children: React.ReactNode }) {
}: {
children: React.ReactNode;
}) {
return (
<header className="sticky top-0 z-10 rounded-t-lg border-b bg-background/90 px-3 py-4 backdrop-blur-sm md:px-6">
<div className="flex items-center gap-4">
@ -21,14 +23,19 @@ export function FullWidthLayoutHeader({
export function FullWidthLayoutContent({
children,
}: { children: React.ReactNode }) {
}: {
children: React.ReactNode;
}) {
return <main className="p-3 pb-44 md:px-6 md:pt-6">{children}</main>;
}
export function FullWidthLayoutTitle({
children,
icon,
}: { children: React.ReactNode; icon?: React.ReactNode }) {
}: {
children: React.ReactNode;
icon?: React.ReactNode;
}) {
return (
<div className="flex items-center gap-2.5">
{icon}

View file

@ -11,9 +11,8 @@ import { useForm } from "react-hook-form";
import z from "zod";
import { usePoll } from "@/contexts/poll";
import { useTranslation } from "@/i18n/client";
import { useTimezone } from "@/features/timezone";
import { useTranslation } from "@/i18n/client";
import { useAddParticipantMutation } from "./poll/mutations";
import VoteIcon from "./poll/vote-icon";
import { useUser } from "./user-provider";

View file

@ -13,6 +13,7 @@ export const ParticipantName = ({
const ref = React.useRef<HTMLDivElement>(null);
const [isTruncated, setIsTruncated] = React.useState(false);
return (
// biome-ignore lint/a11y/noStaticElementInteractions: fix later
<div
ref={ref}
onMouseEnter={() => {

View file

@ -59,7 +59,6 @@ export const PollContextProvider: React.FunctionComponent<{
(optionId: string) => {
return (participants ?? []).reduce(
(acc, curr) => {
// biome-ignore lint/complexity/noForEach: Fix this later
curr.votes.forEach((vote) => {
if (vote.optionId !== optionId) {
return;
@ -104,7 +103,6 @@ export const PollContextProvider: React.FunctionComponent<{
);
const participantsByOptionId: Record<string, Participant[]> = {};
// biome-ignore lint/complexity/noForEach: Fix this later
poll.options.forEach((option) => {
participantsByOptionId[option.id] = (participants ?? []).filter(
(participant) =>

View file

@ -19,7 +19,7 @@ import { Trans } from "@/components/trans";
import { useTranslation } from "@/i18n/client";
import { usePoll } from "../../poll-context";
import { VoteSelector, toggleVote } from "../vote-selector";
import { toggleVote, VoteSelector } from "../vote-selector";
export interface ParticipantRowFormProps {
name?: string;
@ -114,6 +114,7 @@ const ParticipantRowForm = ({
name={`votes.${i}`}
render={({ field }) => (
// biome-ignore lint/a11y/useKeyWithClickEvents: Fix later
// biome-ignore lint/a11y/noStaticElementInteractions: Fix later
<div
onClick={() => {
field.onChange({

View file

@ -7,9 +7,8 @@ import {
} from "@rallly/ui/tooltip";
import { ClockIcon } from "lucide-react";
import type * as React from "react";
import { useOptions } from "@/components/poll-context";
import { ConnectedScoreSummary } from "@/components/poll/score-summary";
import { useOptions } from "@/components/poll-context";
import { Trans } from "@/components/trans";
const TimeRange: React.FunctionComponent<{

View file

@ -52,7 +52,6 @@ const useScoreByOptionId = () => {
return React.useMemo(() => {
const scoreByOptionId: Record<string, OptionScore> = {};
// biome-ignore lint/complexity/noForEach: Fix this later
options.forEach((option) => {
scoreByOptionId[option.id] = {
yes: [],
@ -61,9 +60,7 @@ const useScoreByOptionId = () => {
};
});
// biome-ignore lint/complexity/noForEach: Fix this later
responses?.forEach((response) => {
// biome-ignore lint/complexity/noForEach: Fix this later
response.votes.forEach((vote) => {
scoreByOptionId[vote.optionId]?.[vote.type].push(response.id);
});

View file

@ -19,9 +19,9 @@ import { TimesShownIn } from "@/components/clock";
import { OptimizedAvatarImage } from "@/components/optimized-avatar-image";
import { Participant, ParticipantName } from "@/components/participant";
import { ParticipantDropdown } from "@/components/participant-dropdown";
import { useOptions, usePoll } from "@/components/poll-context";
import { useVotingForm } from "@/components/poll/voting-form";
import { YouAvatar } from "@/components/poll/you-avatar";
import { useOptions, usePoll } from "@/components/poll-context";
import { Trans } from "@/components/trans";
import { usePermissions } from "@/contexts/permissions";
import { useTranslation } from "@/i18n/client";

View file

@ -1,3 +1,6 @@
/** biome-ignore-all lint/a11y/useSemanticElements: fix later */
/** biome-ignore-all lint/a11y/useKeyWithClickEvents: fix later */
/** biome-ignore-all lint/a11y/useFocusableInteractive: fix later */
"use client";
import type { Participant, VoteType } from "@rallly/database";
import { cn } from "@rallly/ui";
@ -114,8 +117,8 @@ const PollOption: React.FunctionComponent<PollOptionProps> = ({
const [active, setActive] = React.useState(false);
const [isExpanded, toggle] = useToggle(false);
return (
// biome-ignore lint/a11y/useKeyWithClickEvents: Fix this later
<div
role="button"
className={cn("space-y-4 bg-white p-4", {
"bg-gray-500/5": editable && active,
})}

View file

@ -1,9 +1,8 @@
import type { VoteType } from "@rallly/database";
import type * as React from "react";
import { Controller } from "react-hook-form";
import { usePoll } from "@/components/poll-context";
import { useVotingForm } from "@/components/poll/voting-form";
import { usePoll } from "@/components/poll-context";
import type { ParsedDateTimeOpton } from "@/utils/date-time-utils";
import DateOption from "./date-option";

View file

@ -1,9 +1,7 @@
import { usePostHog } from "@rallly/posthog/client";
import { toast } from "@rallly/ui/sonner";
import { usePoll } from "@/components/poll-context";
import { trpc } from "@/trpc/client";
import { toast } from "@rallly/ui/sonner";
import type { ParticipantForm } from "./types";
export const normalizeVotes = (

View file

@ -1,14 +1,13 @@
"use client";
import { Badge } from "@rallly/ui/badge";
import { CalendarIcon } from "lucide-react";
import { AddToCalendarButton } from "@/components/add-to-calendar-button";
import { ParticipantAvatarBar } from "@/components/participant-avatar-bar";
import { Trans } from "@/components/trans";
import { IfParticipantsVisible } from "@/components/visibility";
import { usePoll } from "@/contexts/poll";
import { useDayjs } from "@/utils/dayjs";
import { Badge } from "@rallly/ui/badge";
function FinalDate({ start }: { start: Date }) {
const poll = usePoll();

View file

@ -1,11 +1,10 @@
import type { VoteType } from "@rallly/database";
import { cn } from "@rallly/ui";
import { cva, type VariantProps } from "class-variance-authority";
import { IfNeedBeIcon } from "@/components/vote-icon/if-need-be-icon";
import { NoIcon } from "@/components/vote-icon/no-icon";
import { PendingIcon } from "@/components/vote-icon/pending-icon";
import { YesIcon } from "@/components/vote-icon/yes-icon";
import { cn } from "@rallly/ui";
import { type VariantProps, cva } from "class-variance-authority";
const iconVariants = cva("", {
variants: {

View file

@ -8,24 +8,32 @@ export function SettingsGroup({ children }: { children: React.ReactNode }) {
export function SettingsGroupHeader({
children,
}: { children: React.ReactNode }) {
}: {
children: React.ReactNode;
}) {
return <div className="lg:w-1/3">{children}</div>;
}
export function SettingsGroupTitle({
children,
}: { children: React.ReactNode }) {
}: {
children: React.ReactNode;
}) {
return <h2 className="font-semibold text-base">{children}</h2>;
}
export function SettingsGroupDescription({
children,
}: { children: React.ReactNode }) {
}: {
children: React.ReactNode;
}) {
return <p className="mt-1 text-muted-foreground text-sm">{children}</p>;
}
export function SettingsGroupContent({
children,
}: { children: React.ReactNode }) {
}: {
children: React.ReactNode;
}) {
return <div className="flex-1">{children}</div>;
}

View file

@ -1,7 +1,7 @@
"use client";
import { usePostHog } from "@rallly/posthog/client";
import { signIn, signOut } from "next-auth/react";
import { useRouter } from "next/navigation";
import { signIn, signOut } from "next-auth/react";
import React from "react";
import { useTranslation } from "@/i18n/client";

View file

@ -1,9 +1,9 @@
export const isSelfHosted = process.env.NEXT_PUBLIC_SELF_HOSTED === "true";
export const IfSelfHosted = ({ children }: React.PropsWithChildren) => {
return isSelfHosted ? <>{children}</> : null;
return isSelfHosted ? children : null;
};
export const IfCloudHosted = ({ children }: React.PropsWithChildren) => {
return isSelfHosted ? null : <>{children}</>;
return isSelfHosted ? null : children;
};

View file

@ -1,9 +1,9 @@
import type { PureAbility } from "@casl/ability";
import { AbilityBuilder } from "@casl/ability";
import {
createPrismaAbility,
type PrismaQuery,
type Subjects,
createPrismaAbility,
} from "@casl/prisma";
import type {
Comment,

View file

@ -30,13 +30,14 @@ import type { Feedback } from "../schema";
import { feedbackSchema } from "../schema";
export function FeedbackToggle() {
const form = useForm<Feedback>({
resolver: zodResolver(feedbackSchema),
});
if (isSelfHosted) {
return null;
}
const form = useForm<Feedback>({
resolver: zodResolver(feedbackSchema),
});
return (
<Dialog>
<Tooltip>

View file

@ -1,8 +1,8 @@
"use server";
import { requireAdmin } from "@/auth/queries";
import { type InstanceSettings, prisma } from "@rallly/database";
import { revalidateTag } from "next/cache";
import { requireAdmin } from "@/auth/queries";
import { instanceSettingsTag } from "./constants";
export async function updateInstanceSettings(data: Partial<InstanceSettings>) {

View file

@ -1,7 +1,7 @@
"use server";
import { rateLimit } from "@/features/rate-limit";
import { prisma } from "@rallly/database";
import { rateLimit } from "@/features/rate-limit";
import { licensingClient } from "../client";
export async function validateLicenseKey(key: string) {

View file

@ -1,7 +1,5 @@
"use client";
import { Trans } from "@/components/trans";
import { useTranslation } from "@/i18n/client";
import { zodResolver } from "@hookform/resolvers/zod";
import { Button } from "@rallly/ui/button";
import {
@ -16,6 +14,8 @@ import { Input } from "@rallly/ui/input";
import { useRouter } from "next/navigation";
import { useForm } from "react-hook-form";
import { z } from "zod";
import { Trans } from "@/components/trans";
import { useTranslation } from "@/i18n/client";
import { validateLicenseKey } from "../actions/validate-license";
import { checkLicenseKey } from "../helpers/check-license-key";
@ -68,7 +68,7 @@ export function LicenseKeyForm() {
}
return;
}
} catch (error) {
} catch (_error) {
form.setError("licenseKey", {
message: t("licenseKeyGenericError", {
defaultValue: "An error occurred while validating the license key",

View file

@ -1,8 +1,8 @@
import Link from "next/link";
import { Trans } from "@/components/trans";
import { getLicense } from "@/features/licensing/queries";
import { getUserCount } from "@/features/user/queries";
import { isSelfHosted } from "@/utils/constants";
import Link from "next/link";
export async function LicenseLimitWarning() {
if (!isSelfHosted) {

View file

@ -1,6 +1,5 @@
"use client";
import { Trans } from "@/components/trans";
import { Button } from "@rallly/ui/button";
import {
Dialog,
@ -17,6 +16,7 @@ import { Icon } from "@rallly/ui/icon";
import { XIcon } from "lucide-react";
import { useRouter } from "next/navigation";
import { useTransition } from "react";
import { Trans } from "@/components/trans";
import { removeInstanceLicense } from "../mutations";
export function RemoveLicenseButton() {

View file

@ -1,7 +1,7 @@
import {
type CreateLicenseInput,
type ValidateLicenseInputKeySchema,
createLicenseResponseSchema,
type ValidateLicenseInputKeySchema,
validateLicenseKeyResponseSchema,
} from "../schema";

View file

@ -1,12 +1,12 @@
"use server";
import { requireAdmin } from "@/auth/queries";
import { prisma } from "@rallly/database";
import { requireAdmin } from "@/auth/queries";
export async function removeInstanceLicense() {
try {
await requireAdmin();
} catch (error) {
} catch (_error) {
return {
success: false,
message: "You must be an admin to delete a license",
@ -15,7 +15,7 @@ export async function removeInstanceLicense() {
try {
await prisma.instanceLicense.deleteMany();
} catch (error) {
} catch (_error) {
return {
success: false,
message: "Failed to delete license",

View file

@ -10,7 +10,7 @@ export function containsSuspiciousPatterns(text: string) {
const repetitiveCharsPattern = /(.)\1{4,}/;
const allCapsPattern = /[A-Z]{5,}/;
const excessiveSpecialCharsPattern =
/[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]{4,}/;
/[!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?]{4,}/;
const suspiciousUrlPattern = /(bit\.ly|tinyurl|goo\.gl|t\.co|is\.gd)/i;
// Email address pattern
@ -18,7 +18,7 @@ export function containsSuspiciousPatterns(text: string) {
// Comprehensive phone number pattern covering various formats
const phoneNumberPattern =
/(\+?\d{1,3}[-.\s]?)?(\d{3}[-.\s]?)?\d{3}[-.\s]?\d{4}|\+\d[\d\s\-\.]{5,14}|\+\d{6,15}/i;
/(\+?\d{1,3}[-.\s]?)?(\d{3}[-.\s]?)?\d{3}[-.\s]?\d{4}|\+\d[\d\s\-.]{5,14}|\+\d{6,15}/i;
// Detect suspicious Unicode patterns - simplified version without surrogate pairs
const suspiciousUnicodePattern =

View file

@ -9,6 +9,7 @@ import {
CommandList,
} from "@rallly/ui/command";
import { DialogDescription, DialogTitle, useDialog } from "@rallly/ui/dialog";
import { Icon } from "@rallly/ui/icon";
import {
ArrowRightIcon,
KeySquareIcon,
@ -17,7 +18,6 @@ import {
UsersIcon,
} from "lucide-react";
import { useRouter } from "next/navigation";
import {
BillingPageIcon,
EventPageIcon,
@ -28,17 +28,11 @@ import {
ProfilePageIcon,
} from "@/app/components/page-icons";
import { Trans } from "@/components/trans";
import { useUser } from "@/components/user-provider";
import { useTranslation } from "@/i18n/client";
import { Icon } from "@rallly/ui/icon";
import { CommandGlobalShortcut } from "./command-global-shortcut";
function NavigationCommandLabel({
label,
}: {
label: string;
}) {
function NavigationCommandLabel({ label }: { label: string }) {
return (
<div>
<Trans

View file

@ -1,6 +1,6 @@
import { getActiveSpace } from "@/auth/queries";
import type { PollStatus, Prisma } from "@rallly/database";
import { prisma } from "@rallly/database";
import { getActiveSpace } from "@/auth/queries";
type PollFilters = {
status?: PollStatus;

View file

@ -1,7 +1,7 @@
"use client";
import { useTranslation } from "@/i18n/client";
import { toast } from "@rallly/ui/sonner";
import { useAction } from "next-safe-action/hooks";
import { useTranslation } from "@/i18n/client";
export const useSafeAction: typeof useAction = (action, options) => {
const { t } = useTranslation();

View file

@ -1,10 +1,10 @@
"server-only";
import { requireUserAbility } from "@/auth/queries";
import { posthog } from "@rallly/posthog/server";
import { waitUntil } from "@vercel/functions";
import { createMiddleware, createSafeActionClient } from "next-safe-action";
import { revalidatePath } from "next/cache";
import { createMiddleware, createSafeActionClient } from "next-safe-action";
import z from "zod";
import { requireUserAbility } from "@/auth/queries";
type ActionErrorCode =
| "UNAUTHORIZED"

View file

@ -1,9 +1,9 @@
"use server";
import { ActionError, authActionClient } from "@/features/safe-action/server";
import { getEmailClient } from "@/utils/emails";
import { subject } from "@casl/ability";
import { prisma } from "@rallly/database";
import { z } from "zod";
import { ActionError, authActionClient } from "@/features/safe-action/server";
import { getEmailClient } from "@/utils/emails";
import { formatEventDateTime } from "./utils";
export const cancelEventAction = authActionClient

Some files were not shown because too many files have changed in this diff Show more