mirror of
https://github.com/lukevella/rallly.git
synced 2025-07-22 10:47:26 +02:00
⬆️ Upgrade to biome 2 (#1825)
This commit is contained in:
parent
56c74d5024
commit
9be12f28d9
141 changed files with 663 additions and 790 deletions
|
@ -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";
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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'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'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'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'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>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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 }));
|
||||
|
|
|
@ -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 (
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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" })
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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"]);
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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 }) {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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" })
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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 (
|
||||
|
|
|
@ -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>;
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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 = () => {
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -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")) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"]);
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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>;
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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={() => {
|
||||
|
|
|
@ -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) =>
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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<{
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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,
|
||||
})}
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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 = (
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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>;
|
||||
}
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import {
|
||||
type CreateLicenseInput,
|
||||
type ValidateLicenseInputKeySchema,
|
||||
createLicenseResponseSchema,
|
||||
type ValidateLicenseInputKeySchema,
|
||||
validateLicenseKeyResponseSchema,
|
||||
} from "../schema";
|
||||
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue