mirror of
https://github.com/lukevella/rallly.git
synced 2025-05-03 12:16:04 +02:00
♻️ Replace eslint and prettier with biome (#1697)
This commit is contained in:
parent
1577a0c5df
commit
a34da49486
158 changed files with 450 additions and 2718 deletions
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
|
@ -45,7 +45,7 @@ jobs:
|
||||||
- uses: ./.github/actions/pnpm-install
|
- uses: ./.github/actions/pnpm-install
|
||||||
|
|
||||||
- name: Check linting rules
|
- name: Check linting rules
|
||||||
run: pnpm lint
|
run: pnpm turbo check
|
||||||
|
|
||||||
unit-tests:
|
unit-tests:
|
||||||
name: Unit tests
|
name: Unit tests
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
{
|
|
||||||
"plugins": ["prettier-plugin-tailwindcss"],
|
|
||||||
"semi": true,
|
|
||||||
"tabWidth": 2,
|
|
||||||
"useTabs": false,
|
|
||||||
"trailingComma": "all",
|
|
||||||
"singleQuote": false,
|
|
||||||
"arrowParens": "always"
|
|
||||||
}
|
|
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
|
@ -2,6 +2,9 @@
|
||||||
"editor.codeActionsOnSave": {
|
"editor.codeActionsOnSave": {
|
||||||
"source.fixAll": "explicit"
|
"source.fixAll": "explicit"
|
||||||
},
|
},
|
||||||
|
"[javascript]": {
|
||||||
|
"editor.defaultFormatter": "biomejs.biome"
|
||||||
|
},
|
||||||
"typescript.tsdk": "node_modules/typescript/lib",
|
"typescript.tsdk": "node_modules/typescript/lib",
|
||||||
"typescript.preferences.importModuleSpecifier": "shortest",
|
"typescript.preferences.importModuleSpecifier": "shortest",
|
||||||
"cSpell.words": ["Rallly", "Vella"],
|
"cSpell.words": ["Rallly", "Vella"],
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
/** @type {import("eslint").Linter.Config} */
|
|
||||||
module.exports = {
|
|
||||||
...require("@rallly/eslint-config")(__dirname),
|
|
||||||
};
|
|
|
@ -1,4 +0,0 @@
|
||||||
/** @type {import("eslint").Linter.Config} */
|
|
||||||
module.exports = {
|
|
||||||
...require("@rallly/eslint-config/next")(__dirname),
|
|
||||||
};
|
|
|
@ -7,7 +7,6 @@
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"analyze": "cross-env ANALYZE=true next build",
|
"analyze": "cross-env ANALYZE=true next build",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"lint": "eslint .",
|
|
||||||
"type-check": "tsc --pretty --noEmit",
|
"type-check": "tsc --pretty --noEmit",
|
||||||
"i18n:scan": "i18next-scanner --config i18next-scanner.config.js"
|
"i18n:scan": "i18next-scanner --config i18next-scanner.config.js"
|
||||||
},
|
},
|
||||||
|
@ -41,7 +40,6 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@next/bundle-analyzer": "^14.2.25",
|
"@next/bundle-analyzer": "^14.2.25",
|
||||||
"@rallly/eslint-config": "workspace:*",
|
|
||||||
"@rallly/tsconfig": "workspace:*",
|
"@rallly/tsconfig": "workspace:*",
|
||||||
"@types/color-hash": "^1.0.2",
|
"@types/color-hash": "^1.0.2",
|
||||||
"@types/lodash": "^4.14.178",
|
"@types/lodash": "^4.14.178",
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { absoluteUrl } from "@rallly/utils/absolute-url";
|
import { absoluteUrl } from "@rallly/utils/absolute-url";
|
||||||
import { ArrowLeftIcon } from "lucide-react";
|
import { ArrowLeftIcon } from "lucide-react";
|
||||||
|
import { MDXRemote } from "next-mdx-remote/rsc";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { MDXRemote } from "next-mdx-remote/rsc";
|
|
||||||
|
|
||||||
import PostHeader from "@/components/blog/post-header";
|
import PostHeader from "@/components/blog/post-header";
|
||||||
import { getAllPosts, getPostBySlug } from "@/lib/api";
|
import { getAllPosts, getPostBySlug } from "@/lib/api";
|
||||||
|
|
|
@ -13,7 +13,7 @@ import { LanguagesIcon } from "lucide-react";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { usePathname, useRouter } from "next/navigation";
|
import { usePathname, useRouter } from "next/navigation";
|
||||||
import * as React from "react";
|
import type * as React from "react";
|
||||||
|
|
||||||
import DiscordIcon from "@/assets/discord.svg";
|
import DiscordIcon from "@/assets/discord.svg";
|
||||||
import GithubIcon from "@/assets/github.svg";
|
import GithubIcon from "@/assets/github.svg";
|
||||||
|
@ -301,6 +301,7 @@ export const Footer: React.FunctionComponent = () => {
|
||||||
target="_blank"
|
target="_blank"
|
||||||
href="https://vercel.com?utm_source=rallly&utm_campaign=oss"
|
href="https://vercel.com?utm_source=rallly&utm_campaign=oss"
|
||||||
className="inline-block text-white"
|
className="inline-block text-white"
|
||||||
|
rel="noreferrer"
|
||||||
>
|
>
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/partners/vercel-logotype-dark.svg"
|
src="/static/images/partners/vercel-logotype-dark.svg"
|
||||||
|
@ -315,6 +316,7 @@ export const Footer: React.FunctionComponent = () => {
|
||||||
target="_blank"
|
target="_blank"
|
||||||
className="inline-block"
|
className="inline-block"
|
||||||
href="/partners/digitalocean"
|
href="/partners/digitalocean"
|
||||||
|
rel="noreferrer"
|
||||||
>
|
>
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/partners/digitalocean-logo.svg"
|
src="/static/images/partners/digitalocean-logo.svg"
|
||||||
|
@ -329,6 +331,7 @@ export const Footer: React.FunctionComponent = () => {
|
||||||
target="_blank"
|
target="_blank"
|
||||||
className="inline-block"
|
className="inline-block"
|
||||||
href="https://sentry.io"
|
href="https://sentry.io"
|
||||||
|
rel="noreferrer"
|
||||||
>
|
>
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/partners/sentry.svg"
|
src="/static/images/partners/sentry.svg"
|
||||||
|
|
|
@ -14,7 +14,7 @@ import {
|
||||||
import { Icon } from "@rallly/ui/icon";
|
import { Icon } from "@rallly/ui/icon";
|
||||||
import { Analytics } from "@vercel/analytics/react";
|
import { Analytics } from "@vercel/analytics/react";
|
||||||
import { MenuIcon } from "lucide-react";
|
import { MenuIcon } from "lucide-react";
|
||||||
import { domAnimation, LazyMotion } from "motion/react";
|
import { LazyMotion, domAnimation } from "motion/react";
|
||||||
import type { Viewport } from "next";
|
import type { Viewport } from "next";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
/* eslint-disable @next/next/no-img-element */
|
|
||||||
import { ImageResponse } from "next/og";
|
import { ImageResponse } from "next/og";
|
||||||
import type { NextRequest } from "next/server";
|
import type { NextRequest } from "next/server";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
@ -24,7 +23,6 @@ export async function GET(req: NextRequest) {
|
||||||
const excerpt = searchParams.get("excerpt");
|
const excerpt = searchParams.get("excerpt");
|
||||||
|
|
||||||
return new ImageResponse(
|
return new ImageResponse(
|
||||||
(
|
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
height: "100%",
|
height: "100%",
|
||||||
|
@ -56,8 +54,7 @@ export async function GET(req: NextRequest) {
|
||||||
<p tw="text-4xl leading-relaxed text-gray-500">{excerpt}</p>
|
<p tw="text-4xl leading-relaxed text-gray-500">{excerpt}</p>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>,
|
||||||
),
|
|
||||||
{
|
{
|
||||||
width: 1200,
|
width: 1200,
|
||||||
height: 630,
|
height: 630,
|
||||||
|
|
|
@ -7,7 +7,8 @@ type Props = {
|
||||||
const PostBody = ({ content }: Props) => {
|
const PostBody = ({ content }: Props) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={markdownStyles["markdown"]}
|
className={markdownStyles.markdown}
|
||||||
|
// biome-ignore lint/security/noDangerouslySetInnerHtml: Fix this later
|
||||||
dangerouslySetInnerHTML={{ __html: content }}
|
dangerouslySetInnerHTML={{ __html: content }}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
import { Button } from "@rallly/ui/button";
|
import { Button } from "@rallly/ui/button";
|
||||||
import { FileSearchIcon } from "lucide-react";
|
import { FileSearchIcon } from "lucide-react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import * as React from "react";
|
import type * as React from "react";
|
||||||
|
|
||||||
import { useTranslation } from "@/i18n/client/use-translation";
|
import { useTranslation } from "@/i18n/client/use-translation";
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { ArrowUpRight } from "lucide-react";
|
||||||
import * as m from "motion/react-m";
|
import * as m from "motion/react-m";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import React from "react";
|
import type React from "react";
|
||||||
|
|
||||||
import { Trans } from "@/i18n/client/trans";
|
import { Trans } from "@/i18n/client/trans";
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import ICU from "i18next-icu";
|
import ICU from "i18next-icu";
|
||||||
import resourcesToBackend from "i18next-resources-to-backend";
|
import resourcesToBackend from "i18next-resources-to-backend";
|
||||||
import React from "react";
|
import type React from "react";
|
||||||
import { I18nextProvider, initReactI18next } from "react-i18next";
|
import { I18nextProvider, initReactI18next } from "react-i18next";
|
||||||
import { useAsync } from "react-use";
|
import { useAsync } from "react-use";
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import fs from "fs";
|
import fs from "node:fs";
|
||||||
|
import { join } from "node:path";
|
||||||
import matter from "gray-matter";
|
import matter from "gray-matter";
|
||||||
import { join } from "path";
|
|
||||||
|
|
||||||
const postsDirectory = join(process.cwd(), "src", "posts");
|
const postsDirectory = join(process.cwd(), "src", "posts");
|
||||||
|
|
||||||
|
@ -21,6 +21,7 @@ export function getPostBySlug(slug: string, fields: string[] = []) {
|
||||||
const items: Items = {};
|
const items: Items = {};
|
||||||
|
|
||||||
// Ensure only the minimal needed data is exposed
|
// Ensure only the minimal needed data is exposed
|
||||||
|
// biome-ignore lint/complexity/noForEach: Fix this later
|
||||||
fields.forEach((field) => {
|
fields.forEach((field) => {
|
||||||
if (field === "slug") {
|
if (field === "slug") {
|
||||||
items[field] = realSlug;
|
items[field] = realSlug;
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
/** @type {import("eslint").Linter.Config} */
|
|
||||||
module.exports = {
|
|
||||||
...require("@rallly/eslint-config/next")(__dirname),
|
|
||||||
};
|
|
3
apps/web/declarations/next-auth.d.ts
vendored
3
apps/web/declarations/next-auth.d.ts
vendored
|
@ -1,10 +1,9 @@
|
||||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
||||||
import type { TimeFormat } from "@rallly/database";
|
import type { TimeFormat } from "@rallly/database";
|
||||||
import type { NextRequest } from "next/server";
|
|
||||||
import type { DefaultSession, DefaultUser } from "next-auth";
|
import type { DefaultSession, DefaultUser } from "next-auth";
|
||||||
import NextAuth from "next-auth";
|
import NextAuth from "next-auth";
|
||||||
import type { DefaultJWT } from "next-auth/jwt";
|
import type { DefaultJWT } from "next-auth/jwt";
|
||||||
import { JWT } from "next-auth/jwt";
|
import { JWT } from "next-auth/jwt";
|
||||||
|
import type { NextRequest } from "next/server";
|
||||||
|
|
||||||
declare module "next-auth" {
|
declare module "next-auth" {
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
const ICU = require("i18next-icu/i18nextICU.js");
|
const ICU = require("i18next-icu/i18nextICU.js");
|
||||||
const path = require("path");
|
const path = require("node:path");
|
||||||
const i18n = require("./i18n.config.js");
|
const i18n = require("./i18n.config.js");
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|
|
@ -28,9 +28,6 @@ const nextConfig = {
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
},
|
},
|
||||||
eslint: {
|
|
||||||
ignoreDuringBuilds: true,
|
|
||||||
},
|
|
||||||
typescript: {
|
typescript: {
|
||||||
ignoreBuildErrors: true,
|
ignoreBuildErrors: true,
|
||||||
},
|
},
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
"build:test": "NODE_ENV=test next build",
|
"build:test": "NODE_ENV=test next build",
|
||||||
"analyze": "cross-env ANALYZE=true next build",
|
"analyze": "cross-env ANALYZE=true next build",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"lint": "eslint .",
|
|
||||||
"type-check": "tsc --pretty --noEmit",
|
"type-check": "tsc --pretty --noEmit",
|
||||||
"i18n:scan": "i18next-scanner --config i18next-scanner.config.js",
|
"i18n:scan": "i18next-scanner --config i18next-scanner.config.js",
|
||||||
"test:integration": "NODE_ENV=test playwright test",
|
"test:integration": "NODE_ENV=test playwright test",
|
||||||
|
@ -96,7 +95,6 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.26.10",
|
"@babel/core": "^7.26.10",
|
||||||
"@playwright/test": "^1.49.1",
|
"@playwright/test": "^1.49.1",
|
||||||
"@rallly/eslint-config": "workspace:*",
|
|
||||||
"@rallly/tsconfig": "workspace:*",
|
"@rallly/tsconfig": "workspace:*",
|
||||||
"@types/color-hash": "^1.0.2",
|
"@types/color-hash": "^1.0.2",
|
||||||
"@types/js-cookie": "^3.0.1",
|
"@types/js-cookie": "^3.0.1",
|
||||||
|
|
|
@ -2,9 +2,9 @@ import { cn } from "@rallly/ui";
|
||||||
import { DotPattern } from "@rallly/ui/dot-pattern";
|
import { DotPattern } from "@rallly/ui/dot-pattern";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
isQuickCreateEnabled,
|
|
||||||
QuickCreateButton,
|
QuickCreateButton,
|
||||||
QuickCreateWidget,
|
QuickCreateWidget,
|
||||||
|
isQuickCreateEnabled,
|
||||||
} from "@/features/quick-create";
|
} from "@/features/quick-create";
|
||||||
|
|
||||||
export default async function Layout({
|
export default async function Layout({
|
||||||
|
|
|
@ -10,8 +10,8 @@ import {
|
||||||
FormMessage,
|
FormMessage,
|
||||||
} from "@rallly/ui/form";
|
} from "@rallly/ui/form";
|
||||||
import { Input } from "@rallly/ui/input";
|
import { Input } from "@rallly/ui/input";
|
||||||
import { useRouter, useSearchParams } from "next/navigation";
|
|
||||||
import { signIn } from "next-auth/react";
|
import { signIn } from "next-auth/react";
|
||||||
|
import { useRouter, useSearchParams } from "next/navigation";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
import { Button } from "@rallly/ui/button";
|
import { Button } from "@rallly/ui/button";
|
||||||
import { Icon } from "@rallly/ui/icon";
|
import { Icon } from "@rallly/ui/icon";
|
||||||
import { UserIcon } from "lucide-react";
|
import { UserIcon } from "lucide-react";
|
||||||
import Image from "next/image";
|
|
||||||
import { signIn } from "next-auth/react";
|
import { signIn } from "next-auth/react";
|
||||||
|
import Image from "next/image";
|
||||||
|
|
||||||
import { Trans } from "@/components/trans";
|
import { Trans } from "@/components/trans";
|
||||||
import { useTranslation } from "@/i18n/client";
|
import { useTranslation } from "@/i18n/client";
|
||||||
|
|
|
@ -10,8 +10,8 @@ import {
|
||||||
FormItem,
|
FormItem,
|
||||||
FormMessage,
|
FormMessage,
|
||||||
} from "@rallly/ui/form";
|
} from "@rallly/ui/form";
|
||||||
import { useSearchParams } from "next/navigation";
|
|
||||||
import { signIn } from "next-auth/react";
|
import { signIn } from "next-auth/react";
|
||||||
|
import { useSearchParams } from "next/navigation";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { cn } from "@rallly/ui";
|
import { cn } from "@rallly/ui";
|
||||||
import { BarChart2Icon } from "lucide-react";
|
import { BarChart2Icon } from "lucide-react";
|
||||||
import React from "react";
|
import type React from "react";
|
||||||
|
|
||||||
export function AppCard({
|
export function AppCard({
|
||||||
children,
|
children,
|
||||||
|
|
|
@ -18,7 +18,7 @@ import {
|
||||||
SparklesIcon,
|
SparklesIcon,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import * as React from "react";
|
import type * as React from "react";
|
||||||
|
|
||||||
import { LogoLink } from "@/app/components/logo-link";
|
import { LogoLink } from "@/app/components/logo-link";
|
||||||
import { Trans } from "@/components/trans";
|
import { Trans } from "@/components/trans";
|
||||||
|
|
|
@ -19,7 +19,7 @@ export function EventsTabbedView({ children }: { children: React.ReactNode }) {
|
||||||
const newUrl = `?${params.toString()}`;
|
const newUrl = `?${params.toString()}`;
|
||||||
router.replace(newUrl, { scroll: false });
|
router.replace(newUrl, { scroll: false });
|
||||||
},
|
},
|
||||||
[name, router, searchParams],
|
[router, searchParams],
|
||||||
);
|
);
|
||||||
|
|
||||||
const value = searchParams.get(name) ?? "upcoming";
|
const value = searchParams.get(name) ?? "upcoming";
|
||||||
|
|
|
@ -37,8 +37,8 @@ const pageSchema = z
|
||||||
.nullish()
|
.nullish()
|
||||||
.transform((val) => {
|
.transform((val) => {
|
||||||
if (!val) return 1;
|
if (!val) return 1;
|
||||||
const parsed = parseInt(val, 10);
|
const parsed = Number.parseInt(val, 10);
|
||||||
return isNaN(parsed) || parsed < 1 ? 1 : parsed;
|
return Number.isNaN(parsed) || parsed < 1 ? 1 : parsed;
|
||||||
});
|
});
|
||||||
|
|
||||||
const querySchema = z
|
const querySchema = z
|
||||||
|
@ -56,8 +56,8 @@ const pageSizeSchema = z
|
||||||
.nullish()
|
.nullish()
|
||||||
.transform((val) => {
|
.transform((val) => {
|
||||||
if (!val) return DEFAULT_PAGE_SIZE;
|
if (!val) return DEFAULT_PAGE_SIZE;
|
||||||
const parsed = parseInt(val, 10);
|
const parsed = Number.parseInt(val, 10);
|
||||||
return isNaN(parsed) || parsed < 1
|
return Number.isNaN(parsed) || parsed < 1
|
||||||
? DEFAULT_PAGE_SIZE
|
? DEFAULT_PAGE_SIZE
|
||||||
: Math.min(parsed, 100);
|
: Math.min(parsed, 100);
|
||||||
});
|
});
|
||||||
|
|
|
@ -19,7 +19,7 @@ export function PollsTabbedView({ children }: { children: React.ReactNode }) {
|
||||||
const newUrl = `?${params.toString()}`;
|
const newUrl = `?${params.toString()}`;
|
||||||
router.replace(newUrl, { scroll: false });
|
router.replace(newUrl, { scroll: false });
|
||||||
},
|
},
|
||||||
[name, router, searchParams],
|
[router, searchParams],
|
||||||
);
|
);
|
||||||
|
|
||||||
const value = searchParams.get(name) ?? "live";
|
const value = searchParams.get(name) ?? "live";
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React from "react";
|
import type React from "react";
|
||||||
|
|
||||||
import { SettingsPageIcon } from "@/app/components/page-icons";
|
import { SettingsPageIcon } from "@/app/components/page-icons";
|
||||||
import {
|
import {
|
||||||
|
|
|
@ -96,7 +96,7 @@ const DateTimePreferencesForm = () => {
|
||||||
<Select
|
<Select
|
||||||
value={field.value.toString()}
|
value={field.value.toString()}
|
||||||
onValueChange={(value) => {
|
onValueChange={(value) => {
|
||||||
field.onChange(parseInt(value));
|
field.onChange(Number.parseInt(value));
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<SelectTrigger asChild>
|
<SelectTrigger asChild>
|
||||||
|
@ -106,6 +106,7 @@ const DateTimePreferencesForm = () => {
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
{dayjs.weekdays().map((day, index) => (
|
{dayjs.weekdays().map((day, index) => (
|
||||||
|
// biome-ignore lint/suspicious/noArrayIndexKey: Fix this later
|
||||||
<SelectItem key={index} value={index.toString()}>
|
<SelectItem key={index} value={index.toString()}>
|
||||||
{day}
|
{day}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { prisma } from "@rallly/database";
|
import { prisma } from "@rallly/database";
|
||||||
import { absoluteUrl } from "@rallly/utils/absolute-url";
|
import { absoluteUrl } from "@rallly/utils/absolute-url";
|
||||||
import { dehydrate, Hydrate } from "@tanstack/react-query";
|
import { Hydrate, dehydrate } from "@tanstack/react-query";
|
||||||
import { notFound } from "next/navigation";
|
import { notFound } from "next/navigation";
|
||||||
|
|
||||||
import { InvitePage } from "@/app/[locale]/invite/[urlId]/invite-page";
|
import { InvitePage } from "@/app/[locale]/invite/[urlId]/invite-page";
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
import "../../style.css";
|
import "../../style.css";
|
||||||
|
|
||||||
import { defaultLocale, supportedLngs } from "@rallly/languages";
|
import { defaultLocale, supportedLngs } from "@rallly/languages";
|
||||||
import { posthog, PostHogProvider } from "@rallly/posthog/client";
|
import { PostHogProvider, posthog } from "@rallly/posthog/client";
|
||||||
import { Toaster } from "@rallly/ui/toaster";
|
import { Toaster } from "@rallly/ui/toaster";
|
||||||
import { TooltipProvider } from "@rallly/ui/tooltip";
|
import { TooltipProvider } from "@rallly/ui/tooltip";
|
||||||
import { domAnimation, LazyMotion } from "motion/react";
|
import { LazyMotion, domAnimation } from "motion/react";
|
||||||
import type { Viewport } from "next";
|
import type { Viewport } from "next";
|
||||||
import { Inter } from "next/font/google";
|
import { Inter } from "next/font/google";
|
||||||
import React from "react";
|
import type React from "react";
|
||||||
|
|
||||||
import { TimeZoneChangeDetector } from "@/app/[locale]/timezone-change-detector";
|
import { TimeZoneChangeDetector } from "@/app/[locale]/timezone-change-detector";
|
||||||
import { UserProvider } from "@/components/user-provider";
|
import { UserProvider } from "@/components/user-provider";
|
||||||
|
|
|
@ -15,8 +15,8 @@ import { useForm } from "react-hook-form";
|
||||||
|
|
||||||
import type { PollDetailsData } from "@/components/forms/poll-details-form";
|
import type { PollDetailsData } from "@/components/forms/poll-details-form";
|
||||||
import { PollDetailsForm } from "@/components/forms/poll-details-form";
|
import { PollDetailsForm } from "@/components/forms/poll-details-form";
|
||||||
import { useUpdatePollMutation } from "@/components/poll/mutations";
|
|
||||||
import { usePoll } from "@/components/poll-context";
|
import { usePoll } from "@/components/poll-context";
|
||||||
|
import { useUpdatePollMutation } from "@/components/poll/mutations";
|
||||||
import { Trans } from "@/components/trans";
|
import { Trans } from "@/components/trans";
|
||||||
|
|
||||||
const Page = () => {
|
const Page = () => {
|
||||||
|
|
|
@ -10,8 +10,8 @@ import { useForm } from "react-hook-form";
|
||||||
import type { PollOptionsData } from "@/components/forms";
|
import type { PollOptionsData } from "@/components/forms";
|
||||||
import PollOptionsForm from "@/components/forms/poll-options-form";
|
import PollOptionsForm from "@/components/forms/poll-options-form";
|
||||||
import { useModalContext } from "@/components/modal/modal-provider";
|
import { useModalContext } from "@/components/modal/modal-provider";
|
||||||
import { useUpdatePollMutation } from "@/components/poll/mutations";
|
|
||||||
import { usePoll } from "@/components/poll-context";
|
import { usePoll } from "@/components/poll-context";
|
||||||
|
import { useUpdatePollMutation } from "@/components/poll/mutations";
|
||||||
import { Trans } from "@/components/trans";
|
import { Trans } from "@/components/trans";
|
||||||
import { useTranslation } from "@/i18n/client";
|
import { useTranslation } from "@/i18n/client";
|
||||||
import { encodeDateOption } from "@/utils/date-time-utils";
|
import { encodeDateOption } from "@/utils/date-time-utils";
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { prisma } from "@rallly/database";
|
import { prisma } from "@rallly/database";
|
||||||
import { dehydrate, Hydrate } from "@tanstack/react-query";
|
import { Hydrate, dehydrate } from "@tanstack/react-query";
|
||||||
import { notFound } from "next/navigation";
|
import { notFound } from "next/navigation";
|
||||||
|
|
||||||
import { PollLayout } from "@/components/layouts/poll-layout";
|
import { PollLayout } from "@/components/layouts/poll-layout";
|
||||||
|
|
|
@ -5,8 +5,8 @@ import Link from "next/link";
|
||||||
import { notFound } from "next/navigation";
|
import { notFound } from "next/navigation";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
isQuickCreateEnabled,
|
|
||||||
QuickCreateWidget,
|
QuickCreateWidget,
|
||||||
|
isQuickCreateEnabled,
|
||||||
} from "@/features/quick-create";
|
} from "@/features/quick-create";
|
||||||
import { getTranslation } from "@/i18n/server";
|
import { getTranslation } from "@/i18n/server";
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,6 @@ export async function GET(req: NextRequest) {
|
||||||
});
|
});
|
||||||
|
|
||||||
return new ImageResponse(
|
return new ImageResponse(
|
||||||
(
|
|
||||||
<div tw="flex relative flex-col bg-gray-100 w-full h-full px-[80px] py-[70px] items-start justify-center">
|
<div tw="flex relative flex-col bg-gray-100 w-full h-full px-[80px] py-[70px] items-start justify-center">
|
||||||
<div tw="h-full flex flex-col w-full justify-start">
|
<div tw="h-full flex flex-col w-full justify-start">
|
||||||
<div tw="flex justify-between items-center w-full">
|
<div tw="flex justify-between items-center w-full">
|
||||||
|
@ -67,8 +66,7 @@ export async function GET(req: NextRequest) {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>,
|
||||||
),
|
|
||||||
{
|
{
|
||||||
width: 1200,
|
width: 1200,
|
||||||
height: 630,
|
height: 630,
|
||||||
|
|
|
@ -7,8 +7,8 @@ import { NextResponse } from "next/server";
|
||||||
|
|
||||||
import { auth } from "@/next-auth";
|
import { auth } from "@/next-auth";
|
||||||
|
|
||||||
export function createStripePortalSessionHandler(path: string = "") {
|
export function createStripePortalSessionHandler(path = "") {
|
||||||
return async function (request: NextRequest) {
|
return async (request: NextRequest) => {
|
||||||
const sessionId = request.nextUrl.searchParams.get("session_id");
|
const sessionId = request.nextUrl.searchParams.get("session_id");
|
||||||
const returnPath =
|
const returnPath =
|
||||||
request.nextUrl.searchParams.get("return_path") ?? "/settings/billing";
|
request.nextUrl.searchParams.get("return_path") ?? "/settings/billing";
|
||||||
|
|
|
@ -10,6 +10,7 @@ import { getEventHandler } from "./handlers";
|
||||||
|
|
||||||
export const POST = withPosthog(async (request: NextRequest) => {
|
export const POST = withPosthog(async (request: NextRequest) => {
|
||||||
const body = await request.text();
|
const body = await request.text();
|
||||||
|
// biome-ignore lint/style/noNonNullAssertion: Fix this later
|
||||||
const sig = request.headers.get("stripe-signature")!;
|
const sig = request.headers.get("stripe-signature")!;
|
||||||
const stripeSigningSecret = process.env.STRIPE_SIGNING_SECRET;
|
const stripeSigningSecret = process.env.STRIPE_SIGNING_SECRET;
|
||||||
|
|
||||||
|
@ -28,7 +29,7 @@ export const POST = withPosthog(async (request: NextRequest) => {
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
Sentry.captureException(err);
|
Sentry.captureException(err);
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{ error: `Webhook Error: Failed to construct event` },
|
{ error: "Webhook Error: Failed to construct event" },
|
||||||
{ status: 400 },
|
{ status: 400 },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,10 +20,7 @@ const COOKIE_CONFIG = {
|
||||||
expires: new Date(Date.now() + 5 * 1000), // 5 seconds
|
expires: new Date(Date.now() + 5 * 1000), // 5 seconds
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
const setEmailChangeCookie = (
|
const setEmailChangeCookie = (type: "success" | "error", value = "1") => {
|
||||||
type: "success" | "error",
|
|
||||||
value: string = "1",
|
|
||||||
) => {
|
|
||||||
cookies().set(`email-change-${type}`, value, COOKIE_CONFIG);
|
cookies().set(`email-change-${type}`, value, COOKIE_CONFIG);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ import {
|
||||||
UserIcon,
|
UserIcon,
|
||||||
UsersIcon,
|
UsersIcon,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import React from "react";
|
import type React from "react";
|
||||||
|
|
||||||
const pageIconVariants = cva("inline-flex items-center justify-center", {
|
const pageIconVariants = cva("inline-flex items-center justify-center", {
|
||||||
variants: {
|
variants: {
|
||||||
|
|
|
@ -22,7 +22,7 @@ export function SearchInput({ placeholder }: { placeholder: string }) {
|
||||||
const [inputValue, setInputValue] = React.useState(currentSearchValue);
|
const [inputValue, setInputValue] = React.useState(currentSearchValue);
|
||||||
|
|
||||||
// Create a debounced function to update the URL
|
// Create a debounced function to update the URL
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// biome-ignore lint/correctness/useExhaustiveDependencies: Fix this later
|
||||||
const debouncedUpdateUrl = React.useCallback(
|
const debouncedUpdateUrl = React.useCallback(
|
||||||
debounce((value: string) => {
|
debounce((value: string) => {
|
||||||
const params = new URLSearchParams(searchParams);
|
const params = new URLSearchParams(searchParams);
|
||||||
|
|
|
@ -14,7 +14,7 @@ export default function GlobalError({
|
||||||
}, [error]);
|
}, [error]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<html>
|
<html lang="en">
|
||||||
<body>
|
<body>
|
||||||
{/* `NextError` is the default Next.js error page component. Its type
|
{/* `NextError` is the default Next.js error page component. Its type
|
||||||
definition requires a `statusCode` prop. However, since the App Router
|
definition requires a `statusCode` prop. However, since the App Router
|
||||||
|
|
|
@ -12,7 +12,7 @@ export function PostHogPageView() {
|
||||||
if (pathname && posthog) {
|
if (pathname && posthog) {
|
||||||
let url = window.origin + pathname;
|
let url = window.origin + pathname;
|
||||||
if (searchParams?.toString()) {
|
if (searchParams?.toString()) {
|
||||||
url = url + `?${searchParams.toString()}`;
|
url = `${url}?${searchParams.toString()}`;
|
||||||
}
|
}
|
||||||
posthog.capture("$pageview", {
|
posthog.capture("$pageview", {
|
||||||
$current_url: url,
|
$current_url: url,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import type { NextResponse } from "next/server";
|
|
||||||
import type { NextAuthRequest, Session } from "next-auth";
|
import type { NextAuthRequest, Session } from "next-auth";
|
||||||
import NextAuth from "next-auth";
|
import NextAuth from "next-auth";
|
||||||
|
import type { NextResponse } from "next/server";
|
||||||
|
|
||||||
import { nextAuthConfig } from "@/next-auth.config";
|
import { nextAuthConfig } from "@/next-auth.config";
|
||||||
|
|
||||||
|
|
|
@ -79,7 +79,10 @@ export const TimesShownIn = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ClockPreferences>
|
<ClockPreferences>
|
||||||
<button className="inline-flex items-center gap-x-2.5 text-sm hover:underline">
|
<button
|
||||||
|
type="button"
|
||||||
|
className="inline-flex items-center gap-x-2.5 text-sm hover:underline"
|
||||||
|
>
|
||||||
<GlobeIcon className="size-4" />
|
<GlobeIcon className="size-4" />
|
||||||
<Trans
|
<Trans
|
||||||
i18nKey="timeShownIn"
|
i18nKey="timeShownIn"
|
||||||
|
|
|
@ -48,6 +48,7 @@ const CookieConsentPopover = () => {
|
||||||
Privacy Policy
|
Privacy Policy
|
||||||
</Link>
|
</Link>
|
||||||
<button
|
<button
|
||||||
|
type="button"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
Cookies.set("rallly_cookie_consent", "1", { expires: 365 });
|
Cookies.set("rallly_cookie_consent", "1", { expires: 365 });
|
||||||
setVisible(false);
|
setVisible(false);
|
||||||
|
|
|
@ -11,7 +11,7 @@ import {
|
||||||
import { Form } from "@rallly/ui/form";
|
import { Form } from "@rallly/ui/form";
|
||||||
import { useToast } from "@rallly/ui/hooks/use-toast";
|
import { useToast } from "@rallly/ui/hooks/use-toast";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import React from "react";
|
import type React from "react";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import useFormPersist from "react-hook-form-persist";
|
import useFormPersist from "react-hook-form-persist";
|
||||||
import { useUnmount } from "react-use";
|
import { useUnmount } from "react-use";
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { cn } from "@rallly/ui";
|
import { cn } from "@rallly/ui";
|
||||||
import * as React from "react";
|
import type * as React from "react";
|
||||||
|
|
||||||
export interface DateCardProps {
|
export interface DateCardProps {
|
||||||
day: string;
|
day: string;
|
||||||
|
|
|
@ -223,10 +223,8 @@ function DiscussionInner() {
|
||||||
</div>
|
</div>
|
||||||
{canDelete && (
|
{canDelete && (
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild={true}>
|
<DropdownMenuTrigger className="hover:text-foreground text-gray-500">
|
||||||
<button className="hover:text-foreground text-gray-500">
|
|
||||||
<MoreHorizontalIcon className="size-4" />
|
<MoreHorizontalIcon className="size-4" />
|
||||||
</button>
|
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent align="start">
|
<DropdownMenuContent align="start">
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
|
@ -268,6 +266,7 @@ function DiscussionInner() {
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<button
|
<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"
|
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)}
|
onClick={() => setIsWriting(true)}
|
||||||
>
|
>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { Button } from "@rallly/ui/button";
|
import { Button } from "@rallly/ui/button";
|
||||||
import { FrownIcon } from "lucide-react";
|
import { FrownIcon } from "lucide-react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import * as React from "react";
|
import type * as React from "react";
|
||||||
|
|
||||||
import { useTranslation } from "@/i18n/client";
|
import { useTranslation } from "@/i18n/client";
|
||||||
|
|
||||||
|
|
|
@ -5,9 +5,9 @@ import { Icon } from "@rallly/ui/icon";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import { DotIcon, MapPinIcon, PauseIcon } from "lucide-react";
|
import { DotIcon, MapPinIcon, PauseIcon } from "lucide-react";
|
||||||
|
|
||||||
|
import { PollStatusBadge } from "@/components/poll-status";
|
||||||
import TruncatedLinkify from "@/components/poll/truncated-linkify";
|
import TruncatedLinkify from "@/components/poll/truncated-linkify";
|
||||||
import VoteIcon from "@/components/poll/vote-icon";
|
import VoteIcon from "@/components/poll/vote-icon";
|
||||||
import { PollStatusBadge } from "@/components/poll-status";
|
|
||||||
import { RandomGradientBar } from "@/components/random-gradient-bar";
|
import { RandomGradientBar } from "@/components/random-gradient-bar";
|
||||||
import { Trans } from "@/components/trans";
|
import { Trans } from "@/components/trans";
|
||||||
import { usePoll } from "@/contexts/poll";
|
import { usePoll } from "@/contexts/poll";
|
||||||
|
|
|
@ -30,7 +30,7 @@ const FeedbackButton = () => {
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
<DropdownMenuItem asChild>
|
<DropdownMenuItem asChild>
|
||||||
<Link
|
<Link
|
||||||
href={`https://feedback.rallly.co/?b=feedback`}
|
href={"https://feedback.rallly.co/?b=feedback"}
|
||||||
target={"_blank"}
|
target={"_blank"}
|
||||||
>
|
>
|
||||||
<SmileIcon className="mr-2 size-4" />
|
<SmileIcon className="mr-2 size-4" />
|
||||||
|
@ -39,7 +39,7 @@ const FeedbackButton = () => {
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem asChild>
|
<DropdownMenuItem asChild>
|
||||||
<Link
|
<Link
|
||||||
href={`https://feedback.rallly.co/?b=feature-request`}
|
href={"https://feedback.rallly.co/?b=feature-request"}
|
||||||
target={"_blank"}
|
target={"_blank"}
|
||||||
>
|
>
|
||||||
<LightbulbIcon className="mr-2 size-4" />
|
<LightbulbIcon className="mr-2 size-4" />
|
||||||
|
@ -48,7 +48,7 @@ const FeedbackButton = () => {
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem asChild>
|
<DropdownMenuItem asChild>
|
||||||
<Link
|
<Link
|
||||||
href={`https://feedback.rallly.co/?b=bug-reports`}
|
href={"https://feedback.rallly.co/?b=bug-reports"}
|
||||||
target={"_blank"}
|
target={"_blank"}
|
||||||
>
|
>
|
||||||
<BugIcon className="mr-2 size-4" />
|
<BugIcon className="mr-2 size-4" />
|
||||||
|
@ -57,7 +57,7 @@ const FeedbackButton = () => {
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
<DropdownMenuItem asChild>
|
<DropdownMenuItem asChild>
|
||||||
<Link href={`https://support.rallly.co`} target={"_blank"}>
|
<Link href={"https://support.rallly.co"} target={"_blank"}>
|
||||||
<LifeBuoyIcon className="mr-2 size-4" />
|
<LifeBuoyIcon className="mr-2 size-4" />
|
||||||
<Trans i18nKey={"getSupport"} defaults={"Get Support"} />
|
<Trans i18nKey={"getSupport"} defaults={"Get Support"} />
|
||||||
</Link>
|
</Link>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { Button } from "@rallly/ui/button";
|
import { Button } from "@rallly/ui/button";
|
||||||
import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react";
|
import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react";
|
||||||
import * as React from "react";
|
import type * as React from "react";
|
||||||
|
|
||||||
import { useTranslation } from "@/i18n/client";
|
import { useTranslation } from "@/i18n/client";
|
||||||
|
|
||||||
|
|
|
@ -1,22 +1,26 @@
|
||||||
/* eslint-disable */
|
|
||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import { DateLocalizer } from "react-big-calendar";
|
import { DateLocalizer } from "react-big-calendar";
|
||||||
|
|
||||||
const weekRangeFormat = ({ start, end }, culture, local) =>
|
const weekRangeFormat = ({ start, end }, culture, local) =>
|
||||||
|
// biome-ignore lint/style/useTemplate: Fix this later
|
||||||
local.format(start, "MMMM DD", culture) +
|
local.format(start, "MMMM DD", culture) +
|
||||||
" – " +
|
" – " +
|
||||||
local.format(end, local.eq(start, end, "month") ? "DD" : "MMMM DD", culture);
|
local.format(end, local.eq(start, end, "month") ? "DD" : "MMMM DD", culture);
|
||||||
|
|
||||||
const dateRangeFormat = ({ start, end }, culture, local) =>
|
const dateRangeFormat = ({ start, end }, culture, local) =>
|
||||||
|
// biome-ignore lint/style/useTemplate: Fix this later
|
||||||
local.format(start, "L", culture) + " – " + local.format(end, "L", culture);
|
local.format(start, "L", culture) + " – " + local.format(end, "L", culture);
|
||||||
|
|
||||||
const timeRangeFormat = ({ start, end }, culture, local) =>
|
const timeRangeFormat = ({ start, end }, culture, local) =>
|
||||||
|
// biome-ignore lint/style/useTemplate: Fix this later
|
||||||
local.format(start, "LT", culture) + " – " + local.format(end, "LT", culture);
|
local.format(start, "LT", culture) + " – " + local.format(end, "LT", culture);
|
||||||
|
|
||||||
const timeRangeStartFormat = ({ start }, culture, local) =>
|
const timeRangeStartFormat = ({ start }, culture, local) =>
|
||||||
|
// biome-ignore lint/style/useTemplate: Fix this later
|
||||||
local.format(start, "LT", culture) + " – ";
|
local.format(start, "LT", culture) + " – ";
|
||||||
|
|
||||||
const timeRangeEndFormat = ({ end }, culture, local) =>
|
const timeRangeEndFormat = ({ end }, culture, local) =>
|
||||||
|
// biome-ignore lint/style/useTemplate: Fix this later
|
||||||
" – " + local.format(end, "LT", culture);
|
" – " + local.format(end, "LT", culture);
|
||||||
|
|
||||||
export const formats = {
|
export const formats = {
|
||||||
|
@ -62,6 +66,7 @@ export default function (dayjs) {
|
||||||
return [dtA, dtB, datePart];
|
return [dtA, dtB, datePart];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// biome-ignore lint/style/useDefaultParameterLast: Fix this later
|
||||||
function startOf(date = null, unit) {
|
function startOf(date = null, unit) {
|
||||||
const datePart = fixUnit(unit);
|
const datePart = fixUnit(unit);
|
||||||
if (datePart) {
|
if (datePart) {
|
||||||
|
@ -70,6 +75,7 @@ export default function (dayjs) {
|
||||||
return dayjs(date).toDate();
|
return dayjs(date).toDate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// biome-ignore lint/style/useDefaultParameterLast: Fix this later
|
||||||
function endOf(date = null, unit) {
|
function endOf(date = null, unit) {
|
||||||
const datePart = fixUnit(unit);
|
const datePart = fixUnit(unit);
|
||||||
if (datePart) {
|
if (datePart) {
|
||||||
|
|
|
@ -26,6 +26,7 @@ import type { NewEventData } from "@/components/forms";
|
||||||
import { Trans } from "@/components/trans";
|
import { Trans } from "@/components/trans";
|
||||||
import { useTranslation } from "@/i18n/client";
|
import { useTranslation } from "@/i18n/client";
|
||||||
|
|
||||||
|
import type { DateTimeOption } from "..";
|
||||||
import {
|
import {
|
||||||
expectTimeOption,
|
expectTimeOption,
|
||||||
getBrowserTimeZone,
|
getBrowserTimeZone,
|
||||||
|
@ -34,7 +35,6 @@ import {
|
||||||
} from "../../../../utils/date-time-utils";
|
} from "../../../../utils/date-time-utils";
|
||||||
import DateCard from "../../../date-card";
|
import DateCard from "../../../date-card";
|
||||||
import { useHeadlessDatePicker } from "../../../headless-date-picker";
|
import { useHeadlessDatePicker } from "../../../headless-date-picker";
|
||||||
import type { DateTimeOption } from "..";
|
|
||||||
import type { DateTimePickerProps } from "../types";
|
import type { DateTimePickerProps } from "../types";
|
||||||
import { formatDateWithoutTime, formatDateWithoutTz } from "../utils";
|
import { formatDateWithoutTime, formatDateWithoutTz } from "../utils";
|
||||||
import TimePicker from "./time-picker";
|
import TimePicker from "./time-picker";
|
||||||
|
@ -81,7 +81,7 @@ const MonthCalendar: React.FunctionComponent<DateTimePickerProps> = ({
|
||||||
|
|
||||||
const datepickerSelection = React.useMemo(() => {
|
const datepickerSelection = React.useMemo(() => {
|
||||||
return Object.keys(optionsByDay).map(
|
return Object.keys(optionsByDay).map(
|
||||||
(dateString) => new Date(dateString + "T12:00:00"),
|
(dateString) => new Date(`${dateString}T12:00:00`),
|
||||||
);
|
);
|
||||||
}, [optionsByDay]);
|
}, [optionsByDay]);
|
||||||
|
|
||||||
|
@ -127,6 +127,7 @@ const MonthCalendar: React.FunctionComponent<DateTimePickerProps> = ({
|
||||||
{datepicker.days.map((day, i) => {
|
{datepicker.days.map((day, i) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
// biome-ignore lint/suspicious/noArrayIndexKey: Fix this later
|
||||||
key={i}
|
key={i}
|
||||||
className={cn("h-11", {
|
className={cn("h-11", {
|
||||||
"border-r": (i + 1) % 7 !== 0,
|
"border-r": (i + 1) % 7 !== 0,
|
||||||
|
@ -192,7 +193,7 @@ const MonthCalendar: React.FunctionComponent<DateTimePickerProps> = ({
|
||||||
? "border-primary-300 group-hover:border-primary-400 border-dashed shadow-sm"
|
? "border-primary-300 group-hover:border-primary-400 border-dashed shadow-sm"
|
||||||
: "border-dashed border-transparent group-hover:border-gray-400 group-active:bg-gray-200",
|
: "border-dashed border-transparent group-hover:border-gray-400 group-active:bg-gray-200",
|
||||||
)}
|
)}
|
||||||
></span>
|
/>
|
||||||
<span className="z-10">{day.day}</span>
|
<span className="z-10">{day.day}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -272,7 +273,7 @@ const MonthCalendar: React.FunctionComponent<DateTimePickerProps> = ({
|
||||||
className="space-y-3 p-3 sm:flex sm:items-start sm:space-x-4 sm:space-y-0 sm:p-4"
|
className="space-y-3 p-3 sm:flex sm:items-start sm:space-x-4 sm:space-y-0 sm:p-4"
|
||||||
>
|
>
|
||||||
<DateCard
|
<DateCard
|
||||||
{...getDateProps(new Date(dateString + "T12:00:00"))}
|
{...getDateProps(new Date(`${dateString}T12:00:00`))}
|
||||||
/>
|
/>
|
||||||
<div className="grow space-y-3">
|
<div className="grow space-y-3">
|
||||||
{optionsForDay.map(({ option, index }) => {
|
{optionsForDay.map(({ option, index }) => {
|
||||||
|
@ -411,8 +412,10 @@ const MonthCalendar: React.FunctionComponent<DateTimePickerProps> = ({
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
const newOptions: DateTimeOption[] = [];
|
const newOptions: DateTimeOption[] = [];
|
||||||
|
// biome-ignore lint/complexity/noForEach: Fix this later
|
||||||
Object.keys(optionsByDay).forEach(
|
Object.keys(optionsByDay).forEach(
|
||||||
(dateString) => {
|
(dateString) => {
|
||||||
|
// biome-ignore lint/complexity/noForEach: Fix this later
|
||||||
times.forEach((time) => {
|
times.forEach((time) => {
|
||||||
const start =
|
const start =
|
||||||
dateString + time.startTime;
|
dateString + time.startTime;
|
||||||
|
@ -465,6 +468,7 @@ const MonthCalendar: React.FunctionComponent<DateTimePickerProps> = ({
|
||||||
.map((selectedDate, i) => {
|
.map((selectedDate, i) => {
|
||||||
return (
|
return (
|
||||||
<DateCard
|
<DateCard
|
||||||
|
// biome-ignore lint/suspicious/noArrayIndexKey: Fix this later
|
||||||
key={i}
|
key={i}
|
||||||
{...getDateProps(selectedDate)}
|
{...getDateProps(selectedDate)}
|
||||||
// annotation={
|
// annotation={
|
||||||
|
|
|
@ -71,8 +71,7 @@ const PollOptionsForm = ({
|
||||||
[views, watchView],
|
[views, watchView],
|
||||||
);
|
);
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
const watchOptions = watch("options", []);
|
||||||
const watchOptions = watch("options", [])!;
|
|
||||||
const watchDuration = watch("duration");
|
const watchDuration = watch("duration");
|
||||||
const watchTimeZone = watch("timeZone");
|
const watchTimeZone = watch("timeZone");
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ import "./rbc-overrides.css";
|
||||||
|
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import { XIcon } from "lucide-react";
|
import { XIcon } from "lucide-react";
|
||||||
import React from "react";
|
import type React from "react";
|
||||||
import type { CalendarProps } from "react-big-calendar";
|
import type { CalendarProps } from "react-big-calendar";
|
||||||
import { Calendar } from "react-big-calendar";
|
import { Calendar } from "react-big-calendar";
|
||||||
import { createBreakpoint } from "react-use";
|
import { createBreakpoint } from "react-use";
|
||||||
|
@ -123,7 +123,7 @@ const WeekCalendar: React.FunctionComponent<DateTimePickerProps> = ({
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
week: {
|
week: {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// biome-ignore lint/suspicious/noExplicitAny: Fix this later
|
||||||
header: function Header({ date }: any) {
|
header: function Header({ date }: any) {
|
||||||
return (
|
return (
|
||||||
<span className="w-full rounded-md text-center text-sm tracking-tight">
|
<span className="w-full rounded-md text-center text-sm tracking-tight">
|
||||||
|
|
|
@ -11,7 +11,7 @@ import { useDialog } from "@rallly/ui/dialog";
|
||||||
import { FormField } from "@rallly/ui/form";
|
import { FormField } from "@rallly/ui/form";
|
||||||
import { Switch } from "@rallly/ui/switch";
|
import { Switch } from "@rallly/ui/switch";
|
||||||
import { AtSignIcon, EyeIcon, MessageCircleIcon, VoteIcon } from "lucide-react";
|
import { AtSignIcon, EyeIcon, MessageCircleIcon, VoteIcon } from "lucide-react";
|
||||||
import React from "react";
|
import type React from "react";
|
||||||
import { useFormContext } from "react-hook-form";
|
import { useFormContext } from "react-hook-form";
|
||||||
import { Trans } from "react-i18next";
|
import { Trans } from "react-i18next";
|
||||||
|
|
||||||
|
@ -52,6 +52,7 @@ const SettingTitle = ({
|
||||||
|
|
||||||
const Setting = ({ children }: React.PropsWithChildren) => {
|
const Setting = ({ children }: React.PropsWithChildren) => {
|
||||||
return (
|
return (
|
||||||
|
// biome-ignore lint/a11y/noLabelWithoutControl: Fix this later
|
||||||
<label
|
<label
|
||||||
className={cn(
|
className={cn(
|
||||||
"cursor-pointer bg-white hover:bg-gray-50 active:bg-gray-100",
|
"cursor-pointer bg-white hover:bg-gray-50 active:bg-gray-100",
|
||||||
|
|
|
@ -7,7 +7,7 @@ export type NewEventData = PollDetailsData &
|
||||||
PollOptionsData &
|
PollOptionsData &
|
||||||
PollSettingsFormData;
|
PollSettingsFormData;
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// biome-ignore lint/suspicious/noExplicitAny: Fix this later
|
||||||
export interface PollFormProps<T extends Record<string, any>> {
|
export interface PollFormProps<T extends Record<string, any>> {
|
||||||
onSubmit?: (data: T) => void;
|
onSubmit?: (data: T) => void;
|
||||||
onChange?: (data: Partial<T>) => void;
|
onChange?: (data: Partial<T>) => void;
|
||||||
|
|
|
@ -80,9 +80,9 @@ export const InviteDialog = () => {
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<div className="min-w-0">
|
<div className="min-w-0">
|
||||||
<label className="mb-2">
|
<p className="mb-2 text-sm">
|
||||||
<Trans i18nKey="inviteLink" defaults="Invite Link" />
|
<Trans i18nKey="inviteLink" defaults="Invite Link" />
|
||||||
</label>
|
</p>
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<CopyInviteLinkButton />
|
<CopyInviteLinkButton />
|
||||||
<div className="shrink-0">
|
<div className="shrink-0">
|
||||||
|
|
|
@ -10,7 +10,7 @@ import {
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useParams, usePathname } from "next/navigation";
|
import { useParams, usePathname } from "next/navigation";
|
||||||
import React from "react";
|
import type React from "react";
|
||||||
|
|
||||||
import { LogoutButton } from "@/app/components/logout-button";
|
import { LogoutButton } from "@/app/components/logout-button";
|
||||||
import { InviteDialog } from "@/components/invite-dialog";
|
import { InviteDialog } from "@/components/invite-dialog";
|
||||||
|
|
|
@ -78,6 +78,7 @@ const ModalProvider: React.FunctionComponent<ModalProviderProps> = ({
|
||||||
))}
|
))}
|
||||||
{modals.map((props, i) => (
|
{modals.map((props, i) => (
|
||||||
<Modal
|
<Modal
|
||||||
|
// biome-ignore lint/suspicious/noArrayIndexKey: Fix this later
|
||||||
key={i}
|
key={i}
|
||||||
visible={true}
|
visible={true}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
|
@ -8,7 +8,7 @@ import {
|
||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
} from "@rallly/ui/dialog";
|
} from "@rallly/ui/dialog";
|
||||||
import * as React from "react";
|
import type * as React from "react";
|
||||||
|
|
||||||
export interface ModalProps {
|
export interface ModalProps {
|
||||||
title?: React.ReactNode;
|
title?: React.ReactNode;
|
||||||
|
|
|
@ -168,6 +168,7 @@ export const NewParticipantForm = (props: NewParticipantModalProps) => {
|
||||||
) : null}
|
) : null}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
|
{/* biome-ignore lint/a11y/noLabelWithoutControl: Fix this later */}
|
||||||
<label className="mb-1 text-gray-500">{t("response")}</label>
|
<label className="mb-1 text-gray-500">{t("response")}</label>
|
||||||
<VoteSummary votes={props.votes} />
|
<VoteSummary votes={props.votes} />
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
|
@ -16,7 +16,7 @@ interface ParticipantAvatarBarProps {
|
||||||
|
|
||||||
export const ParticipantAvatarBar = ({
|
export const ParticipantAvatarBar = ({
|
||||||
participants,
|
participants,
|
||||||
max = Infinity,
|
max = Number.POSITIVE_INFINITY,
|
||||||
}: ParticipantAvatarBarProps) => {
|
}: ParticipantAvatarBarProps) => {
|
||||||
const totalParticipants = participants.length;
|
const totalParticipants = participants.length;
|
||||||
|
|
||||||
|
@ -37,6 +37,7 @@ export const ParticipantAvatarBar = ({
|
||||||
return (
|
return (
|
||||||
<ul className="flex cursor-default items-center -space-x-1 rounded-full bg-white p-0.5">
|
<ul className="flex cursor-default items-center -space-x-1 rounded-full bg-white p-0.5">
|
||||||
{visibleParticipants.map((participant, index) => (
|
{visibleParticipants.map((participant, index) => (
|
||||||
|
// biome-ignore lint/suspicious/noArrayIndexKey: Fix this later
|
||||||
<Tooltip delayDuration={100} key={index}>
|
<Tooltip delayDuration={100} key={index}>
|
||||||
<TooltipTrigger asChild>
|
<TooltipTrigger asChild>
|
||||||
<li className="z-10 inline-flex items-center justify-center rounded-full ring-2 ring-white">
|
<li className="z-10 inline-flex items-center justify-center rounded-full ring-2 ring-white">
|
||||||
|
@ -70,6 +71,7 @@ export const ParticipantAvatarBar = ({
|
||||||
<TooltipContent className="z-10">
|
<TooltipContent className="z-10">
|
||||||
<ul>
|
<ul>
|
||||||
{tooltipParticipants.map((participant, index) => (
|
{tooltipParticipants.map((participant, index) => (
|
||||||
|
// biome-ignore lint/suspicious/noArrayIndexKey: Fix this later
|
||||||
<li key={index}>{participant.name}</li>
|
<li key={index}>{participant.name}</li>
|
||||||
))}
|
))}
|
||||||
{remainingCount > 0 && (
|
{remainingCount > 0 && (
|
||||||
|
|
|
@ -59,6 +59,7 @@ export const PollContextProvider: React.FunctionComponent<{
|
||||||
(optionId: string) => {
|
(optionId: string) => {
|
||||||
return (participants ?? []).reduce(
|
return (participants ?? []).reduce(
|
||||||
(acc, curr) => {
|
(acc, curr) => {
|
||||||
|
// biome-ignore lint/complexity/noForEach: Fix this later
|
||||||
curr.votes.forEach((vote) => {
|
curr.votes.forEach((vote) => {
|
||||||
if (vote.optionId !== optionId) {
|
if (vote.optionId !== optionId) {
|
||||||
return;
|
return;
|
||||||
|
@ -103,6 +104,7 @@ export const PollContextProvider: React.FunctionComponent<{
|
||||||
);
|
);
|
||||||
|
|
||||||
const participantsByOptionId: Record<string, Participant[]> = {};
|
const participantsByOptionId: Record<string, Participant[]> = {};
|
||||||
|
// biome-ignore lint/complexity/noForEach: Fix this later
|
||||||
poll.options.forEach((option) => {
|
poll.options.forEach((option) => {
|
||||||
participantsByOptionId[option.id] = (participants ?? []).filter(
|
participantsByOptionId[option.id] = (participants ?? []).filter(
|
||||||
(participant) =>
|
(participant) =>
|
||||||
|
@ -233,13 +235,13 @@ export const OptionsProvider = (props: React.PropsWithChildren) => {
|
||||||
const { poll } = usePoll();
|
const { poll } = usePoll();
|
||||||
const { timeZone: targetTimeZone, timeFormat } = useDayjs();
|
const { timeZone: targetTimeZone, timeFormat } = useDayjs();
|
||||||
|
|
||||||
|
// biome-ignore lint/correctness/useExhaustiveDependencies: Fix this later
|
||||||
const options = React.useMemo(() => {
|
const options = React.useMemo(() => {
|
||||||
return createOptionsContextValue(
|
return createOptionsContextValue(
|
||||||
poll.options,
|
poll.options,
|
||||||
targetTimeZone,
|
targetTimeZone,
|
||||||
poll.timeZone,
|
poll.timeZone,
|
||||||
);
|
);
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [poll.options, poll.timeZone, targetTimeZone, timeFormat]);
|
}, [poll.options, poll.timeZone, targetTimeZone, timeFormat]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -311,6 +311,7 @@ const DesktopPoll: React.FunctionComponent = () => {
|
||||||
? visibleParticipants.map((participant, i) => {
|
? visibleParticipants.map((participant, i) => {
|
||||||
return (
|
return (
|
||||||
<ParticipantRow
|
<ParticipantRow
|
||||||
|
// biome-ignore lint/suspicious/noArrayIndexKey: Fix this later
|
||||||
key={i}
|
key={i}
|
||||||
participant={{
|
participant={{
|
||||||
id: participant.id,
|
id: participant.id,
|
||||||
|
|
|
@ -19,7 +19,7 @@ import { Trans } from "@/components/trans";
|
||||||
import { useTranslation } from "@/i18n/client";
|
import { useTranslation } from "@/i18n/client";
|
||||||
|
|
||||||
import { usePoll } from "../../poll-context";
|
import { usePoll } from "../../poll-context";
|
||||||
import { toggleVote, VoteSelector } from "../vote-selector";
|
import { VoteSelector, toggleVote } from "../vote-selector";
|
||||||
|
|
||||||
export interface ParticipantRowFormProps {
|
export interface ParticipantRowFormProps {
|
||||||
name?: string;
|
name?: string;
|
||||||
|
@ -113,6 +113,7 @@ const ParticipantRowForm = ({
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name={`votes.${i}`}
|
name={`votes.${i}`}
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
|
// biome-ignore lint/a11y/useKeyWithClickEvents: Fix later
|
||||||
<div
|
<div
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
field.onChange({
|
field.onChange({
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { Badge } from "@rallly/ui/badge";
|
||||||
import { Button } from "@rallly/ui/button";
|
import { Button } from "@rallly/ui/button";
|
||||||
import { Icon } from "@rallly/ui/icon";
|
import { Icon } from "@rallly/ui/icon";
|
||||||
import { MoreHorizontalIcon } from "lucide-react";
|
import { MoreHorizontalIcon } from "lucide-react";
|
||||||
import * as React from "react";
|
import type * as React from "react";
|
||||||
|
|
||||||
import { OptimizedAvatarImage } from "@/components/optimized-avatar-image";
|
import { OptimizedAvatarImage } from "@/components/optimized-avatar-image";
|
||||||
import { Participant, ParticipantName } from "@/components/participant";
|
import { Participant, ParticipantName } from "@/components/participant";
|
||||||
|
@ -68,6 +68,7 @@ export const ParticipantRowView: React.FunctionComponent<{
|
||||||
{votes.map((vote, i) => {
|
{votes.map((vote, i) => {
|
||||||
return (
|
return (
|
||||||
<td
|
<td
|
||||||
|
// biome-ignore lint/suspicious/noArrayIndexKey: Fix this later
|
||||||
key={i}
|
key={i}
|
||||||
className={cn(
|
className={cn(
|
||||||
"h-12 border-l border-t",
|
"h-12 border-l border-t",
|
||||||
|
@ -95,7 +96,7 @@ export const ParticipantRowView: React.FunctionComponent<{
|
||||||
</td>
|
</td>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
<td className="bg-diagonal-lines border-l"></td>
|
<td className="bg-diagonal-lines border-l" />
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -109,7 +110,7 @@ const ParticipantRow: React.FunctionComponent<ParticipantRowProps> = ({
|
||||||
const { ownsObject } = useUser();
|
const { ownsObject } = useUser();
|
||||||
const { getVote, optionIds } = usePoll();
|
const { getVote, optionIds } = usePoll();
|
||||||
|
|
||||||
const isYou = ownsObject(participant) ? true : false;
|
const isYou = ownsObject(participant);
|
||||||
|
|
||||||
const { canEditParticipant } = usePermissions();
|
const { canEditParticipant } = usePermissions();
|
||||||
const canEdit = canEditParticipant(participant.id);
|
const canEdit = canEditParticipant(participant.id);
|
||||||
|
|
|
@ -6,10 +6,10 @@ import {
|
||||||
TooltipTrigger,
|
TooltipTrigger,
|
||||||
} from "@rallly/ui/tooltip";
|
} from "@rallly/ui/tooltip";
|
||||||
import { ClockIcon } from "lucide-react";
|
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";
|
import { useOptions } from "@/components/poll-context";
|
||||||
|
import { ConnectedScoreSummary } from "@/components/poll/score-summary";
|
||||||
import { Trans } from "@/components/trans";
|
import { Trans } from "@/components/trans";
|
||||||
|
|
||||||
const TimeRange: React.FunctionComponent<{
|
const TimeRange: React.FunctionComponent<{
|
||||||
|
@ -50,9 +50,9 @@ const TimelineRow = ({
|
||||||
<th
|
<th
|
||||||
style={{ minWidth: 240, top }}
|
style={{ minWidth: 240, top }}
|
||||||
className="sticky left-0 z-30 bg-white pl-4 pr-4"
|
className="sticky left-0 z-30 bg-white pl-4 pr-4"
|
||||||
></th>
|
/>
|
||||||
{children}
|
{children}
|
||||||
<th className="w-full min-w-4 border-l"></th>
|
<th className="w-full min-w-4 border-l" />
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -186,7 +186,6 @@ const ManagePoll: React.FunctionComponent<{
|
||||||
</Link>
|
</Link>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
<>
|
|
||||||
{poll.status === "finalized" ? (
|
{poll.status === "finalized" ? (
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
@ -224,7 +223,6 @@ const ManagePoll: React.FunctionComponent<{
|
||||||
<PauseResumeToggle />
|
<PauseResumeToggle />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</>
|
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
<DropdownMenuItem onClick={exportToCsv}>
|
<DropdownMenuItem onClick={exportToCsv}>
|
||||||
<DropdownMenuItemIconLabel icon={DownloadIcon}>
|
<DropdownMenuItemIconLabel icon={DownloadIcon}>
|
||||||
|
|
|
@ -8,7 +8,7 @@ import {
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
} from "@rallly/ui/dialog";
|
} from "@rallly/ui/dialog";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import * as React from "react";
|
import type * as React from "react";
|
||||||
|
|
||||||
import { Trans } from "@/components/trans";
|
import { Trans } from "@/components/trans";
|
||||||
import { trpc } from "@/trpc/client";
|
import { trpc } from "@/trpc/client";
|
||||||
|
|
|
@ -52,6 +52,7 @@ const useScoreByOptionId = () => {
|
||||||
|
|
||||||
return React.useMemo(() => {
|
return React.useMemo(() => {
|
||||||
const scoreByOptionId: Record<string, OptionScore> = {};
|
const scoreByOptionId: Record<string, OptionScore> = {};
|
||||||
|
// biome-ignore lint/complexity/noForEach: Fix this later
|
||||||
options.forEach((option) => {
|
options.forEach((option) => {
|
||||||
scoreByOptionId[option.id] = {
|
scoreByOptionId[option.id] = {
|
||||||
yes: [],
|
yes: [],
|
||||||
|
@ -60,7 +61,9 @@ const useScoreByOptionId = () => {
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// biome-ignore lint/complexity/noForEach: Fix this later
|
||||||
responses?.forEach((response) => {
|
responses?.forEach((response) => {
|
||||||
|
// biome-ignore lint/complexity/noForEach: Fix this later
|
||||||
response.votes.forEach((vote) => {
|
response.votes.forEach((vote) => {
|
||||||
scoreByOptionId[vote.optionId]?.[vote.type].push(response.id);
|
scoreByOptionId[vote.optionId]?.[vote.type].push(response.id);
|
||||||
});
|
});
|
||||||
|
|
|
@ -12,16 +12,16 @@ import {
|
||||||
import { MoreHorizontalIcon, PlusIcon, UsersIcon } from "lucide-react";
|
import { MoreHorizontalIcon, PlusIcon, UsersIcon } from "lucide-react";
|
||||||
import { AnimatePresence } from "motion/react";
|
import { AnimatePresence } from "motion/react";
|
||||||
import * as m from "motion/react-m";
|
import * as m from "motion/react-m";
|
||||||
import * as React from "react";
|
import type * as React from "react";
|
||||||
import smoothscroll from "smoothscroll-polyfill";
|
import smoothscroll from "smoothscroll-polyfill";
|
||||||
|
|
||||||
import { TimesShownIn } from "@/components/clock";
|
import { TimesShownIn } from "@/components/clock";
|
||||||
import { OptimizedAvatarImage } from "@/components/optimized-avatar-image";
|
import { OptimizedAvatarImage } from "@/components/optimized-avatar-image";
|
||||||
import { Participant, ParticipantName } from "@/components/participant";
|
import { Participant, ParticipantName } from "@/components/participant";
|
||||||
import { ParticipantDropdown } from "@/components/participant-dropdown";
|
import { ParticipantDropdown } from "@/components/participant-dropdown";
|
||||||
|
import { useOptions, usePoll } from "@/components/poll-context";
|
||||||
import { useVotingForm } from "@/components/poll/voting-form";
|
import { useVotingForm } from "@/components/poll/voting-form";
|
||||||
import { YouAvatar } from "@/components/poll/you-avatar";
|
import { YouAvatar } from "@/components/poll/you-avatar";
|
||||||
import { useOptions, usePoll } from "@/components/poll-context";
|
|
||||||
import { Trans } from "@/components/trans";
|
import { Trans } from "@/components/trans";
|
||||||
import { usePermissions } from "@/contexts/permissions";
|
import { usePermissions } from "@/contexts/permissions";
|
||||||
import { useTranslation } from "@/i18n/client";
|
import { useTranslation } from "@/i18n/client";
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import * as React from "react";
|
import type * as React from "react";
|
||||||
|
|
||||||
import type { PollOptionProps } from "./poll-option";
|
import type { PollOptionProps } from "./poll-option";
|
||||||
import PollOption from "./poll-option";
|
import PollOption from "./poll-option";
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { cn } from "@rallly/ui";
|
import { cn } from "@rallly/ui";
|
||||||
import { groupBy } from "lodash";
|
import { groupBy } from "lodash";
|
||||||
import * as React from "react";
|
import type * as React from "react";
|
||||||
|
|
||||||
import type { ParsedDateTimeOpton } from "@/utils/date-time-utils";
|
import type { ParsedDateTimeOpton } from "@/utils/date-time-utils";
|
||||||
|
|
||||||
|
|
|
@ -49,6 +49,7 @@ const PollOptionVoteSummary: React.FunctionComponent<{ optionId: string }> = ({
|
||||||
<div className="grid grid-cols-2 gap-2">
|
<div className="grid grid-cols-2 gap-2">
|
||||||
<div className="col-span-1 space-y-2.5">
|
<div className="col-span-1 space-y-2.5">
|
||||||
{participantsWhoVotedYes.map(({ name }, i) => (
|
{participantsWhoVotedYes.map(({ name }, i) => (
|
||||||
|
// biome-ignore lint/suspicious/noArrayIndexKey: Fix this later
|
||||||
<div key={i} className="flex">
|
<div key={i} className="flex">
|
||||||
<div className="relative mr-2.5 flex size-5 items-center justify-center">
|
<div className="relative mr-2.5 flex size-5 items-center justify-center">
|
||||||
<OptimizedAvatarImage size="xs" name={name} />
|
<OptimizedAvatarImage size="xs" name={name} />
|
||||||
|
@ -62,6 +63,7 @@ const PollOptionVoteSummary: React.FunctionComponent<{ optionId: string }> = ({
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
{participantsWhoVotedIfNeedBe.map(({ name }, i) => (
|
{participantsWhoVotedIfNeedBe.map(({ name }, i) => (
|
||||||
|
// biome-ignore lint/suspicious/noArrayIndexKey: Fix this later
|
||||||
<div key={i} className="flex">
|
<div key={i} className="flex">
|
||||||
<div className="relative mr-2.5 flex size-5 items-center justify-center">
|
<div className="relative mr-2.5 flex size-5 items-center justify-center">
|
||||||
<OptimizedAvatarImage size="xs" name={name} />
|
<OptimizedAvatarImage size="xs" name={name} />
|
||||||
|
@ -77,6 +79,7 @@ const PollOptionVoteSummary: React.FunctionComponent<{ optionId: string }> = ({
|
||||||
</div>
|
</div>
|
||||||
<div className="col-span-1 space-y-2.5">
|
<div className="col-span-1 space-y-2.5">
|
||||||
{participantsWhoVotedNo.map(({ name }, i) => (
|
{participantsWhoVotedNo.map(({ name }, i) => (
|
||||||
|
// biome-ignore lint/suspicious/noArrayIndexKey: Fix this later
|
||||||
<div key={i} className="flex">
|
<div key={i} className="flex">
|
||||||
<div className="relative mr-2.5 flex size-5 items-center justify-center">
|
<div className="relative mr-2.5 flex size-5 items-center justify-center">
|
||||||
<OptimizedAvatarImage size="xs" name={name} />
|
<OptimizedAvatarImage size="xs" name={name} />
|
||||||
|
@ -111,6 +114,7 @@ const PollOption: React.FunctionComponent<PollOptionProps> = ({
|
||||||
const [active, setActive] = React.useState(false);
|
const [active, setActive] = React.useState(false);
|
||||||
const [isExpanded, toggle] = useToggle(false);
|
const [isExpanded, toggle] = useToggle(false);
|
||||||
return (
|
return (
|
||||||
|
// biome-ignore lint/a11y/useKeyWithClickEvents: Fix this later
|
||||||
<div
|
<div
|
||||||
className={cn("space-y-4 bg-white p-4", {
|
className={cn("space-y-4 bg-white p-4", {
|
||||||
"bg-gray-500/5": editable && active,
|
"bg-gray-500/5": editable && active,
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import type { VoteType } from "@rallly/database";
|
import type { VoteType } from "@rallly/database";
|
||||||
import * as React from "react";
|
import type * as React from "react";
|
||||||
import { Controller } from "react-hook-form";
|
import { Controller } from "react-hook-form";
|
||||||
|
|
||||||
import { useVotingForm } from "@/components/poll/voting-form";
|
|
||||||
import { usePoll } from "@/components/poll-context";
|
import { usePoll } from "@/components/poll-context";
|
||||||
|
import { useVotingForm } from "@/components/poll/voting-form";
|
||||||
import type { ParsedDateTimeOpton } from "@/utils/date-time-utils";
|
import type { ParsedDateTimeOpton } from "@/utils/date-time-utils";
|
||||||
|
|
||||||
import DateOption from "./date-option";
|
import DateOption from "./date-option";
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { ClockIcon } from "lucide-react";
|
import { ClockIcon } from "lucide-react";
|
||||||
import * as React from "react";
|
import type * as React from "react";
|
||||||
|
|
||||||
import type { PollOptionProps } from "./poll-option";
|
import type { PollOptionProps } from "./poll-option";
|
||||||
import PollOption from "./poll-option";
|
import PollOption from "./poll-option";
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { Icon } from "@rallly/ui/icon";
|
||||||
import { Tooltip, TooltipContent, TooltipTrigger } from "@rallly/ui/tooltip";
|
import { Tooltip, TooltipContent, TooltipTrigger } from "@rallly/ui/tooltip";
|
||||||
import { BellOffIcon, BellRingIcon } from "lucide-react";
|
import { BellOffIcon, BellRingIcon } from "lucide-react";
|
||||||
import { signIn } from "next-auth/react";
|
import { signIn } from "next-auth/react";
|
||||||
import * as React from "react";
|
import type * as React from "react";
|
||||||
|
|
||||||
import { Skeleton } from "@/components/skeleton";
|
import { Skeleton } from "@/components/skeleton";
|
||||||
import { Trans } from "@/components/trans";
|
import { Trans } from "@/components/trans";
|
||||||
|
@ -22,7 +22,7 @@ const NotificationsToggle: React.FunctionComponent = () => {
|
||||||
pollId: poll.id,
|
pollId: poll.id,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
staleTime: Infinity,
|
staleTime: Number.POSITIVE_INFINITY,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,7 @@ export function PollViewTracker({ pollId }: { pollId: string }) {
|
||||||
|
|
||||||
// Only track a view if it's been more than 30 minutes since the last view
|
// Only track a view if it's been more than 30 minutes since the last view
|
||||||
// or if this is the first view in this session
|
// or if this is the first view in this session
|
||||||
if (!lastView || now - parseInt(lastView) > 30 * 60 * 1000) {
|
if (!lastView || now - Number.parseInt(lastView) > 30 * 60 * 1000) {
|
||||||
// Record the view using server action
|
// Record the view using server action
|
||||||
trackPollView(pollId)
|
trackPollView(pollId)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
|
|
@ -118,7 +118,7 @@ export function ScheduledEvent() {
|
||||||
}
|
}
|
||||||
guests={attendees
|
guests={attendees
|
||||||
.filter((participant) => !!participant.email)
|
.filter((participant) => !!participant.email)
|
||||||
.map((participant) => participant.email!)}
|
.map((participant) => participant.email as string)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { Tooltip, TooltipContent, TooltipTrigger } from "@rallly/ui/tooltip";
|
import { Tooltip, TooltipContent, TooltipTrigger } from "@rallly/ui/tooltip";
|
||||||
import Linkify from "linkify-react";
|
import Linkify from "linkify-react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import * as React from "react";
|
import type * as React from "react";
|
||||||
|
|
||||||
export const truncateLink = (href: string, text: string, key: number) => {
|
export const truncateLink = (href: string, text: string, key: number) => {
|
||||||
const textWithoutProtocol = text.replace(/^https?:\/\//i, "");
|
const textWithoutProtocol = text.replace(/^https?:\/\//i, "");
|
||||||
|
|
|
@ -17,7 +17,7 @@ export const RegisterLink = React.forwardRef<
|
||||||
onClick={async (e) => {
|
onClick={async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
props.onClick?.(e);
|
props.onClick?.(e);
|
||||||
router.push("/register?redirectTo=" + encodeURIComponent(pathname));
|
router.push(`/register?redirectTo=${encodeURIComponent(pathname)}`);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { cn } from "@rallly/ui";
|
import { cn } from "@rallly/ui";
|
||||||
import React from "react";
|
import type React from "react";
|
||||||
|
|
||||||
export const Skeleton = (props: {
|
export const Skeleton = (props: {
|
||||||
className?: string;
|
className?: string;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React from "react";
|
import type React from "react";
|
||||||
|
|
||||||
export interface StepProps {
|
export interface StepProps {
|
||||||
title: string;
|
title: string;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { cn } from "@rallly/ui";
|
import { cn } from "@rallly/ui";
|
||||||
import React from "react";
|
import type React from "react";
|
||||||
|
|
||||||
import { useTranslation } from "@/i18n/client";
|
import { useTranslation } from "@/i18n/client";
|
||||||
|
|
||||||
|
@ -28,6 +28,7 @@ const Steps: React.FunctionComponent<StepsProps> = ({
|
||||||
{[...Array(total)].map((_, i) => {
|
{[...Array(total)].map((_, i) => {
|
||||||
return (
|
return (
|
||||||
<span
|
<span
|
||||||
|
// biome-ignore lint/suspicious/noArrayIndexKey: Fix this later
|
||||||
key={i}
|
key={i}
|
||||||
className={cn("h-2 w-2 rounded-full transition-all", {
|
className={cn("h-2 w-2 rounded-full transition-all", {
|
||||||
"bg-primary-400": i <= current,
|
"bg-primary-400": i <= current,
|
||||||
|
|
|
@ -20,6 +20,7 @@ export function DataTableColumnHeader<TData, TValue>({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
|
type="button"
|
||||||
className="flex w-full items-center gap-x-2.5"
|
className="flex w-full items-center gap-x-2.5"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
column.toggleSorting();
|
column.toggleSorting();
|
||||||
|
|
|
@ -17,12 +17,14 @@ const TimeFormatPicker = ({
|
||||||
return (
|
return (
|
||||||
<RadioGroup value={value} onValueChange={onChange} disabled={disabled}>
|
<RadioGroup value={value} onValueChange={onChange} disabled={disabled}>
|
||||||
<div className="grid gap-y-1">
|
<div className="grid gap-y-1">
|
||||||
|
{/* biome-ignore lint/a11y/noLabelWithoutControl: Fix this later */}
|
||||||
<label className="flex items-center gap-x-2">
|
<label className="flex items-center gap-x-2">
|
||||||
<RadioGroupItem value="hours12" />
|
<RadioGroupItem value="hours12" />
|
||||||
<span>
|
<span>
|
||||||
<Trans i18nKey="12h" />
|
<Trans i18nKey="12h" />
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
|
{/* biome-ignore lint/a11y/noLabelWithoutControl: Fix this later */}
|
||||||
<label className="flex items-center gap-x-2">
|
<label className="flex items-center gap-x-2">
|
||||||
<RadioGroupItem value="hours24" />
|
<RadioGroupItem value="hours24" />
|
||||||
<span>
|
<span>
|
||||||
|
|
|
@ -50,13 +50,3 @@ export const UpgradeButton = ({
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const UpgradeLink = ({}) => {
|
|
||||||
return (
|
|
||||||
<Button variant="primary" asChild>
|
|
||||||
<Link href="/settings/billing">
|
|
||||||
<Trans i18nKey="upgrade" defaults="Upgrade" />
|
|
||||||
</Link>
|
|
||||||
</Button>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
"use client";
|
"use client";
|
||||||
import { usePostHog } from "@rallly/posthog/client";
|
import { usePostHog } from "@rallly/posthog/client";
|
||||||
import { useRouter } from "next/navigation";
|
|
||||||
import { signIn, signOut } from "next-auth/react";
|
import { signIn, signOut } from "next-auth/react";
|
||||||
|
import { useRouter } from "next/navigation";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import { useTranslation } from "@/i18n/client";
|
import { useTranslation } from "@/i18n/client";
|
||||||
|
@ -83,6 +83,7 @@ export const UserProvider = ({
|
||||||
const isGuest = !user || user.tier === "guest";
|
const isGuest = !user || user.tier === "guest";
|
||||||
const tier = isGuest ? "guest" : user.tier;
|
const tier = isGuest ? "guest" : user.tier;
|
||||||
|
|
||||||
|
// biome-ignore lint/correctness/useExhaustiveDependencies: Fix this later
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (user) {
|
if (user) {
|
||||||
posthog?.identify(user.id, {
|
posthog?.identify(user.id, {
|
||||||
|
@ -92,7 +93,6 @@ export const UserProvider = ({
|
||||||
image: user.image,
|
image: user.image,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [user?.id]);
|
}, [user?.id]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -33,7 +33,7 @@ export const VoteSummaryProgressBar = (props: {
|
||||||
<div
|
<div
|
||||||
className="h-full bg-green-500 opacity-75 hover:opacity-100"
|
className="h-full bg-green-500 opacity-75 hover:opacity-100"
|
||||||
style={{
|
style={{
|
||||||
width: (props.yes.length / props.total) * 100 + "%",
|
width: `${(props.yes.length / props.total) * 100}%`,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
|
@ -46,7 +46,7 @@ export const VoteSummaryProgressBar = (props: {
|
||||||
<div
|
<div
|
||||||
className="h-full bg-amber-400 opacity-75 hover:opacity-100"
|
className="h-full bg-amber-400 opacity-75 hover:opacity-100"
|
||||||
style={{
|
style={{
|
||||||
width: (props.ifNeedBe.length / props.total) * 100 + "%",
|
width: `${(props.ifNeedBe.length / props.total) * 100}%`,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
|
@ -59,7 +59,7 @@ export const VoteSummaryProgressBar = (props: {
|
||||||
<div
|
<div
|
||||||
className="h-full bg-gray-300 opacity-75 hover:opacity-100"
|
className="h-full bg-gray-300 opacity-75 hover:opacity-100"
|
||||||
style={{
|
style={{
|
||||||
width: (props.no.length / props.total) * 100 + "%",
|
width: `${(props.no.length / props.total) * 100}%`,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
|
|
|
@ -22,7 +22,7 @@ export async function moderateContentWithAI(text: string) {
|
||||||
|
|
||||||
return result.text.includes("FLAGGED");
|
return result.text.includes("FLAGGED");
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(`❌ AI moderation failed:`, err);
|
console.error("❌ AI moderation failed:", err);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
"use client";
|
import React from "react";
|
||||||
|
|
||||||
import { useRouter } from "next/navigation";
|
|
||||||
import { useEffect } from "react";
|
|
||||||
|
|
||||||
function cmdKey(e: KeyboardEvent) {
|
function cmdKey(e: KeyboardEvent) {
|
||||||
if (e.metaKey || e.ctrlKey) {
|
if (e.metaKey || e.ctrlKey) {
|
||||||
|
@ -11,9 +8,7 @@ function cmdKey(e: KeyboardEvent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function CommandGlobalShortcut({ trigger }: { trigger: () => void }) {
|
export function CommandGlobalShortcut({ trigger }: { trigger: () => void }) {
|
||||||
const router = useRouter();
|
React.useEffect(() => {
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const handleKeyDown = (e: KeyboardEvent) => {
|
const handleKeyDown = (e: KeyboardEvent) => {
|
||||||
switch (cmdKey(e)) {
|
switch (cmdKey(e)) {
|
||||||
case "k":
|
case "k":
|
||||||
|
@ -28,7 +23,7 @@ export function CommandGlobalShortcut({ trigger }: { trigger: () => void }) {
|
||||||
return () => {
|
return () => {
|
||||||
document.removeEventListener("keydown", handleKeyDown);
|
document.removeEventListener("keydown", handleKeyDown);
|
||||||
};
|
};
|
||||||
}, [router, trigger]);
|
}, [trigger]);
|
||||||
|
|
||||||
// This component doesn't render anything
|
// This component doesn't render anything
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -34,7 +34,7 @@ export async function getScheduledEvents({
|
||||||
const where: Prisma.ScheduledEventWhereInput = {
|
const where: Prisma.ScheduledEventWhereInput = {
|
||||||
userId,
|
userId,
|
||||||
deletedAt: null,
|
deletedAt: null,
|
||||||
...(status != "past" && { start: { gte: now } }),
|
...(status !== "past" && { start: { gte: now } }),
|
||||||
...(status === "past" && { start: { lt: now } }),
|
...(status === "past" && { start: { lt: now } }),
|
||||||
...(search && { title: { contains: search, mode: "insensitive" } }),
|
...(search && { title: { contains: search, mode: "insensitive" } }),
|
||||||
status: mapStatus[status],
|
status: mapStatus[status],
|
||||||
|
|
|
@ -34,10 +34,7 @@ export const TimezoneProvider = ({
|
||||||
return getBrowserTimeZone();
|
return getBrowserTimeZone();
|
||||||
});
|
});
|
||||||
|
|
||||||
const value = React.useMemo(
|
const value = React.useMemo(() => ({ timezone, setTimezone }), [timezone]);
|
||||||
() => ({ timezone, setTimezone }),
|
|
||||||
[timezone, setTimezone],
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TimezoneContext.Provider value={value}>
|
<TimezoneContext.Provider value={value}>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { cn } from "@rallly/ui";
|
import { cn } from "@rallly/ui";
|
||||||
import type { ConfigType } from "dayjs";
|
import type { ConfigType } from "dayjs";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import * as React from "react";
|
import type * as React from "react";
|
||||||
|
|
||||||
interface FormattedDateTimeServerProps
|
interface FormattedDateTimeServerProps
|
||||||
extends Omit<React.HTMLAttributes<HTMLTimeElement>, "dateTime"> {
|
extends Omit<React.HTMLAttributes<HTMLTimeElement>, "dateTime"> {
|
||||||
|
|
|
@ -98,6 +98,7 @@ export const groupTimezonesByOffset = (): Record<string, string[]> => {
|
||||||
const timezones = getAllTimezones();
|
const timezones = getAllTimezones();
|
||||||
const grouped: Record<string, string[]> = {};
|
const grouped: Record<string, string[]> = {};
|
||||||
|
|
||||||
|
// biome-ignore lint/complexity/noForEach: Fix this later
|
||||||
timezones.forEach((tz) => {
|
timezones.forEach((tz) => {
|
||||||
const offset = dayjs().tz(tz).format("Z");
|
const offset = dayjs().tz(tz).format("Z");
|
||||||
if (!grouped[offset]) {
|
if (!grouped[offset]) {
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue