Replace eslint with biome

This commit is contained in:
Luke Vella 2024-10-13 10:52:11 +01:00
parent 251e517810
commit cfb9ec8e8a
No known key found for this signature in database
GPG key ID: 469CAD687F0D784C
183 changed files with 422 additions and 409 deletions

View file

@ -24,15 +24,18 @@ jobs:
run: pnpm type-check
linting:
name: Linting
name: Linting and Formatting
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup-node
- uses: ./.github/actions/pnpm-install
- name: Check linting rules
run: pnpm lint
- name: Install Biome
run: pnpm add -D @biomejs/biome
- name: Check linting and formatting
run: pnpm ci
unit-tests:
name: Unit tests

View file

@ -1,7 +1,10 @@
{
"editor.codeActionsOnSave": {
"source.fixAll": "explicit"
"source.fixAll.eslint": "never",
"source.organizeImports.biome": "explicit",
"quickfix.biome": "explicit"
},
"editor.defaultFormatter": "biomejs.biome",
"typescript.tsdk": "node_modules/typescript/lib",
"typescript.preferences.importModuleSpecifier": "non-relative",
"cSpell.words": ["Rallly", "Vella"]

View file

@ -1,4 +0,0 @@
/** @type {import("eslint").Linter.Config} */
module.exports = {
...require("@rallly/eslint-config")(__dirname),
};

View file

@ -1,4 +0,0 @@
/** @type {import("eslint").Linter.Config} */
module.exports = {
...require("@rallly/eslint-config")(__dirname),
};

View file

@ -1,9 +1,9 @@
import "react-i18next";
import blog from "../public/locales/en/blog.json";
import common from "../public/locales/en/common.json";
import home from "../public/locales/en/home.json";
import pricing from "../public/locales/en/pricing.json";
import type blog from "../public/locales/en/blog.json";
import type common from "../public/locales/en/common.json";
import type home from "../public/locales/en/home.json";
import type pricing from "../public/locales/en/pricing.json";
interface I18nNamespaces {
common: typeof common;

View file

@ -7,7 +7,6 @@
"build": "next build",
"analyze": "cross-env ANALYZE=true next build",
"start": "next start",
"lint": "eslint .",
"type-check": "tsc --pretty --noEmit",
"i18n:scan": "i18next-scanner --config i18next-scanner.config.js",
"prettier": "prettier --write ./src"
@ -39,7 +38,6 @@
},
"devDependencies": {
"@next/bundle-analyzer": "^12.3.4",
"@rallly/eslint-config": "workspace:*",
"@rallly/tsconfig": "workspace:*",
"@types/accept-language-parser": "^1.5.3",
"@types/color-hash": "^1.0.2",

View file

@ -1,9 +1,9 @@
(function (w, d, e, u, f, l, n) {
((w, d, e, u, f, l, n) => {
(w[f] =
w[f] ||
function () {
(() => {
(w[f].q = w[f].q || []).push(arguments);
}),
})),
(l = d.createElement(e)),
(l.async = 1),
(l.src = u),

View file

@ -19,7 +19,7 @@ import {
import Image from "next/image";
import Link from "next/link";
import { usePathname, useRouter } from "next/navigation";
import * as React from "react";
import type * as React from "react";
import { useTranslation } from "react-i18next";
import { Trans } from "@/components/trans";
@ -302,7 +302,7 @@ export const Footer: React.FunctionComponent = () => {
<a
target="_blank"
href="https://vercel.com?utm_source=rallly&utm_campaign=oss"
className="inline-block text-white"
className="inline-block text-white" rel="noreferrer"
>
<Image
src="/static/images/partners/vercel-logotype-dark.svg"
@ -316,7 +316,7 @@ export const Footer: React.FunctionComponent = () => {
<a
target="_blank"
className="inline-block"
href="/partners/digitalocean"
href="/partners/digitalocean" rel="noreferrer"
>
<Image
src="/static/images/partners/digitalocean-logo.svg"
@ -330,7 +330,7 @@ export const Footer: React.FunctionComponent = () => {
<a
target="_blank"
className="inline-block"
href="https://sentry.io"
href="https://sentry.io" rel="noreferrer"
>
<Image
src="/static/images/partners/sentry.svg"

View file

@ -11,7 +11,7 @@ import {
} from "@rallly/ui/dropdown-menu";
import { Analytics } from "@vercel/analytics/react";
import { ChevronRightIcon, MenuIcon } from "lucide-react";
import { Viewport } from "next";
import type { Viewport } from "next";
import Image from "next/image";
import Link from "next/link";
import { Trans } from "react-i18next/TransWithoutContext";

View file

@ -1,4 +1,4 @@
import { TFunction } from "i18next";
import type { TFunction } from "i18next";
import { TrendingUpIcon } from "lucide-react";
import Link from "next/link";
import { Trans } from "react-i18next/TransWithoutContext";

View file

@ -7,7 +7,8 @@ type Props = {
const PostBody = ({ content }: Props) => {
return (
<div
className={markdownStyles["markdown"]}
className={markdownStyles.markdown}
// biome-ignore lint/security/noDangerouslySetInnerHtml: <explanation>
dangerouslySetInnerHTML={{ __html: content }}
/>
);

View file

@ -4,7 +4,7 @@ import { Button } from "@rallly/ui/button";
import { FileSearchIcon } from "lucide-react";
import Link from "next/link";
import { useTranslation } from "next-i18next";
import * as React from "react";
import type * as React from "react";
export interface ComponentProps {
icon?: React.ReactNode;

View file

@ -1,5 +1,5 @@
import { prisma } from "@rallly/database";
import { TFunction } from "i18next";
import type { TFunction } from "i18next";
import {
CalendarCheck2Icon,
LanguagesIcon,

View file

@ -3,7 +3,7 @@ import { motion } from "framer-motion";
import { ArrowUpRight } from "lucide-react";
import Image from "next/image";
import Link from "next/link";
import React from "react";
import type React from "react";
import { Trans } from "@/components/trans";

View file

@ -1,8 +1,8 @@
"use client";
import i18next, { Namespace } from "i18next";
import i18next, { type Namespace } from "i18next";
import ICU from "i18next-icu";
import resourcesToBackend from "i18next-resources-to-backend";
import React from "react";
import type React from "react";
import {
I18nextProvider,
initReactI18next,

View file

@ -1,4 +1,4 @@
import { createInstance, Namespace } from "i18next";
import { createInstance, type Namespace } from "i18next";
import ICU from "i18next-icu";
import resourcesToBackend from "i18next-resources-to-backend";
import { initReactI18next } from "react-i18next/initReactI18next";

View file

@ -1,5 +1,5 @@
import allLanguages from "@rallly/languages";
import { InitOptions } from "i18next";
import type { InitOptions } from "i18next";
export const fallbackLng = "en";
export const languages = Object.keys(allLanguages);

View file

@ -1,6 +1,6 @@
import { supportedLngs } from "@rallly/languages";
import languageParser from "accept-language-parser";
import { NextRequest, NextResponse } from "next/server";
import { type NextRequest, NextResponse } from "next/server";
export async function getLocaleFromHeader(req: NextRequest) {
const headers = req.headers;

View file

@ -1,6 +1,5 @@
/* eslint-disable @next/next/no-img-element */
import { ImageResponse } from "next/og";
import { NextRequest } from "next/server";
import type { NextRequest } from "next/server";
export const config = {
runtime: "edge",

View file

@ -1,5 +1,4 @@
import { NextPage } from "next";
import React from "react";
import type React from "react";
export type ReactTag = keyof JSX.IntrinsicElements;
@ -7,19 +6,6 @@ export type PropsOf<TTag extends ReactTag> = TTag extends React.ElementType
? React.ComponentProps<TTag>
: never;
// eslint-disable-next-line @typescript-eslint/ban-types
export type NextPageWithLayout<P = {}, IP = P> = NextPage<P, IP> & {
getLayout?: (page: React.ReactElement) => React.ReactNode;
};
// eslint-disable-next-line @typescript-eslint/ban-types
export type PropsWithClassName<TProps extends Record<string, unknown> = {}> =
React.PropsWithChildren<TProps> & {
className?: string;
};
export type IconComponent = React.ComponentType<PropsWithClassName>;
export type Post = {
slug: string;
title: string;

View file

@ -1,4 +0,0 @@
/** @type {import("eslint").Linter.Config} */
module.exports = {
...require("@rallly/eslint-config")(__dirname),
};

View file

@ -1,8 +1,8 @@
import "i18next";
import emails from "@rallly/emails/locales/emails.json";
import type emails from "@rallly/emails/locales/emails.json";
import app from "../public/locales/en/app.json";
import type app from "../public/locales/en/app.json";
interface I18nNamespaces {
app: typeof app;

View file

@ -1,8 +1,7 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import type { TimeFormat } from "@rallly/database";
import { extend } from "lodash";
import NextAuth, { DefaultSession, DefaultUser } from "next-auth";
import { DefaultJWT, JWT } from "next-auth/jwt";
import NextAuth, { type DefaultSession, type DefaultUser } from "next-auth";
import { type DefaultJWT, JWT } from "next-auth/jwt";
declare module "next-auth" {
/**

View file

@ -8,7 +8,6 @@
"build:test": "NODE_ENV=test next build",
"analyze": "cross-env ANALYZE=true next build",
"start": "next start",
"lint": "eslint .",
"type-check": "tsc --pretty --noEmit",
"i18n:scan": "i18next-scanner --config i18next-scanner.config.js",
"prettier": "prettier --write ./src",
@ -88,7 +87,6 @@
"devDependencies": {
"@next/env": "^14.2.15",
"@playwright/test": "^1.39.0",
"@rallly/eslint-config": "workspace:*",
"@rallly/tsconfig": "workspace:*",
"@types/accept-language-parser": "^1.5.3",
"@types/color-hash": "^1.0.2",

View file

@ -1,5 +1,5 @@
import { loadEnvConfig } from "@next/env";
import { devices, PlaywrightTestConfig } from "@playwright/test";
import { devices, type PlaywrightTestConfig } from "@playwright/test";
const ci = process.env.CI === "true";

View file

@ -1,6 +1,6 @@
import { cn } from "@rallly/ui";
import { BarChart2Icon } from "lucide-react";
import React from "react";
import type React from "react";
export function AppCard({
children,

View file

@ -4,7 +4,7 @@ import { Card, CardContent } from "@rallly/ui/card";
import { getCoreRowModel, useReactTable } from "@tanstack/react-table";
import dayjs from "dayjs";
import { ScheduledEvent } from "@/app/[locale]/(admin)/events/types";
import type { ScheduledEvent } from "@/app/[locale]/(admin)/events/types";
import { Trans } from "@/components/trans";
import { generateGradient } from "@/utils/color-hash";
import { useDayjs } from "@/utils/dayjs";

View file

@ -1,5 +1,5 @@
import { UserScheduledEvents } from "@/app/[locale]/(admin)/events/user-scheduled-events";
import { Params } from "@/app/[locale]/types";
import type { Params } from "@/app/[locale]/types";
import {
PageContainer,
PageContent,

View file

@ -1,5 +1,5 @@
import { cn } from "@rallly/ui";
import React from "react";
import type React from "react";
import { MobileNavigation } from "@/app/[locale]/(admin)/mobile-navigation";
import { ProBadge } from "@/app/[locale]/(admin)/pro-badge";

View file

@ -4,7 +4,7 @@ import { cn } from "@rallly/ui";
import { Link } from "lucide-react";
import { usePathname } from "next/navigation";
import { IconComponent } from "@/types";
import type { IconComponent } from "@/types";
export function MenuItem({
href,

View file

@ -1,7 +1,7 @@
import { Trans } from "react-i18next/TransWithoutContext";
import { Sidebar } from "@/app/[locale]/(admin)/sidebar";
import { Params } from "@/app/[locale]/types";
import type { Params } from "@/app/[locale]/types";
import {
PageContainer,
PageContent,

View file

@ -2,7 +2,7 @@ import { HomeIcon } from "lucide-react";
import { Trans } from "react-i18next/TransWithoutContext";
import Dashboard from "@/app/[locale]/(admin)/dashboard";
import { Params } from "@/app/[locale]/types";
import type { Params } from "@/app/[locale]/types";
import {
PageContainer,
PageContent,

View file

@ -1,7 +1,7 @@
import { BarChart2Icon } from "lucide-react";
import { UserPolls } from "@/app/[locale]/(admin)/polls/user-polls";
import { Params } from "@/app/[locale]/types";
import type { Params } from "@/app/[locale]/types";
import {
PageContainer,
PageContent,

View file

@ -1,5 +1,5 @@
"use client";
import { PollStatus } from "@rallly/database";
import type { PollStatus } from "@rallly/database";
import { cn } from "@rallly/ui";
import { Badge } from "@rallly/ui/badge";
import { Button } from "@rallly/ui/button";
@ -50,8 +50,8 @@ function FilteredPolls({ status }: { status: PollStatus }) {
return (
<div className="space-y-6">
<ol className="space-y-4">
{data.pages.map((page, i) => (
<li key={i}>
{data.pages.map((page) => (
<li key={page.nextCursor}>
<PollsListView data={page.polls} />
</li>
))}
@ -102,7 +102,7 @@ function useQueryParam(name: string) {
const searchParams = useSearchParams();
return [
searchParams?.get(name),
function (value: string) {
(value: string) => {
const newParams = new URLSearchParams(searchParams?.toString());
newParams.set(name, value);
window.history.replaceState(null, "", `?${newParams.toString()}`);

View file

@ -16,11 +16,11 @@ import { Trans } from "@/components/trans";
import { useSubscription } from "@/contexts/plan";
import { trpc } from "@/utils/trpc/client";
import { BillingPlans, PricingData } from "./billing-plans";
import { BillingPlans, type PricingData } from "./billing-plans";
declare global {
interface Window {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
Paddle: any;
}
}

View file

@ -2,7 +2,7 @@ import { pricingData } from "@rallly/billing/pricing";
import { notFound } from "next/navigation";
import { BillingPage } from "@/app/[locale]/(admin)/settings/billing/billing-page";
import { Params } from "@/app/[locale]/types";
import type { Params } from "@/app/[locale]/types";
import { getTranslation } from "@/app/i18n";
import { env } from "@/env";

View file

@ -1,4 +1,4 @@
import React from "react";
import type React from "react";
import {
PageContainer,

View file

@ -1,4 +1,4 @@
import { Params } from "@/app/[locale]/types";
import type { Params } from "@/app/[locale]/types";
import { getTranslation } from "@/app/i18n";
import { PreferencesPage } from "./preferences-page";

View file

@ -7,7 +7,7 @@ import {
DialogDescription,
DialogFooter,
DialogHeader,
DialogProps,
type DialogProps,
DialogTitle,
} from "@rallly/ui/dialog";
import { Form, FormField, FormItem, FormMessage } from "@rallly/ui/form";

View file

@ -1,4 +1,4 @@
import { Params } from "@/app/[locale]/types";
import type { Params } from "@/app/[locale]/types";
import { getTranslation } from "@/app/i18n";
import { ProfilePage } from "./profile-page";

View file

@ -25,7 +25,7 @@ import { ProBadge } from "@/components/pro-badge";
import { Trans } from "@/components/trans";
import { IfGuest, useUser } from "@/components/user-provider";
import { IfFreeUser } from "@/contexts/plan";
import { IconComponent } from "@/types";
import type { IconComponent } from "@/types";
import { usePostHog } from "@/utils/posthog";
function NavItem({
@ -64,9 +64,9 @@ export function Sidebar() {
const posthog = usePostHog();
return (
<nav className="flex flex-1 flex-col ">
<ul role="list" className="flex flex-1 flex-col gap-y-7">
<ul className="flex flex-1 flex-col gap-y-7">
<li>
<ul role="list" className="-mx-2 space-y-1">
<ul className="-mx-2 space-y-1">
<li>
<NavItem current={pathname === "/"} href="/" icon={HomeIcon}>
<Trans i18nKey="home" defaults="Home" />
@ -103,7 +103,7 @@ export function Sidebar() {
</Button>
</li>
<li className="mt-auto">
<ul role="list" className="-mx-2 space-y-1">
<ul className="-mx-2 space-y-1">
<IfFreeUser>
<li>
<PayWallDialog>
@ -113,7 +113,10 @@ export function Sidebar() {
}
asChild
>
<button className="mb-4 flex w-full flex-col rounded-md border bg-gray-50 px-4 py-3 focus:border-gray-300 focus:bg-gray-200">
<button
type="button"
className="mb-4 flex w-full flex-col rounded-md border bg-gray-50 px-4 py-3 focus:border-gray-300 focus:bg-gray-200"
>
<span className="mb-2 flex items-center gap-x-2">
<SparklesIcon className="size-5 text-gray-400" />
<span className="text-sm font-bold">
@ -162,7 +165,7 @@ export function Sidebar() {
</li>
</ul>
<hr className="my-2" />
<ul role="list" className="-mx-2 space-y-1">
<ul className="-mx-2 space-y-1">
<li>
<Button
asChild

View file

@ -31,8 +31,8 @@ export function LoginForm() {
});
const { data: providers } = useQuery(["providers"], getProviders, {
cacheTime: Infinity,
staleTime: Infinity,
cacheTime: Number.POSITIVE_INFINITY,
staleTime: Number.POSITIVE_INFINITY,
});
const session = useSession();
@ -223,8 +223,8 @@ export function LoginForm() {
</span>
</div>
<div className="grid gap-2.5">
{alternativeLoginMethods.map((method, i) => (
<Button size="lg" key={i} onClick={method.login}>
{alternativeLoginMethods.map((method) => (
<Button size="lg" key={method.name} onClick={method.login}>
{method.icon}
{method.name}
</Button>

View file

@ -2,7 +2,7 @@ import Link from "next/link";
import { Trans } from "react-i18next/TransWithoutContext";
import { LoginForm } from "@/app/[locale]/(auth)/login/login-form";
import { Params } from "@/app/[locale]/types";
import type { Params } from "@/app/[locale]/types";
import { getTranslation } from "@/app/i18n";
import { AuthCard } from "@/components/auth/auth-layout";

View file

@ -1,5 +1,5 @@
import { RegisterForm } from "@/app/[locale]/(auth)/register/register-page";
import { Params } from "@/app/[locale]/types";
import type { Params } from "@/app/[locale]/types";
import { getTranslation } from "@/app/i18n";
export default async function Page() {

View file

@ -4,7 +4,7 @@ import { notFound } from "next/navigation";
import { Redirect } from "@/app/components/redirect";
import { absoluteUrl } from "@/utils/absolute-url";
import { PParams } from "./types";
import type { PParams } from "./types";
export default async function Page({ params }: { params: PParams }) {
const { adminUrlId } = params;

View file

@ -1,4 +1,4 @@
import { Params } from "@/app/[locale]/types";
import type { Params } from "@/app/[locale]/types";
export interface PParams extends Params {
adminUrlId: string;

View file

@ -1,6 +1,6 @@
"use client";
import { notFound, useParams, useSearchParams } from "next/navigation";
import React from "react";
import type React from "react";
import { LegacyPollContextProvider } from "@/components/poll/poll-context-provider";
import { VisibilityProvider } from "@/components/visibility";

View file

@ -1,5 +1,5 @@
import { prisma } from "@rallly/database";
import { Metadata } from "next";
import type { Metadata } from "next";
import { InvitePage } from "@/app/[locale]/invite/[urlId]/invite-page";
import { getTranslation } from "@/app/i18n";

View file

@ -3,9 +3,9 @@ import "../../style.css";
import languages from "@rallly/languages";
import { Toaster } from "@rallly/ui/toaster";
import { Viewport } from "next";
import type { Viewport } from "next";
import { Inter } from "next/font/google";
import React from "react";
import type React from "react";
import { TimeZoneChangeDetector } from "@/app/[locale]/timezone-change-detector";
import { Providers } from "@/app/providers";

View file

@ -2,7 +2,7 @@ import { Trans } from "react-i18next/TransWithoutContext";
import { GroupPollIcon } from "@/app/[locale]/(admin)/app-card";
import { BackButton } from "@/app/[locale]/(admin)/menu/menu-button";
import { Params } from "@/app/[locale]/types";
import type { Params } from "@/app/[locale]/types";
import { getTranslation } from "@/app/i18n";
import { CreatePoll } from "@/components/create-poll";
import { UserDropdown } from "@/components/user-dropdown";

View file

@ -4,7 +4,7 @@ import { notFound } from "next/navigation";
import { Redirect } from "@/app/components/redirect";
import { absoluteUrl } from "@/utils/absolute-url";
import { PParams } from "./types";
import type { PParams } from "./types";
export default async function Page({ params }: { params: PParams }) {
const { participantUrlId } = params;

View file

@ -1,4 +1,4 @@
import { Params } from "@/app/[locale]/types";
import type { Params } from "@/app/[locale]/types";
export interface PParams extends Params {
participantUrlId: string;

View file

@ -7,7 +7,7 @@ import {
DialogDescription,
DialogFooter,
DialogHeader,
DialogProps,
type DialogProps,
DialogTitle,
} from "@rallly/ui/dialog";
import { useRouter } from "next/navigation";

View file

@ -14,13 +14,13 @@ import { useRouter } from "next/navigation";
import { useForm } from "react-hook-form";
import {
PollDetailsData,
type PollDetailsData,
PollDetailsForm,
} from "@/components/forms/poll-details-form";
import { useUpdatePollMutation } from "@/components/poll/mutations";
import { usePoll } from "@/components/poll-context";
import { Trans } from "@/components/trans";
import { NextPageWithLayout } from "@/types";
import type { NextPageWithLayout } from "@/types";
const Page: NextPageWithLayout = () => {
const { poll } = usePoll();

View file

@ -8,7 +8,7 @@ import { useRouter } from "next/navigation";
import { useTranslation } from "next-i18next";
import { useForm } from "react-hook-form";
import { PollOptionsData } from "@/components/forms";
import type { PollOptionsData } from "@/components/forms";
import PollOptionsForm from "@/components/forms/poll-options-form";
import { useModalContext } from "@/components/modal/modal-provider";
import { useUpdatePollMutation } from "@/components/poll/mutations";

View file

@ -8,7 +8,7 @@ import { useForm } from "react-hook-form";
import {
PollSettingsForm,
PollSettingsFormData,
type PollSettingsFormData,
} from "@/components/forms/poll-settings";
import { useUpdatePollMutation } from "@/components/poll/mutations";
import { Trans } from "@/components/trans";

View file

@ -1,4 +1,4 @@
import { NextRequest, NextResponse } from "next/server";
import { type NextRequest, NextResponse } from "next/server";
import { resetUser } from "@/app/guest";

View file

@ -1,5 +1,5 @@
import { GetObjectCommand } from "@aws-sdk/client-s3";
import { NextRequest, NextResponse } from "next/server";
import { type NextRequest, NextResponse } from "next/server";
import { env } from "@/env";
import { getS3Client } from "@/utils/s3";

View file

@ -1,5 +1,5 @@
"use client";
import { Button, ButtonProps } from "@rallly/ui/button";
import { Button, type ButtonProps } from "@rallly/ui/button";
import { usePostHog } from "@/utils/posthog";

View file

@ -2,7 +2,7 @@
import { cn } from "@rallly/ui";
import Link from "next/link";
import { usePathname } from "next/navigation";
import React from "react";
import type React from "react";
export function TabMenuItem({
href,

View file

@ -4,13 +4,17 @@ import * as Sentry from "@sentry/nextjs";
import NextError from "next/error";
import { useEffect } from "react";
export default function GlobalError({ error }: { error: Error & { digest?: string } }) {
export default function GlobalError({
error,
}: {
error: Error & { digest?: string };
}) {
useEffect(() => {
Sentry.captureException(error);
}, [error]);
return (
<html>
<html lang="en">
<body>
{/* `NextError` is the default Next.js error page component. Its type
definition requires a `statusCode` prop. However, since the App Router
@ -20,4 +24,4 @@ export default function GlobalError({ error }: { error: Error & { digest?: strin
</body>
</html>
);
}
}

View file

@ -1,7 +1,7 @@
import languages from "@rallly/languages";
import languageParser from "accept-language-parser";
import { NextRequest, NextResponse } from "next/server";
import { encode, JWT } from "next-auth/jwt";
import type { NextRequest, NextResponse } from "next/server";
import { encode, type JWT } from "next-auth/jwt";
import { absoluteUrl } from "@/utils/absolute-url";
import { randomid } from "@/utils/nanoid";

View file

@ -1,4 +1,4 @@
import { createInstance, Namespace } from "i18next";
import { createInstance, type Namespace } from "i18next";
import resourcesToBackend from "i18next-resources-to-backend";
import { initReactI18next } from "react-i18next/initReactI18next";

View file

@ -1,9 +1,9 @@
"use client";
import i18next, { Namespace } from "i18next";
import i18next, { type Namespace } from "i18next";
import ICU from "i18next-icu";
import resourcesToBackend from "i18next-resources-to-backend";
import { useParams } from "next/navigation";
import React from "react";
import type React from "react";
import {
I18nextProvider,
initReactI18next,

View file

@ -1,5 +1,5 @@
import allLanguages from "@rallly/languages";
import { InitOptions } from "i18next";
import type { InitOptions } from "i18next";
export const fallbackLng = "en";
export const languages = Object.keys(allLanguages);

View file

@ -8,7 +8,7 @@ import { useState } from "react";
import { I18nProvider } from "@/app/i18n/client";
import { UserProvider } from "@/components/user-provider";
import { AppRouter } from "@/trpc/routers";
import type { AppRouter } from "@/trpc/routers";
import { ConnectedDayjsProvider } from "@/utils/dayjs";
import { trpcConfig } from "@/utils/trpc/config";

View file

@ -10,7 +10,7 @@ import {
} from "@rallly/ui/dropdown-menu";
import { Icon } from "@rallly/ui/icon";
import {
CalendarEvent,
type CalendarEvent,
google,
ics,
office365,

View file

@ -1,7 +1,7 @@
import { Button } from "@rallly/ui/button";
import { Input } from "@rallly/ui/input";
import { Trans, useTranslation } from "next-i18next";
import React from "react";
import type React from "react";
import { useForm } from "react-hook-form";
import { requiredString } from "../../utils/form-validation";

View file

@ -1,4 +1,4 @@
import React from "react";
import type React from "react";
import { Logo } from "@/components/logo";

View file

@ -47,6 +47,7 @@ const CookieConsentPopover = () => {
Privacy Policy
</Link>
<button
type="button"
onClick={() => {
Cookies.set("rallly_cookie_consent", "1", { expires: 365 });
setVisible(false);

View file

@ -9,7 +9,7 @@ import {
} from "@rallly/ui/card";
import { Form } from "@rallly/ui/form";
import { useRouter } from "next/navigation";
import React from "react";
import type React from "react";
import { useForm } from "react-hook-form";
import useFormPersist from "react-hook-form-persist";
import { useUnmount } from "react-use";
@ -21,7 +21,7 @@ import { setCookie } from "@/utils/cookies";
import { usePostHog } from "@/utils/posthog";
import { trpc } from "@/utils/trpc/client";
import { NewEventData, PollDetailsForm, PollOptionsForm } from "./forms";
import { type NewEventData, PollDetailsForm, PollOptionsForm } from "./forms";
const required = <T,>(v: T | undefined): T => {
if (!v) {

View file

@ -1,5 +1,5 @@
import { cn } from "@rallly/ui";
import * as React from "react";
import type * as React from "react";
export interface DateCardProps {
day: string;

View file

@ -1,5 +1,5 @@
import { cn } from "@rallly/ui";
import dayjs, { Dayjs } from "dayjs";
import dayjs, { type Dayjs } from "dayjs";
export const DateIconInner = (props: {
dow?: React.ReactNode;

View file

@ -65,9 +65,8 @@ function NewCommentForm({
if (user.isGuest) {
const participant = participants.find((p) => p.userId === user.id);
return participant?.name ?? "";
} else {
return user.name;
}
return user.name;
}, [user, participants]);
const pollId = poll.id;
@ -237,7 +236,10 @@ function DiscussionInner() {
{canDelete && (
<DropdownMenu>
<DropdownMenuTrigger asChild={true}>
<button className="hover:text-foreground text-gray-500">
<button
type="button"
className="hover:text-foreground text-gray-500"
>
<MoreHorizontalIcon className="size-4" />
</button>
</DropdownMenuTrigger>
@ -281,6 +283,7 @@ function DiscussionInner() {
/>
) : (
<button
type="button"
className="border-input text-muted-foreground flex w-full rounded border bg-transparent px-2 py-2 text-left text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-1"
onClick={() => setIsWriting(true)}
>

View file

@ -2,7 +2,7 @@ import { Button } from "@rallly/ui/button";
import { FrownIcon } from "lucide-react";
import Link from "next/link";
import { useTranslation } from "next-i18next";
import * as React from "react";
import type * as React from "react";
export interface ComponentProps {
icon?: React.ComponentType<{ className?: string }>;

View file

@ -13,8 +13,8 @@ import {
MegaphoneIcon,
SmileIcon,
} from "lucide-react";
import Link from "next/link";
import { Trans } from "next-i18next";
import Link from "next/link";
const FeedbackButton = () => {
return (
@ -28,17 +28,14 @@ const FeedbackButton = () => {
</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem asChild>
<Link
href={`https://feedback.rallly.co/?b=feedback`}
target={"_blank"}
>
<Link href="https://feedback.rallly.co/?b=feedback" target={"_blank"}>
<SmileIcon className="mr-2 size-4" />
<Trans i18nKey="sendFeedback" defaults="Send Feedback" />
</Link>
</DropdownMenuItem>
<DropdownMenuItem asChild>
<Link
href={`https://feedback.rallly.co/?b=feature-request`}
href="https://feedback.rallly.co/?b=feature-request"
target={"_blank"}
>
<LightbulbIcon className="mr-2 size-4" />
@ -47,7 +44,7 @@ const FeedbackButton = () => {
</DropdownMenuItem>
<DropdownMenuItem asChild>
<Link
href={`https://feedback.rallly.co/?b=bug-reports`}
href="https://feedback.rallly.co/?b=bug-reports"
target={"_blank"}
>
<BugIcon className="mr-2 size-4" />
@ -56,7 +53,7 @@ const FeedbackButton = () => {
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem asChild>
<Link href={`https://support.rallly.co`} target={"_blank"}>
<Link href="https://support.rallly.co" target={"_blank"}>
<LifeBuoyIcon className="mr-2 size-4" />
<Trans i18nKey={"getSupport"} defaults={"Get Support"} />
</Link>

View file

@ -7,7 +7,7 @@ import { useFormContext } from "react-hook-form";
import { Trans } from "@/components/trans";
import { useFormValidation } from "@/utils/form-validation";
import { NewEventData } from "./types";
import type { NewEventData } from "./types";
export interface PollDetailsData {
title: string;

View file

@ -1,7 +1,7 @@
import { Button } from "@rallly/ui/button";
import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react";
import { useTranslation } from "next-i18next";
import * as React from "react";
import type * as React from "react";
export interface DateNavigationToolbarProps {
year: number;

View file

@ -23,7 +23,7 @@ import { useTranslation } from "next-i18next";
import * as React from "react";
import { useFormContext } from "react-hook-form";
import { NewEventData } from "@/components/forms";
import type { NewEventData } from "@/components/forms";
import { Trans } from "@/components/trans";
import {
@ -34,8 +34,8 @@ import {
} from "../../../../utils/date-time-utils";
import DateCard from "../../../date-card";
import { useHeadlessDatePicker } from "../../../headless-date-picker";
import { DateTimeOption } from "..";
import { DateTimePickerProps } from "../types";
import type { DateTimeOption } from "..";
import type { DateTimePickerProps } from "../types";
import { formatDateWithoutTime, formatDateWithoutTz } from "../utils";
import TimePicker from "./time-picker";

View file

@ -23,9 +23,9 @@ import { useFormContext } from "react-hook-form";
import { TimeZoneCommand } from "@/components/time-zone-picker/time-zone-select";
import { getBrowserTimeZone } from "../../../utils/date-time-utils";
import { NewEventData } from "../types";
import type { NewEventData } from "../types";
import MonthCalendar from "./month-calendar";
import { DateTimeOption } from "./types";
import type { DateTimeOption } from "./types";
import WeekCalendar from "./week-calendar";
export type PollOptionsData = {

View file

@ -3,14 +3,14 @@ import "./rbc-overrides.css";
import dayjs from "dayjs";
import { XIcon } from "lucide-react";
import React from "react";
import { Calendar, CalendarProps } from "react-big-calendar";
import type React from "react";
import { Calendar, type CalendarProps } from "react-big-calendar";
import { createBreakpoint } from "react-use";
import { getDuration } from "../../../utils/date-time-utils";
import DateNavigationToolbar from "./date-navigation-toolbar";
import dayjsLocalizer from "./dayjs-localizer";
import { DateTimeOption, DateTimePickerProps } from "./types";
import type { DateTimeOption, DateTimePickerProps } from "./types";
import { formatDateWithoutTz } from "./utils";
const localizer = dayjsLocalizer(dayjs);
@ -47,12 +47,11 @@ const WeekCalendar: React.FunctionComponent<DateTimePickerProps> = ({
events={options.map((option) => {
if (option.type === "date") {
return { start: new Date(option.date) };
} else {
return {
start: new Date(option.start),
end: new Date(option.end),
};
}
return {
start: new Date(option.start),
end: new Date(option.end),
};
})}
culture="default"
onNavigate={onNavigate}
@ -122,7 +121,7 @@ const WeekCalendar: React.FunctionComponent<DateTimePickerProps> = ({
);
},
week: {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
header: function Header({ date }: any) {
return (
<span className="w-full rounded-md text-center text-sm tracking-tight">

View file

@ -10,7 +10,7 @@ import { useDialog } from "@rallly/ui/dialog";
import { FormField } from "@rallly/ui/form";
import { Switch } from "@rallly/ui/switch";
import { AtSignIcon, EyeIcon, MessageCircleIcon, VoteIcon } from "lucide-react";
import React from "react";
import type React from "react";
import { useFormContext } from "react-hook-form";
import { Trans } from "react-i18next";

View file

@ -1,7 +1,7 @@
import { PollSettingsFormData } from "@/components/forms/poll-settings";
import type { PollSettingsFormData } from "@/components/forms/poll-settings";
import { PollDetailsData } from "./poll-details-form";
import { PollOptionsData } from "./poll-options-form/poll-options-form";
import type { PollDetailsData } from "./poll-details-form";
import type { PollOptionsData } from "./poll-options-form/poll-options-form";
export type NewEventData = PollDetailsData &
PollOptionsData &

View file

@ -11,7 +11,7 @@ import {
} from "lucide-react";
import Link from "next/link";
import { useParams, usePathname } from "next/navigation";
import React from "react";
import type React from "react";
import { GroupPollIcon } from "@/app/[locale]/(admin)/app-card";
import Loader from "@/app/[locale]/poll/[urlId]/skeleton";

View file

@ -1,4 +1,4 @@
import Link, { LinkProps } from "next/link";
import Link, { type LinkProps } from "next/link";
import { usePathname } from "next/navigation";
import React from "react";

View file

@ -2,7 +2,7 @@ import * as React from "react";
import { useList } from "react-use";
import { useRequiredContext } from "../use-required-context";
import Modal, { ModalProps } from "./modal";
import Modal, { type ModalProps } from "./modal";
export interface ModalProviderProps {
children?: React.ReactNode;

View file

@ -1,4 +1,4 @@
import { Button, ButtonProps } from "@rallly/ui/button";
import { Button, type ButtonProps } from "@rallly/ui/button";
import {
Dialog,
DialogClose,
@ -7,7 +7,7 @@ import {
DialogHeader,
DialogTitle,
} from "@rallly/ui/dialog";
import * as React from "react";
import type * as React from "react";
export interface ModalProps {
title?: React.ReactNode;

View file

@ -1,6 +1,6 @@
import React from "react";
import Modal, { ModalProps } from "./modal";
import Modal, { type ModalProps } from "./modal";
type OpenModalFn = () => void;
type CloseModalFn = () => void;

View file

@ -1,5 +1,5 @@
import { zodResolver } from "@hookform/resolvers/zod";
import { VoteType } from "@rallly/database";
import type { VoteType } from "@rallly/database";
import { cn } from "@rallly/ui";
import { Badge } from "@rallly/ui/badge";
import { Button } from "@rallly/ui/button";

View file

@ -1,5 +1,5 @@
import { Container } from "@/components/container";
import { IconComponent } from "@/types";
import type { IconComponent } from "@/types";
export const PageDialog = (
props: React.PropsWithChildren<{ icon?: IconComponent }>,

View file

@ -10,7 +10,7 @@ interface ParticipantAvatarBarProps {
export const ParticipantAvatarBar = ({
participants,
max = Infinity,
max = Number.POSITIVE_INFINITY,
}: ParticipantAvatarBarProps) => {
const visibleCount = participants.length > max ? max - 1 : max;
const hiddenCount = participants.length - visibleCount;

View file

@ -30,7 +30,7 @@ import { Input } from "@rallly/ui/input";
import { PencilIcon, TagIcon, TrashIcon } from "lucide-react";
import { useTranslation } from "next-i18next";
import React from "react";
import { SubmitHandler, useForm } from "react-hook-form";
import { type SubmitHandler, useForm } from "react-hook-form";
import { useMount } from "react-use";
import { z } from "zod";

View file

@ -1,10 +1,10 @@
import { Participant, VoteType } from "@rallly/database";
import type { Participant, VoteType } from "@rallly/database";
import * as React from "react";
import { useVisibility } from "@/components/visibility";
import { usePermissions } from "@/contexts/permissions";
import { trpc } from "@/utils/trpc/client";
import { Vote } from "@/utils/trpc/types";
import type { Vote } from "@/utils/trpc/types";
import { useRequiredContext } from "./use-required-context";
@ -27,7 +27,7 @@ export const ParticipantsProvider: React.FunctionComponent<{
},
{
staleTime: 1000 * 10,
cacheTime: Infinity,
cacheTime: Number.POSITIVE_INFINITY,
},
);

View file

@ -5,7 +5,7 @@ import { Badge } from "@rallly/ui/badge";
import {
Dialog,
DialogContent,
DialogProps,
type DialogProps,
useDialog,
} from "@rallly/ui/dialog";
import { RadioGroup, RadioGroupItem } from "@rallly/ui/radio-group";

View file

@ -1,4 +1,4 @@
import { Participant, VoteType } from "@rallly/database";
import type { Participant, VoteType } from "@rallly/database";
import dayjs from "dayjs";
import { keyBy } from "lodash";
import { TrashIcon } from "lucide-react";
@ -6,12 +6,12 @@ import { useTranslation } from "next-i18next";
import React from "react";
import {
type ParsedDateOption,
type ParsedTimeSlotOption,
getDuration,
ParsedDateOption,
ParsedTimeSlotOption,
} from "@/utils/date-time-utils";
import { useDayjs } from "@/utils/dayjs";
import { GetPollApiResponse, Vote } from "@/utils/trpc/types";
import type { GetPollApiResponse, Vote } from "@/utils/trpc/types";
import ErrorPage from "./error-page";
import { useParticipants } from "./participants-provider";
@ -59,9 +59,9 @@ export const PollContextProvider: React.FunctionComponent<{
(optionId: string) => {
return (participants ?? []).reduce(
(acc, curr) => {
curr.votes.forEach((vote) => {
for (const vote of curr.votes) {
if (vote.optionId !== optionId) {
return;
continue;
}
if (vote.type === "yes") {
acc.yes += 1;
@ -72,7 +72,7 @@ export const PollContextProvider: React.FunctionComponent<{
} else {
acc.skip += 1;
}
});
}
return acc;
},
{ yes: 0, ifNeedBe: 0, no: 0, skip: 0 },
@ -103,14 +103,14 @@ export const PollContextProvider: React.FunctionComponent<{
);
const participantsByOptionId: Record<string, Participant[]> = {};
poll.options.forEach((option) => {
for (const option of poll.options) {
participantsByOptionId[option.id] = (participants ?? []).filter(
(participant) =>
participant.votes.some(
({ type, optionId }) => optionId === option.id && type === "yes",
),
);
});
}
return {
optionIds,
@ -208,38 +208,37 @@ function createOptionsContextValue(
} satisfies ParsedTimeSlotOption;
}),
};
} else {
return {
pollType: "date",
options: pollOptions.map((option) => {
const localTime = sourceTimeZone
? dayjs(option.startTime).tz(targetTimeZone)
: dayjs(option.startTime).utc();
return {
optionId: option.id,
type: "date",
month: localTime.format("MMM"),
day: localTime.format("D"),
dow: localTime.format("ddd"),
year: localTime.format("YYYY"),
} satisfies ParsedDateOption;
}),
};
}
return {
pollType: "date",
options: pollOptions.map((option) => {
const localTime = sourceTimeZone
? dayjs(option.startTime).tz(targetTimeZone)
: dayjs(option.startTime).utc();
return {
optionId: option.id,
type: "date",
month: localTime.format("MMM"),
day: localTime.format("D"),
dow: localTime.format("ddd"),
year: localTime.format("YYYY"),
} satisfies ParsedDateOption;
}),
};
}
export const OptionsProvider = (props: React.PropsWithChildren) => {
const { poll } = usePoll();
const { timeZone: targetTimeZone, timeFormat } = useDayjs();
// biome-ignore lint/correctness/useExhaustiveDependencies: <explanation>
const options = React.useMemo(() => {
return createOptionsContextValue(
poll.options,
targetTimeZone,
poll.timeZone,
);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [poll.options, poll.timeZone, targetTimeZone, timeFormat]);
return (

View file

@ -1,4 +1,4 @@
import { PollStatus } from "@rallly/database";
import type { PollStatus } from "@rallly/database";
import { cn } from "@rallly/ui";
import { Trans } from "@/components/trans";

View file

@ -5,7 +5,7 @@ import { Button } from "@rallly/ui/button";
import { Flex } from "@rallly/ui/flex";
import { Icon } from "@rallly/ui/icon";
import { MoreHorizontalIcon } from "lucide-react";
import * as React from "react";
import type * as React from "react";
import { OptimizedAvatarImage } from "@/components/optimized-avatar-image";
import { ParticipantName } from "@/components/participant";
@ -14,7 +14,7 @@ import { usePoll } from "@/components/poll-context";
import { Trans } from "@/components/trans";
import { useUser } from "@/components/user-provider";
import { usePermissions } from "@/contexts/permissions";
import { Vote } from "@/utils/trpc/types";
import type { Vote } from "@/utils/trpc/types";
import VoteIcon from "../vote-icon";
import ParticipantRowForm from "./participant-row-form";

View file

@ -6,7 +6,7 @@ import {
TooltipTrigger,
} from "@rallly/ui/tooltip";
import { ClockIcon } from "lucide-react";
import * as React from "react";
import type * as React from "react";
import { ConnectedScoreSummary } from "@/components/poll/score-summary";
import { useOptions } from "@/components/poll-context";

View file

@ -7,7 +7,7 @@ import {
DialogTitle,
} from "@rallly/ui/dialog";
import { useRouter } from "next/navigation";
import * as React from "react";
import type * as React from "react";
import { Trans } from "@/components/trans";
import { usePostHog } from "@/utils/posthog";

View file

@ -7,7 +7,7 @@ import {
DialogDescription,
DialogFooter,
DialogHeader,
DialogProps,
type DialogProps,
DialogTitle,
} from "@rallly/ui/dialog";
import {

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