diff --git a/apps/web/.env.test b/apps/web/.env.test index 485b227f41f..ec040f4182f 100644 --- a/apps/web/.env.test +++ b/apps/web/.env.test @@ -7,7 +7,7 @@ PORT=3002 QUICK_CREATE_ENABLED=true RATE_LIMIT_ENABLED=false SECRET_PASSWORD=abcdef1234567890abcdef1234567890 -SMTP_HOST=localhost +SMTP_HOST=0.0.0.0 SMTP_PORT=1025 SMTP_REJECT_UNAUTHORIZED=false SUPPORT_EMAIL=support@rallly.co diff --git a/apps/web/package.json b/apps/web/package.json index f26b7e12765..a997aa59e98 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -62,7 +62,7 @@ "@vercel/functions": "^3.3.6", "ai": "^6.0.87", "arctic": "^3.7.0", - "better-auth": "^1.4.7", + "better-auth": "^1.6.0", "calendar-link": "^2.6.0", "class-variance-authority": "^0.7.1", "color-hash": "^2.0.2", diff --git a/apps/web/public/locales/en/app.json b/apps/web/public/locales/en/app.json index 92085f20ea5..d360261ddca 100644 --- a/apps/web/public/locales/en/app.json +++ b/apps/web/public/locales/en/app.json @@ -13,7 +13,6 @@ "continue": "Continue", "configuredByEnvironmentVariable": "This setting has been configured by environment variable.", "copied": "Copied", - "createdBy": "by {name}", "delete": "Delete", "deleteDate": "Delete date", "deletedPoll": "Deleted poll", @@ -236,7 +235,6 @@ "paginationPage": "Page {currentPage, number} of {totalPages, number}", "paginationNext": "Next", "searchPollsPlaceholder": "Search polls by title...", - "poll": "Poll", "sendFeedbackDesc": "Share your feedback with us.", "sendFeedbackSuccess": "Thank you for your feedback!", "searchEventsPlaceholder": "Search events by title...", @@ -580,7 +578,6 @@ "openPollCount": "{count, plural, other {# open}}", "pollStatusClosed": "Closed", "pollStatusScheduled": "Scheduled", - "pollStatusClosedDescription": "Votes cannot be submitted or edited at this time", "closePoll": "Close", "pollStatusCanceled": "Canceled", "schedulePoll": "Schedule", @@ -619,5 +616,9 @@ "unmuteNotifications": "Unmute notifications", "muteNotifications": "Mute notifications", "creatingPoll": "Creating poll...", - "createPollPollCreated": "Poll created" + "createPollPollCreated": "Poll created", + "viewPoll": "View poll", + "responseOptions": "Response Options", + "viewResults": "View results", + "pollClosedDescription": "No more responses are being accepted." } diff --git a/apps/web/src/app/[locale]/(optional-space)/poll/[urlId]/admin-page.tsx b/apps/web/src/app/[locale]/(optional-space)/poll/[urlId]/admin-page.tsx index 1da1d3d9f41..17a8f6dd847 100644 --- a/apps/web/src/app/[locale]/(optional-space)/poll/[urlId]/admin-page.tsx +++ b/apps/web/src/app/[locale]/(optional-space)/poll/[urlId]/admin-page.tsx @@ -1,13 +1,12 @@ "use client"; import Discussion from "@/components/discussion"; import { EventCard } from "@/components/event-card"; +import { PollBranding } from "@/components/poll/poll-branding"; import { PollFooter } from "@/components/poll/poll-footer"; import { PollViewTracker } from "@/components/poll/poll-view-tracker"; import { ResponsiveResults } from "@/components/poll/responsive-results"; -import { ScheduledEvent } from "@/components/poll/scheduled-event"; import { VotingForm } from "@/components/poll/voting-form"; import { usePoll } from "@/contexts/poll"; - import { GuestPollAlert } from "./guest-poll-alert"; export function AdminPage() { @@ -16,11 +15,13 @@ export function AdminPage() { return (
+ {poll.space?.showBranding && poll.space.primaryColor ? ( + + ) : null} {/* Track poll views */} - diff --git a/apps/web/src/app/[locale]/(space)/(dashboard)/layout.tsx b/apps/web/src/app/[locale]/(space)/(dashboard)/layout.tsx index 9704728f0f4..77206e69bae 100644 --- a/apps/web/src/app/[locale]/(space)/(dashboard)/layout.tsx +++ b/apps/web/src/app/[locale]/(space)/(dashboard)/layout.tsx @@ -12,7 +12,7 @@ import { SidebarSeparator, } from "@rallly/ui/sidebar"; import { dehydrate, HydrationBoundary } from "@tanstack/react-query"; -import { Settings2Icon } from "lucide-react"; +import { SettingsIcon } from "lucide-react"; import Link from "next/link"; import { NavUser } from "@/components/nav-user"; import { LicenseLimitWarning } from "@/features/licensing/components/license-limit-warning"; @@ -35,6 +35,7 @@ export default async function Layout({ const helpers = await createPrivateSSRHelper(); await Promise.all([ + helpers.billing.getTier.prefetch(), helpers.user.getAuthed.prefetch(), helpers.spaces.list.prefetch(), ]); @@ -61,9 +62,9 @@ export default async function Layout({ - - - + + + diff --git a/apps/web/src/app/[locale]/invite/[urlId]/invite-page.tsx b/apps/web/src/app/[locale]/invite/[urlId]/invite-page.tsx index 8ea538fd10e..498d807ec83 100644 --- a/apps/web/src/app/[locale]/invite/[urlId]/invite-page.tsx +++ b/apps/web/src/app/[locale]/invite/[urlId]/invite-page.tsx @@ -4,10 +4,10 @@ import { ArrowUpRightIcon, CrownIcon } from "lucide-react"; import Link from "next/link"; import Discussion from "@/components/discussion"; import { EventCard } from "@/components/event-card"; +import { PollBranding } from "@/components/poll/poll-branding"; import { PollFooter } from "@/components/poll/poll-footer"; import { PollViewTracker } from "@/components/poll/poll-view-tracker"; import { ResponsiveResults } from "@/components/poll/responsive-results"; -import { ScheduledEvent } from "@/components/poll/scheduled-event"; import { VotingForm } from "@/components/poll/voting-form"; import { useUser } from "@/components/user-provider"; import { usePoll } from "@/contexts/poll"; @@ -51,12 +51,13 @@ export function InvitePage() { return (
-
+ {poll.space?.showBranding && poll.space.primaryColor ? ( + + ) : null}
- diff --git a/apps/web/src/components/empty-state.tsx b/apps/web/src/components/empty-state.tsx index c0f6d639a53..d8eae4b696f 100644 --- a/apps/web/src/components/empty-state.tsx +++ b/apps/web/src/components/empty-state.tsx @@ -22,10 +22,8 @@ export function EmptyState({ export function EmptyStateIcon({ children }: { children: React.ReactNode }) { return ( -
- - {children} - +
+ {children}
); } diff --git a/apps/web/src/components/event-card.tsx b/apps/web/src/components/event-card.tsx index be17f26ae30..7103eafc8d4 100644 --- a/apps/web/src/components/event-card.tsx +++ b/apps/web/src/components/event-card.tsx @@ -1,111 +1,108 @@ "use client"; -import { Alert, AlertDescription, AlertTitle } from "@rallly/ui/alert"; -import { Card, CardContent, CardDescription } from "@rallly/ui/card"; -import { Icon } from "@rallly/ui/icon"; -import { CircleStopIcon, DotIcon, MapPinIcon } from "lucide-react"; +import { Card, CardContent } from "@rallly/ui/card"; +import { MapPinIcon, User2Icon } from "lucide-react"; +import { + EventMetaDescription, + EventMetaItem, + EventMetaList, + EventMetaTitle, +} from "@/components/event-meta"; import TruncatedLinkify from "@/components/poll/truncated-linkify"; import VoteIcon from "@/components/poll/vote-icon"; -import { PollStatusBadge } from "@/components/poll-status"; import { RandomGradientBar } from "@/components/random-gradient-bar"; import { usePoll } from "@/contexts/poll"; -import { Trans, useTranslation } from "@/i18n/client"; -import { dayjs } from "@/lib/dayjs"; +import { SpaceIcon } from "@/features/space/components/space-icon"; +import { Trans } from "@/i18n/client"; -function IconGuide() { +function IconDescriptionList({ + children, + ...props +}: React.HTMLAttributes) { return ( -
    -
  • - - -
  • -
  • - - -
  • -
  • - - -
  • -
+
+ {children} +
+ ); +} + +function IconDescription({ + icon, + label, +}: { + icon: React.ReactNode; + label: React.ReactNode; +}) { + return ( +
+
{icon}
+
{label}
+
); } export function EventCard() { const poll = usePoll(); - const { t } = useTranslation(); return ( - <> - - - -
-
-

- {poll.title} -

- - - - , - }} - /> - - - - - - - - - + + + +
+ {poll.space?.showBranding && poll.space.image ? ( +
+ +

+ {poll.space.name} +

- {poll.status !== "open" ? ( - - ) : null} + ) : null} +
+ {poll.title}
{poll.description ? ( -

- {poll.description} -

+ {poll.description} ) : null} -
- + + {poll.user ? ( + + + {poll.user.name} + + ) : null} {poll.location ? ( -

- - - + + {poll.location} -

+ ) : null} -
- - - {poll.status === "closed" ? ( - - - - - - -

- -

-
-
- ) : null} - + +
+
+

+ +

+ + } + label={} + /> + } + label={} + /> + } + label={} + /> + +
+
+
); } diff --git a/apps/web/src/components/event-meta.tsx b/apps/web/src/components/event-meta.tsx index 84afd490a33..e03ccdd85a9 100644 --- a/apps/web/src/components/event-meta.tsx +++ b/apps/web/src/components/event-meta.tsx @@ -10,7 +10,7 @@ export function EventMetaTitle({ children: React.ReactNode; }) { return ( -

+

{children}

); @@ -27,7 +27,7 @@ export function EventMetaDescription({

{children} @@ -42,7 +42,7 @@ export function EventMetaList({ className?: string; children: React.ReactNode; }) { - return

    {children}
; + return
    {children}
; } export function EventMetaItem({ @@ -56,7 +56,7 @@ export function EventMetaItem({
  • {children} @@ -71,7 +71,9 @@ export function EventMetaHost({ className?: string; children: React.ReactNode; }) { - return
    {children}
    ; + return ( +
    {children}
    + ); } export const EventMetaHostAvatar = OptimizedAvatarImage; @@ -83,9 +85,5 @@ export function EventMetaHostName({ className?: string; children: React.ReactNode; }) { - return ( -

    - {children} -

    - ); + return

    {children}

    ; } diff --git a/apps/web/src/components/poll/desktop-poll.tsx b/apps/web/src/components/poll/desktop-poll.tsx index a489d1263a2..e6b412135b6 100644 --- a/apps/web/src/components/poll/desktop-poll.tsx +++ b/apps/web/src/components/poll/desktop-poll.tsx @@ -248,7 +248,7 @@ const DesktopPoll: React.FunctionComponent = () => { )} ref={containerRef} > - +
    @@ -313,7 +313,7 @@ const DesktopPoll: React.FunctionComponent = () => { "scrollbar-thin dark:scrollbar-thumb-gray-600 dark:scrollbar-track-gray-800 hover:scrollbar-thumb-gray-400 dark:hover:scrollbar-thumb-gray-500 scrollbar-thumb-gray-300 scrollbar-track-gray-100 relative z-10 h-full min-h-0 grow overflow-auto overscroll-x-none", )} > - +
    diff --git a/apps/web/src/components/poll/desktop-poll/participant-row.tsx b/apps/web/src/components/poll/desktop-poll/participant-row.tsx index 63d0bff59a6..caf1b0a5c94 100644 --- a/apps/web/src/components/poll/desktop-poll/participant-row.tsx +++ b/apps/web/src/components/poll/desktop-poll/participant-row.tsx @@ -72,7 +72,7 @@ export const ParticipantRowView: React.FunctionComponent<{
    {isYou ? ( - + ) : null} diff --git a/apps/web/src/components/poll/mobile-poll.tsx b/apps/web/src/components/poll/mobile-poll.tsx index 7eff70f292c..bb89d0c3e19 100644 --- a/apps/web/src/components/poll/mobile-poll.tsx +++ b/apps/web/src/components/poll/mobile-poll.tsx @@ -1,12 +1,6 @@ import { Badge } from "@rallly/ui/badge"; import { Button } from "@rallly/ui/button"; -import { - Card, - CardContent, - CardFooter, - CardHeader, - CardTitle, -} from "@rallly/ui/card"; +import { Card, CardContent, CardFooter } from "@rallly/ui/card"; import { Icon } from "@rallly/ui/icon"; import { Select, @@ -68,15 +62,6 @@ const MobilePoll: React.FunctionComponent = () => { return ( - -
    - - - - {visibleParticipants.length} -
    -
    -
    {selectedParticipantId || !isEditing ? ( diff --git a/apps/web/src/components/poll/poll-branding.tsx b/apps/web/src/components/poll/poll-branding.tsx new file mode 100644 index 00000000000..156ccab4057 --- /dev/null +++ b/apps/web/src/components/poll/poll-branding.tsx @@ -0,0 +1,20 @@ +"use client"; + +import { getPrimaryColorVars } from "@/features/branding/color"; + +export function PollBranding({ primaryColor }: { primaryColor: string }) { + const primaryColorVars = getPrimaryColorVars(primaryColor); + + return ( + + ); +} diff --git a/apps/web/src/components/poll/responsive-results.tsx b/apps/web/src/components/poll/responsive-results.tsx index 851a8fd53f8..f5c33339f24 100644 --- a/apps/web/src/components/poll/responsive-results.tsx +++ b/apps/web/src/components/poll/responsive-results.tsx @@ -1,13 +1,125 @@ +"use client"; + +import { Button } from "@rallly/ui/button"; +import { Card } from "@rallly/ui/card"; +import { CalendarIcon, LockIcon } from "lucide-react"; +import * as React from "react"; import { createBreakpoint } from "react-use"; +import { AddToCalendarButton } from "@/components/add-to-calendar-button"; +import { + EmptyState, + EmptyStateDescription, + EmptyStateFooter, + EmptyStateIcon, + EmptyStateTitle, +} from "@/components/empty-state"; import DesktopPoll from "@/components/poll/desktop-poll"; import MobilePoll from "@/components/poll/mobile-poll"; +import { usePoll } from "@/contexts/poll"; +import { Trans } from "@/i18n/client"; +import { dayjs } from "@/lib/dayjs"; +import { useDayjs } from "@/utils/dayjs"; const useBreakpoint = createBreakpoint({ list: 320, table: 640 }); +function ScheduledDateTime({ + start, + duration, + timeZone, +}: { + start: Date; + duration: number; + timeZone: string | null; +}) { + const { adjustTimeZone } = useDayjs(); + const adjusted = adjustTimeZone(start, !timeZone); + const date = adjusted.format("dddd, LL"); + + if (duration === 0) { + return ( + + {date} + + + + + ); + } + + const endTime = adjustTimeZone( + dayjs(start).add(duration, "minutes"), + !timeZone, + ); + const time = `${adjusted.format("LT")} - ${endTime.format(timeZone ? "LT z" : "LT")}`; + + return ( + + {date} + {time} + + ); +} + export function ResponsiveResults() { + const poll = usePoll(); const breakpoint = useBreakpoint(); + const [dismissed, setDismissed] = React.useState(false); const PollComponent = breakpoint === "table" ? DesktopPoll : MobilePoll; + if (!dismissed && poll.status === "scheduled" && poll.event) { + return ( + + + + + + + + + + + + + + + + + + ); + } + + if (!dismissed && poll.status === "closed") { + return ( + + + + + + + + + + + + + + + + + ); + } + return ; } diff --git a/apps/web/src/lib/auth.ts b/apps/web/src/lib/auth.ts index 34cf8939f7b..89983b43da6 100644 --- a/apps/web/src/lib/auth.ts +++ b/apps/web/src/lib/auth.ts @@ -4,11 +4,11 @@ import { absoluteUrl } from "@rallly/utils/absolute-url"; import type { BetterAuthPlugin } from "better-auth"; import { APIError, betterAuth } from "better-auth"; import { prismaAdapter } from "better-auth/adapters/prisma"; +import { createAuthMiddleware } from "better-auth/api"; import { admin, anonymous, captcha, - createAuthMiddleware, emailOTP, genericOAuth, lastLoginMethod, @@ -35,56 +35,61 @@ const baseURL = absoluteUrl("/api/better-auth"); const logger = createLogger("auth"); -const plugins: BetterAuthPlugin[] = []; - -if (env.TURNSTILE_SECRET_KEY) { - plugins.push( - captcha({ - provider: "cloudflare-turnstile", - secretKey: env.TURNSTILE_SECRET_KEY, - endpoints: ["/sign-up/email"], - }), +if (env.OIDC_ISSUER_URL) { + logger.info( + "OIDC_ISSUER_URL is no longer used. You can remove it from your environment variables.", ); } -if (env.OIDC_CLIENT_ID && env.OIDC_CLIENT_SECRET && env.OIDC_DISCOVERY_URL) { - if (env.OIDC_ISSUER_URL) { - logger.info( - "OIDC_ISSUER_URL is no longer used. You can remove it from your environment variables.", - ); - } - plugins.push( - genericOAuth({ - config: [ - { - providerId: "oidc", - discoveryUrl: env.OIDC_DISCOVERY_URL, - clientId: env.OIDC_CLIENT_ID, - clientSecret: env.OIDC_CLIENT_SECRET, - scopes: ["openid", "profile", "email"], - redirectURI: absoluteUrl("/api/auth/callback/oidc"), - pkce: true, - mapProfileToUser(profile) { - return { - name: getValueByPath(profile, env.OIDC_NAME_CLAIM_PATH) as string, - email: getValueByPath( - profile, - env.OIDC_EMAIL_CLAIM_PATH, - ) as string, - image: getValueByPath( - profile, - env.OIDC_PICTURE_CLAIM_PATH, - ) as string, - }; - }, - }, - ], - }), - ); -} +// Conditional plugins are typed as BetterAuthPlugin[] — they don't add user +// fields so losing their specific types doesn't affect session type inference. +const conditionalPlugins: BetterAuthPlugin[] = [ + ...(env.TURNSTILE_SECRET_KEY + ? [ + captcha({ + provider: "cloudflare-turnstile", + secretKey: env.TURNSTILE_SECRET_KEY, + endpoints: ["/sign-up/email"], + }), + ] + : []), + ...(env.OIDC_CLIENT_ID && env.OIDC_CLIENT_SECRET && env.OIDC_DISCOVERY_URL + ? [ + genericOAuth({ + config: [ + { + providerId: "oidc", + discoveryUrl: env.OIDC_DISCOVERY_URL, + clientId: env.OIDC_CLIENT_ID, + clientSecret: env.OIDC_CLIENT_SECRET, + scopes: ["openid", "profile", "email"], + redirectURI: absoluteUrl("/api/auth/callback/oidc"), + pkce: true, + mapProfileToUser(profile) { + return { + name: getValueByPath( + profile, + env.OIDC_NAME_CLAIM_PATH, + ) as string, + email: getValueByPath( + profile, + env.OIDC_EMAIL_CLAIM_PATH, + ) as string, + image: getValueByPath( + profile, + env.OIDC_PICTURE_CLAIM_PATH, + ) as string, + }; + }, + }, + ], + }), + ] + : []), +]; export const authLib = betterAuth({ - appName: "Rallly", + appName: env.APP_NAME, secret: env.SECRET_PASSWORD, experimental: { joins: true, @@ -92,6 +97,12 @@ export const authLib = betterAuth({ emailAndPassword: { enabled: env.EMAIL_LOGIN_ENABLED !== "false", requireEmailVerification: true, + onExistingUserSignUp: async ({ user }, request) => { + await authLib.api.sendVerificationOTP({ + body: { email: user.email, type: "email-verification" }, + request, + }); + }, sendResetPassword: async ({ user, url }) => { const locale = "locale" in user ? (user.locale as string) : await getLocale(); @@ -118,7 +129,6 @@ export const authLib = betterAuth({ transaction: false, // when set to true, there is an issue where the after() hook is called before the user is actually created in the database }), plugins: [ - ...plugins, admin(), anonymous({ emailDomainName: "rallly.co", @@ -164,6 +174,7 @@ export const authLib = betterAuth({ } }, }), + ...conditionalPlugins, ], socialProviders: { google: @@ -342,6 +353,7 @@ export const authLib = betterAuth({ maxAge: 5 * 60, // 5 minutes }, }, + trustedOrigins: [absoluteUrl()], baseURL, }); diff --git a/apps/web/src/trpc/client/types.ts b/apps/web/src/trpc/client/types.ts index 3dc04bba5c0..d57ff018b58 100644 --- a/apps/web/src/trpc/client/types.ts +++ b/apps/web/src/trpc/client/types.ts @@ -18,6 +18,13 @@ export type GetPollApiResponse = { participantUrlId: string; createdAt: Date; deleted: boolean; + event: { + id: string; + start: Date; + duration: number; + attendees: Array<{ name: string; email: string; status: string }>; + status: string; + } | null; }; export type Vote = { diff --git a/apps/web/tests/authentication.spec.ts b/apps/web/tests/authentication.spec.ts index 31c321586e4..f1ca9715bc0 100644 --- a/apps/web/tests/authentication.spec.ts +++ b/apps/web/tests/authentication.spec.ts @@ -40,7 +40,9 @@ test.describe.serial(() => { }); test.describe("Existing User", () => { - test("can't register with the same email", async ({ page }) => { + test("sends OTP to existing user when registering with existing email", async ({ + page, + }) => { await page.goto("/register"); await page.getByText("Create Your Account").waitFor(); @@ -53,9 +55,15 @@ test.describe.serial(() => { await page.getByRole("button", { name: "Continue", exact: true }).click(); - await expect( - page.getByText("A user with that email already exists"), - ).toBeVisible(); + // Should show the verification code prompt (not an error) to prevent email enumeration + await page.getByRole("heading", { name: "Finish Logging In" }).waitFor(); + + // The existing user should have received a sign-in OTP + const code = await getCode(testUserEmail); + await page.getByPlaceholder("Enter your 6-digit code").fill(code); + + // Existing user should be signed in + await expect(page.getByText("Test User")).toBeVisible(); }); test("can login with password", async ({ page }) => { diff --git a/apps/web/tests/create-delete-poll.spec.ts b/apps/web/tests/create-delete-poll.spec.ts index 73e892414b0..74320bb5f6f 100644 --- a/apps/web/tests/create-delete-poll.spec.ts +++ b/apps/web/tests/create-delete-poll.spec.ts @@ -17,7 +17,9 @@ test.describe.serial(() => { const dialog = await newPollPage.create({ name: "Monthly Meetup" }); await dialog.goToPollPage(); - await expect(page.getByTestId("poll-title")).toHaveText("Monthly Meetup"); + await expect( + page.getByRole("heading", { name: "Monthly Meetup" }), + ).toBeVisible(); }); // delete the poll we just created diff --git a/apps/web/tests/guest-to-user-migration.spec.ts b/apps/web/tests/guest-to-user-migration.spec.ts index 22e9d950888..58d05136d07 100644 --- a/apps/web/tests/guest-to-user-migration.spec.ts +++ b/apps/web/tests/guest-to-user-migration.spec.ts @@ -33,7 +33,6 @@ test.describe.serial(() => { await newPollPage.goto(); const dialog = await newPollPage.create({ name: "Monthly Meetup" }); await dialog.goToPollPage(); - await expect(page.getByTestId("poll-title")).toHaveText("Monthly Meetup"); // Step 2: Navigate to registration await page.click("text=Create an account"); @@ -42,8 +41,6 @@ test.describe.serial(() => { // Step 3: Complete registration const registerPage = new RegisterPage(page); await registerPage.register(testUser); - - await expect(page.getByTestId("poll-title")).toHaveText("Monthly Meetup"); }); test("guest user can create a poll and link it to existing user", async ({ @@ -54,7 +51,9 @@ test.describe.serial(() => { await newPollPage.goto(); const dialog = await newPollPage.create({ name: "Board Meeting" }); await dialog.goToPollPage(); - await expect(page.getByTestId("poll-title")).toHaveText("Board Meeting"); + await expect( + page.getByRole("heading", { name: "Board Meeting" }), + ).toBeVisible(); // Step 2: Navigate to registration await page.getByRole("link", { name: "Login" }).click(); @@ -67,7 +66,17 @@ test.describe.serial(() => { password: testUser.password, }); - // Step 4: Navigate back to the poll - await expect(page.getByTestId("poll-title")).toHaveText("Board Meeting"); + // Step 4: Verify the poll has been linked to the logged-in user + await expect( + page.getByRole("heading", { name: "Board Meeting" }), + ).toBeVisible(); + const url = page.url(); + const urlId = url.match(/\/poll\/([^/]+)/)?.[1]; + expect(urlId).toBeTruthy(); + const [poll, user] = await Promise.all([ + prisma.poll.findUnique({ where: { id: urlId } }), + prisma.user.findUnique({ where: { email: testUser.email } }), + ]); + expect(poll?.userId).toBe(user?.id); }); }); diff --git a/packages/database/prisma/seed.ts b/packages/database/prisma/seed.ts index dead9647e14..50e3a25d6b4 100644 --- a/packages/database/prisma/seed.ts +++ b/packages/database/prisma/seed.ts @@ -34,7 +34,7 @@ async function main() { // 4. Scheduled events + invites let inviteCount = 0; for (const evt of scheduledEvents) { - const eventId = nextId(); + const eventId = evt.id ?? nextId(); const uid = crypto.randomUUID(); await prisma.scheduledEvent.create({ @@ -97,6 +97,7 @@ async function main() { spaceId: poll.spaceId, adminUrlId, participantUrlId, + scheduledEventId: poll.scheduledEventId, hideParticipants: poll.hideParticipants, hideScores: poll.hideScores, disableComments: poll.disableComments, diff --git a/packages/database/prisma/seed/data.ts b/packages/database/prisma/seed/data.ts index f2770544926..78a6fffcff7 100644 --- a/packages/database/prisma/seed/data.ts +++ b/packages/database/prisma/seed/data.ts @@ -7,6 +7,15 @@ import type { VoteType, } from "../../generated/prisma/client"; +// Returns an ISO string N days from midnight UTC today, with optional hour/minute. +function d(offsetDays: number, hour = 0, minute = 0): string { + const base = new Date(); + base.setUTCHours(0, 0, 0, 0); + base.setUTCDate(base.getUTCDate() + offsetDays); + base.setUTCHours(hour, minute, 0, 0); + return base.toISOString(); +} + // ─── Users ─────────────────────────────────────────────────────────────────── export const users = [ @@ -81,6 +90,7 @@ export type PollDef = { deadline?: string; userId: string; spaceId: string; + scheduledEventId?: string; hideParticipants?: boolean; hideScores?: boolean; disableComments?: boolean; @@ -106,14 +116,15 @@ const personalPolls: PollDef[] = [ title: "Coffee chat with Alex", description: "Quick catch-up over coffee. Any of these times work for the café on 5th.", + location: "Café on 5th Ave", status: "open", timeZone: "America/New_York", userId: "user-1", spaceId: "space-1", options: [ - { startTime: "2026-03-10T10:00:00Z", duration: 60 }, - { startTime: "2026-03-11T14:00:00Z", duration: 60 }, - { startTime: "2026-03-12T10:00:00Z", duration: 60 }, + { startTime: d(7, 10), duration: 60 }, + { startTime: d(8, 14), duration: 60 }, + { startTime: d(9, 10), duration: 60 }, ], participants: [ { @@ -135,10 +146,10 @@ const personalPolls: PollDef[] = [ userId: "user-1", spaceId: "space-1", options: [ - { startTime: "2026-03-17T00:00:00Z", duration: 0 }, - { startTime: "2026-03-18T00:00:00Z", duration: 0 }, - { startTime: "2026-03-19T00:00:00Z", duration: 0 }, - { startTime: "2026-03-20T00:00:00Z", duration: 0 }, + { startTime: d(14), duration: 0 }, + { startTime: d(15), duration: 0 }, + { startTime: d(16), duration: 0 }, + { startTime: d(17), duration: 0 }, ], participants: [ { @@ -152,14 +163,15 @@ const personalPolls: PollDef[] = [ title: "Weekend hike", description: "Planning a group hike at Bear Mountain. Bring water and sunscreen!", + location: "Bear Mountain State Park, NY", status: "open", userId: "user-1", spaceId: "space-1", options: [ - { startTime: "2026-03-14T00:00:00Z", duration: 0 }, - { startTime: "2026-03-15T00:00:00Z", duration: 0 }, - { startTime: "2026-03-21T00:00:00Z", duration: 0 }, - { startTime: "2026-03-22T00:00:00Z", duration: 0 }, + { startTime: d(11), duration: 0 }, + { startTime: d(12), duration: 0 }, + { startTime: d(18), duration: 0 }, + { startTime: d(19), duration: 0 }, ], participants: [ { @@ -207,9 +219,9 @@ const personalPolls: PollDef[] = [ userId: "user-1", spaceId: "space-1", options: [ - { startTime: "2026-03-13T19:00:00Z", duration: 120 }, - { startTime: "2026-03-14T18:00:00Z", duration: 120 }, - { startTime: "2026-03-20T19:00:00Z", duration: 120 }, + { startTime: d(-21, 19), duration: 120 }, + { startTime: d(-20, 18), duration: 120 }, + { startTime: d(-14, 19), duration: 120 }, ], participants: [ { @@ -233,16 +245,17 @@ const personalPolls: PollDef[] = [ title: "Book club meeting", description: "Discussing 'Project Hail Mary' by Andy Weir. Chapter 15 onwards.", + location: "Public Library, Room 2B", status: "open", timeZone: "America/New_York", - deadline: "2026-03-15T00:00:00Z", + deadline: d(6), userId: "user-1", spaceId: "space-1", options: [ - { startTime: "2026-03-18T19:00:00Z", duration: 90 }, - { startTime: "2026-03-19T19:00:00Z", duration: 90 }, - { startTime: "2026-03-20T19:00:00Z", duration: 90 }, - { startTime: "2026-03-25T19:00:00Z", duration: 90 }, + { startTime: d(8, 19), duration: 90 }, + { startTime: d(9, 19), duration: 90 }, + { startTime: d(10, 19), duration: 90 }, + { startTime: d(15, 19), duration: 90 }, ], participants: [ { @@ -296,10 +309,11 @@ const personalPolls: PollDef[] = [ timeZone: "America/New_York", userId: "user-1", spaceId: "space-1", + scheduledEventId: "event-car-service", options: [ - { startTime: "2026-03-09T09:00:00Z", duration: 60 }, - { startTime: "2026-03-10T09:00:00Z", duration: 60 }, - { startTime: "2026-03-11T09:00:00Z", duration: 60 }, + { startTime: d(-24, 9), duration: 60 }, + { startTime: d(-23, 9), duration: 60 }, + { startTime: d(-22, 9), duration: 60 }, ], participants: [ { @@ -313,13 +327,14 @@ const personalPolls: PollDef[] = [ title: "Photography class", description: "Beginner landscape photography workshop at the community center.", + location: "Riverside Community Center, Studio 3", status: "open", userId: "user-1", spaceId: "space-1", options: [ - { startTime: "2026-03-21T00:00:00Z", duration: 0 }, - { startTime: "2026-03-22T00:00:00Z", duration: 0 }, - { startTime: "2026-03-28T00:00:00Z", duration: 0 }, + { startTime: d(18), duration: 0 }, + { startTime: d(19), duration: 0 }, + { startTime: d(25), duration: 0 }, ], participants: [ { @@ -347,10 +362,10 @@ const personalPolls: PollDef[] = [ userId: "user-1", spaceId: "space-1", options: [ - { startTime: "2026-04-04T00:00:00Z", duration: 0 }, - { startTime: "2026-04-05T00:00:00Z", duration: 0 }, - { startTime: "2026-04-11T00:00:00Z", duration: 0 }, - { startTime: "2026-04-12T00:00:00Z", duration: 0 }, + { startTime: d(8), duration: 0 }, + { startTime: d(9), duration: 0 }, + { startTime: d(15), duration: 0 }, + { startTime: d(16), duration: 0 }, ], participants: [ { @@ -418,15 +433,16 @@ const acmePolls: PollDef[] = [ title: "Q2 Kickoff Meeting", description: "All-hands to review Q1 results and set Q2 objectives. Mandatory for all team leads.", + location: "Main Conference Room", status: "open", timeZone: "America/New_York", userId: "user-1", spaceId: "space-2", options: [ - { startTime: "2026-03-31T13:00:00Z", duration: 120 }, - { startTime: "2026-04-01T13:00:00Z", duration: 120 }, - { startTime: "2026-04-02T13:00:00Z", duration: 120 }, - { startTime: "2026-04-03T13:00:00Z", duration: 120 }, + { startTime: d(4, 13), duration: 120 }, + { startTime: d(5, 13), duration: 120 }, + { startTime: d(6, 13), duration: 120 }, + { startTime: d(7, 13), duration: 120 }, ], participants: [ { @@ -476,9 +492,9 @@ const acmePolls: PollDef[] = [ userId: "user-2", spaceId: "space-2", options: [ - { startTime: "2026-03-12T14:00:00Z", duration: 60 }, - { startTime: "2026-03-13T14:00:00Z", duration: 60 }, - { startTime: "2026-03-14T10:00:00Z", duration: 60 }, + { startTime: d(5, 14), duration: 60 }, + { startTime: d(6, 14), duration: 60 }, + { startTime: d(7, 10), duration: 60 }, ], participants: [ { @@ -508,14 +524,15 @@ const acmePolls: PollDef[] = [ { title: "Sprint Retrospective", description: "Reflecting on Sprint 14. What went well, what didn't.", + location: "Zoom", status: "closed", timeZone: "America/New_York", userId: "user-3", spaceId: "space-2", options: [ - { startTime: "2026-03-06T15:00:00Z", duration: 60 }, - { startTime: "2026-03-06T16:00:00Z", duration: 60 }, - { startTime: "2026-03-09T15:00:00Z", duration: 60 }, + { startTime: d(-28, 15), duration: 60 }, + { startTime: d(-28, 16), duration: 60 }, + { startTime: d(-25, 15), duration: 60 }, ], participants: [ { @@ -553,16 +570,17 @@ const acmePolls: PollDef[] = [ title: "Client Presentation Prep", description: "Final run-through before the Globex Corp pitch. Bring your slides.", + location: "Zoom", status: "open", timeZone: "America/New_York", userId: "user-1", spaceId: "space-2", options: [ - { startTime: "2026-03-17T10:00:00Z", duration: 60 }, - { startTime: "2026-03-17T14:00:00Z", duration: 60 }, - { startTime: "2026-03-18T10:00:00Z", duration: 60 }, - { startTime: "2026-03-18T14:00:00Z", duration: 60 }, - { startTime: "2026-03-19T10:00:00Z", duration: 60 }, + { startTime: d(3, 10), duration: 60 }, + { startTime: d(3, 14), duration: 60 }, + { startTime: d(4, 10), duration: 60 }, + { startTime: d(4, 14), duration: 60 }, + { startTime: d(5, 10), duration: 60 }, ], participants: [ { @@ -598,10 +616,10 @@ const acmePolls: PollDef[] = [ userId: "user-1", spaceId: "space-2", options: [ - { startTime: "2026-04-14T00:00:00Z", duration: 0 }, - { startTime: "2026-04-15T00:00:00Z", duration: 0 }, - { startTime: "2026-04-21T00:00:00Z", duration: 0 }, - { startTime: "2026-04-22T00:00:00Z", duration: 0 }, + { startTime: d(11), duration: 0 }, + { startTime: d(12), duration: 0 }, + { startTime: d(18), duration: 0 }, + { startTime: d(19), duration: 0 }, ], participants: [ { @@ -660,9 +678,9 @@ const acmePolls: PollDef[] = [ userId: "user-1", spaceId: "space-2", options: [ - { startTime: "2026-03-11T15:00:00Z", duration: 30 }, - { startTime: "2026-03-12T15:00:00Z", duration: 30 }, - { startTime: "2026-03-13T15:00:00Z", duration: 30 }, + { startTime: d(5, 15), duration: 30 }, + { startTime: d(6, 15), duration: 30 }, + { startTime: d(7, 15), duration: 30 }, ], participants: [ { @@ -687,10 +705,10 @@ const acmePolls: PollDef[] = [ userId: "user-3", spaceId: "space-2", options: [ - { startTime: "2026-03-10T13:00:00Z", duration: 15 }, - { startTime: "2026-03-10T13:30:00Z", duration: 15 }, - { startTime: "2026-03-10T14:00:00Z", duration: 15 }, - { startTime: "2026-03-10T14:30:00Z", duration: 15 }, + { startTime: d(-24, 13), duration: 15 }, + { startTime: d(-24, 13, 30), duration: 15 }, + { startTime: d(-24, 14), duration: 15 }, + { startTime: d(-24, 14, 30), duration: 15 }, ], participants: [ { @@ -732,9 +750,9 @@ const acmePolls: PollDef[] = [ userId: "user-2", spaceId: "space-2", options: [ - { startTime: "2026-03-19T13:00:00Z", duration: 180 }, - { startTime: "2026-03-20T13:00:00Z", duration: 180 }, - { startTime: "2026-03-21T13:00:00Z", duration: 180 }, + { startTime: d(9, 13), duration: 180 }, + { startTime: d(10, 13), duration: 180 }, + { startTime: d(11, 13), duration: 180 }, ], participants: [ { @@ -776,11 +794,12 @@ const acmePolls: PollDef[] = [ timeZone: "America/New_York", userId: "user-1", spaceId: "space-2", + scheduledEventId: "event-qbr", options: [ - { startTime: "2026-03-28T14:00:00Z", duration: 120 }, - { startTime: "2026-03-31T14:00:00Z", duration: 120 }, - { startTime: "2026-04-01T14:00:00Z", duration: 120 }, - { startTime: "2026-04-02T14:00:00Z", duration: 120 }, + { startTime: d(10, 14), duration: 120 }, + { startTime: d(14, 14), duration: 120 }, + { startTime: d(17, 14), duration: 120 }, + { startTime: d(18, 14), duration: 120 }, ], participants: [ { @@ -822,9 +841,9 @@ const acmePolls: PollDef[] = [ userId: "user-5", spaceId: "space-2", options: [ - { startTime: "2026-03-13T12:00:00Z", duration: 60 }, - { startTime: "2026-03-14T12:00:00Z", duration: 60 }, - { startTime: "2026-03-20T12:00:00Z", duration: 60 }, + { startTime: d(5, 12), duration: 60 }, + { startTime: d(6, 12), duration: 60 }, + { startTime: d(12, 12), duration: 60 }, ], participants: [ { @@ -879,16 +898,17 @@ const acmePolls: PollDef[] = [ title: "Interview Panel: Senior Designer", description: "We have 3 candidates next week. Need at least 3 panelists per slot.", + location: "Conference Room B", status: "open", timeZone: "America/New_York", requireParticipantEmail: true, userId: "user-2", spaceId: "space-2", options: [ - { startTime: "2026-03-18T14:00:00Z", duration: 90 }, - { startTime: "2026-03-19T14:00:00Z", duration: 90 }, - { startTime: "2026-03-20T14:00:00Z", duration: 90 }, - { startTime: "2026-03-21T14:00:00Z", duration: 90 }, + { startTime: d(15, 14), duration: 90 }, + { startTime: d(16, 14), duration: 90 }, + { startTime: d(17, 14), duration: 90 }, + { startTime: d(18, 14), duration: 90 }, ], participants: [ { @@ -924,10 +944,10 @@ const acmePolls: PollDef[] = [ userId: "user-4", spaceId: "space-2", options: [ - { startTime: "2026-12-12T00:00:00Z", duration: 0 }, - { startTime: "2026-12-13T00:00:00Z", duration: 0 }, - { startTime: "2026-12-19T00:00:00Z", duration: 0 }, - { startTime: "2026-12-20T00:00:00Z", duration: 0 }, + { startTime: d(253), duration: 0 }, + { startTime: d(254), duration: 0 }, + { startTime: d(260), duration: 0 }, + { startTime: d(261), duration: 0 }, ], participants: [ { @@ -986,6 +1006,7 @@ export const polls: PollDef[] = [...personalPolls, ...acmePolls]; // ─── Scheduled Events ──────────────────────────────────────────────────────── export type ScheduledEventDef = { + id?: string; title: string; description?: string; location?: string; @@ -1012,8 +1033,8 @@ export const scheduledEvents: ScheduledEventDef[] = [ location: "Dr. Lee's Office, 45 Oak St", status: "confirmed", timeZone: "America/New_York", - start: "2026-03-09T14:00:00Z", - end: "2026-03-09T15:00:00Z", + start: d(-25, 14), + end: d(-25, 15), allDay: false, userId: "user-1", spaceId: "space-1", @@ -1023,8 +1044,8 @@ export const scheduledEvents: ScheduledEventDef[] = [ location: "Olive Garden, Paramus", status: "confirmed", timeZone: "America/New_York", - start: "2026-03-14T18:00:00Z", - end: "2026-03-14T20:00:00Z", + start: d(-20, 18), + end: d(-20, 20), allDay: false, userId: "user-1", spaceId: "space-1", @@ -1034,8 +1055,8 @@ export const scheduledEvents: ScheduledEventDef[] = [ description: "Bear Mountain — meet at the trailhead parking lot at 8am.", status: "confirmed", timeZone: "America/New_York", - start: "2026-03-15T00:00:00Z", - end: "2026-03-16T00:00:00Z", + start: d(-19), + end: d(-18), allDay: true, userId: "user-1", spaceId: "space-1", @@ -1063,8 +1084,8 @@ export const scheduledEvents: ScheduledEventDef[] = [ location: "Public Library, Room 2B", status: "confirmed", timeZone: "America/New_York", - start: "2026-03-18T19:00:00Z", - end: "2026-03-18T20:30:00Z", + start: d(-16, 19), + end: d(-16, 20, 30), allDay: false, userId: "user-1", spaceId: "space-1", @@ -1092,16 +1113,35 @@ export const scheduledEvents: ScheduledEventDef[] = [ ], }, { + id: "event-car-service", title: "Car Service", location: "Mike's Auto Shop, 234 Elm St", status: "confirmed", timeZone: "America/New_York", - start: "2026-03-10T09:00:00Z", - end: "2026-03-10T10:00:00Z", + start: d(-24, 9), + end: d(-24, 10), allDay: false, userId: "user-1", spaceId: "space-1", }, + { + title: "Coffee with Alex", + location: "Café on 5th", + status: "confirmed", + timeZone: "America/New_York", + start: d(7, 10), + end: d(7, 11), + allDay: false, + userId: "user-1", + spaceId: "space-1", + invites: [ + { + inviteeName: "Alex Rivera", + inviteeEmail: "alex.rivera@gmail.com", + status: "accepted", + }, + ], + }, // ── Acme Inc events ──────────────────────────────────────────────────────── { @@ -1110,8 +1150,8 @@ export const scheduledEvents: ScheduledEventDef[] = [ location: "Zoom", status: "confirmed", timeZone: "America/New_York", - start: "2026-03-06T15:00:00Z", - end: "2026-03-06T16:00:00Z", + start: d(-28, 15), + end: d(-28, 16), allDay: false, userId: "user-3", spaceId: "space-2", @@ -1152,8 +1192,8 @@ export const scheduledEvents: ScheduledEventDef[] = [ location: "Conference Room A", status: "confirmed", timeZone: "America/New_York", - start: "2026-03-12T14:00:00Z", - end: "2026-03-12T15:00:00Z", + start: d(-22, 14), + end: d(-22, 15), allDay: false, userId: "user-2", spaceId: "space-2", @@ -1180,8 +1220,8 @@ export const scheduledEvents: ScheduledEventDef[] = [ location: "Globex HQ, 100 Innovation Blvd", status: "confirmed", timeZone: "America/New_York", - start: "2026-03-20T10:00:00Z", - end: "2026-03-20T11:30:00Z", + start: d(-14, 10), + end: d(-14, 11, 30), allDay: false, userId: "user-1", spaceId: "space-2", @@ -1210,26 +1250,20 @@ export const scheduledEvents: ScheduledEventDef[] = [ ], }, { - title: "Team Offsite", - description: "Spring team-building day at the Catskills ropes course.", - status: "confirmed", + title: "Engineering All-Hands", + description: "Monthly engineering department sync.", + status: "canceled", timeZone: "America/New_York", - start: "2026-04-15T00:00:00Z", - end: "2026-04-16T00:00:00Z", - allDay: true, - userId: "user-1", + start: d(-27, 17), + end: d(-27, 18), + allDay: false, + userId: "user-3", spaceId: "space-2", invites: [ { - inviteeName: "Sarah Chen", - inviteeEmail: "sarah@rallly.co", - inviteeId: "user-2", - status: "accepted", - }, - { - inviteeName: "Michael Torres", - inviteeEmail: "michael@rallly.co", - inviteeId: "user-3", + inviteeName: "Dev User", + inviteeEmail: "dev@rallly.co", + inviteeId: "user-1", status: "accepted", }, { @@ -1238,12 +1272,6 @@ export const scheduledEvents: ScheduledEventDef[] = [ inviteeId: "user-4", status: "accepted", }, - { - inviteeName: "James Okonkwo", - inviteeEmail: "james@rallly.co", - inviteeId: "user-5", - status: "pending", - }, ], }, { @@ -1252,8 +1280,8 @@ export const scheduledEvents: ScheduledEventDef[] = [ location: "Conference Room B", status: "confirmed", timeZone: "America/New_York", - start: "2026-03-13T12:00:00Z", - end: "2026-03-13T13:00:00Z", + start: d(-21, 12), + end: d(-21, 13), allDay: false, userId: "user-5", spaceId: "space-2", @@ -1290,8 +1318,8 @@ export const scheduledEvents: ScheduledEventDef[] = [ location: "Main Conference Room", status: "confirmed", timeZone: "America/New_York", - start: "2026-04-01T13:00:00Z", - end: "2026-04-01T15:00:00Z", + start: d(7, 13), + end: d(7, 15), allDay: false, userId: "user-1", spaceId: "space-2", @@ -1323,14 +1351,16 @@ export const scheduledEvents: ScheduledEventDef[] = [ ], }, { - title: "Engineering All-Hands", - description: "Monthly engineering department sync.", - status: "canceled", + title: "Product Roadmap Workshop", + description: + "Half-day session to align on H2 priorities using RICE framework.", + location: "Conference Room A", + status: "confirmed", timeZone: "America/New_York", - start: "2026-03-07T17:00:00Z", - end: "2026-03-07T18:00:00Z", + start: d(9, 13), + end: d(9, 16), allDay: false, - userId: "user-3", + userId: "user-2", spaceId: "space-2", invites: [ { @@ -1339,31 +1369,41 @@ export const scheduledEvents: ScheduledEventDef[] = [ inviteeId: "user-1", status: "accepted", }, + { + inviteeName: "Michael Torres", + inviteeEmail: "michael@rallly.co", + inviteeId: "user-3", + status: "accepted", + }, { inviteeName: "Emily Nakamura", inviteeEmail: "emily@rallly.co", inviteeId: "user-4", status: "accepted", }, + { + inviteeName: "James Okonkwo", + inviteeEmail: "james@rallly.co", + inviteeId: "user-5", + status: "tentative", + }, ], }, { - title: "Product Roadmap Workshop", - description: - "Half-day session to align on H2 priorities using RICE framework.", - location: "Conference Room A", + title: "Team Offsite", + description: "Spring team-building day at the Catskills ropes course.", status: "confirmed", timeZone: "America/New_York", - start: "2026-03-19T13:00:00Z", - end: "2026-03-19T16:00:00Z", - allDay: false, - userId: "user-2", + start: d(12), + end: d(13), + allDay: true, + userId: "user-1", spaceId: "space-2", invites: [ { - inviteeName: "Dev User", - inviteeEmail: "dev@rallly.co", - inviteeId: "user-1", + inviteeName: "Sarah Chen", + inviteeEmail: "sarah@rallly.co", + inviteeId: "user-2", status: "accepted", }, { @@ -1382,7 +1422,46 @@ export const scheduledEvents: ScheduledEventDef[] = [ inviteeName: "James Okonkwo", inviteeEmail: "james@rallly.co", inviteeId: "user-5", - status: "tentative", + status: "pending", + }, + ], + }, + { + id: "event-qbr", + title: "Quarterly Business Review", + description: + "Presenting Q1 numbers to leadership. Finance team please prepare slides by Thursday.", + status: "confirmed", + timeZone: "America/New_York", + start: d(14, 14), + end: d(14, 16), + allDay: false, + userId: "user-1", + spaceId: "space-2", + invites: [ + { + inviteeName: "Sarah Chen", + inviteeEmail: "sarah@rallly.co", + inviteeId: "user-2", + inviteeTimeZone: "America/Los_Angeles", + status: "accepted", + }, + { + inviteeName: "Michael Torres", + inviteeEmail: "michael@rallly.co", + inviteeId: "user-3", + inviteeTimeZone: "Europe/London", + status: "accepted", + }, + { + inviteeName: "Carla Mendez", + inviteeEmail: "carla.m@acme.co", + status: "accepted", + }, + { + inviteeName: "Owen Bradley", + inviteeEmail: "owen.b@acme.co", + status: "accepted", }, ], }, diff --git a/packages/tailwind-config/shared-styles.css b/packages/tailwind-config/shared-styles.css index bebb1d2981a..96f735dfde4 100644 --- a/packages/tailwind-config/shared-styles.css +++ b/packages/tailwind-config/shared-styles.css @@ -28,7 +28,7 @@ --destructive-foreground: var(--color-rose-100); --background: var(--color-white); - --foreground: var(--color-gray-700); + --foreground: var(--color-gray-800); --accent: oklch(0.552 0.016 285.938 / 0.1); --accent-border: var(--color-gray-300); @@ -102,7 +102,7 @@ --popover-accent: var(--color-gray-700); --card: color-mix(in srgb, var(--color-gray-800) 25%, transparent); - --card-border: var(--color-gray-700); + --card-border: color-mix(in srgb, var(--color-gray-600) 50%, transparent); --card-foreground: var(--color-gray-300); --card-accent: rgba(255, 255, 255, 0.02); } diff --git a/packages/ui/src/alert.tsx b/packages/ui/src/alert.tsx index a3a9ee61721..13a8ab80925 100644 --- a/packages/ui/src/alert.tsx +++ b/packages/ui/src/alert.tsx @@ -11,14 +11,14 @@ const alertVariants = cva( variant: { primary: "border-transparent bg-primary *:data-[slot=alert-description]:text-primary-foreground/90 [&>svg]:text-primary-foreground", - info: "border-blue-200 bg-blue-50 text-blue-700 *:data-[slot=alert-description]:text-blue-700/90 dark:border-blue-900 dark:bg-blue-950 dark:text-blue-100 dark:*:data-[slot=alert-description]:text-blue-100/90 [&>svg]:text-blue-700/90 dark:[&>svg]:text-blue-100/90", + info: "border-blue-500/20 bg-blue-500/10 text-blue-900 *:data-[slot=alert-description]:text-blue-900/90 dark:text-blue-100 dark:*:data-[slot=alert-description]:text-blue-100/90 [&>svg]:text-blue-900/75 dark:[&>svg]:text-blue-100/75", warning: - "border-yellow-200 bg-yellow-50 text-yellow-700 *:data-[slot=alert-description]:text-yellow-700/90 dark:border-yellow-900 dark:bg-yellow-950 dark:text-yellow-100 dark:*:data-[slot=alert-description]:text-yellow-100/90 [&>svg]:text-yellow-700/90 dark:[&>svg]:text-yellow-100/90", + "border-yellow-500/20 bg-yellow-500/10 text-yellow-900 *:data-[slot=alert-description]:text-yellow-900/90 dark:text-yellow-100 dark:*:data-[slot=alert-description]:text-yellow-100/90 [&>svg]:text-yellow-900/75 dark:[&>svg]:text-yellow-100/75", success: - "border-green-200 bg-green-50 text-green-700 *:data-[slot=alert-description]:text-green-700/90 dark:border-green-900 dark:bg-green-950 dark:text-green-100 dark:*:data-[slot=alert-description]:text-green-100/90 [&>svg]:text-green-700/90 dark:[&>svg]:text-green-100/90", + "border-green-500/20 bg-green-500/10 text-green-900 *:data-[slot=alert-description]:text-green-900/90 dark:text-green-100 dark:*:data-[slot=alert-description]:text-green-100/90 [&>svg]:text-green-900/75 dark:[&>svg]:text-green-100/75", note: "border-muted-border bg-muted *:data-[slot=alert-description]:text-muted-foreground/75 [&>svg]:text-muted-foreground/90", error: - "border-rose-200 bg-rose-50 text-rose-700 *:data-[slot=alert-description]:text-rose-700/90 dark:border-rose-900 dark:bg-rose-950 dark:text-rose-100 dark:*:data-[slot=alert-description]:text-rose-100/90 [&>svg]:text-rose-700/90 dark:[&>svg]:text-rose-100/90", + "border-rose-500/20 bg-rose-500/10 text-rose-900 *:data-[slot=alert-description]:text-rose-900/90 dark:text-rose-100 dark:*:data-[slot=alert-description]:text-rose-100/90 [&>svg]:text-rose-900/75 dark:[&>svg]:text-rose-100/75", }, }, defaultVariants: { diff --git a/packages/ui/src/avatar.tsx b/packages/ui/src/avatar.tsx index 0cf28bbdfc1..d9b453a5105 100644 --- a/packages/ui/src/avatar.tsx +++ b/packages/ui/src/avatar.tsx @@ -4,22 +4,19 @@ import type { VariantProps } from "class-variance-authority"; import { cva } from "class-variance-authority"; import * as React from "react"; -const avatarVariants = cva( - "relative flex shrink-0 overflow-hidden ring-1 ring-button-outline", - { - variants: { - size: { - xl: "size-12 rounded-xl text-lg", - lg: "size-8 rounded-md text-sm", - md: "size-5 rounded-md text-[10px]", - sm: "size-4 rounded text-[8px]", - }, - }, - defaultVariants: { - size: "md", +const avatarVariants = cva("relative flex shrink-0 overflow-hidden", { + variants: { + size: { + xl: "size-12 rounded-xl text-lg", + lg: "size-8 rounded-md text-sm", + md: "size-5 rounded-md text-[10px]", + sm: "size-4 rounded text-[8px]", }, }, -); + defaultVariants: { + size: "md", + }, +}); export type AvatarProps = VariantProps; diff --git a/packages/ui/src/card.tsx b/packages/ui/src/card.tsx index d77384a2958..1e03b2751d7 100644 --- a/packages/ui/src/card.tsx +++ b/packages/ui/src/card.tsx @@ -23,10 +23,7 @@ const CardHeader = React.forwardRef< >(({ className, ...props }, ref) => (
    )); @@ -73,10 +70,7 @@ const CardFooter = React.forwardRef< >(({ className, ...props }, ref) => (
    )); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f0a443f1b2f..78a8823f936 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -320,8 +320,8 @@ importers: specifier: ^3.7.0 version: 3.7.0 better-auth: - specifier: ^1.4.7 - version: 1.4.18(@prisma/client@7.3.0(prisma@7.3.0(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(mysql2@3.15.3)(next@16.2.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(pg@8.18.0)(prisma@7.3.0(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@24.10.11)(jiti@2.6.1)(jsdom@26.1.0)(lightningcss@1.30.2)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)) + specifier: ^1.6.0 + version: 1.6.0(@opentelemetry/api@1.9.0)(@prisma/client@7.3.0(prisma@7.3.0(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(mysql2@3.15.3)(next@16.2.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(pg@8.18.0)(prisma@7.3.0(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@24.10.11)(jiti@2.6.1)(jsdom@26.1.0)(lightningcss@1.30.2)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)) calendar-link: specifier: ^2.6.0 version: 2.11.0 @@ -1716,23 +1716,79 @@ packages: '@types/react': optional: true - '@better-auth/core@1.4.18': - resolution: {integrity: sha512-q+awYgC7nkLEBdx2sW0iJjkzgSHlIxGnOpsN1r/O1+a4m7osJNHtfK2mKJSL1I+GfNyIlxJF8WvD/NLuYMpmcg==} + '@better-auth/core@1.6.0': + resolution: {integrity: sha512-LmdPTyKRDn6iCcXBGlOHOyzpJl1W/3w64zrEbhhHaWmtdpzQWlY8awlWBoDTL9eL4TAusr9dDvwIbMYTvEqaeA==} peerDependencies: - '@better-auth/utils': 0.3.0 + '@better-auth/utils': 0.4.0 '@better-fetch/fetch': 1.1.21 - better-call: 1.1.8 + '@cloudflare/workers-types': '>=4' + '@opentelemetry/api': ^1.9.0 + better-call: 1.3.5 jose: ^6.1.0 kysely: ^0.28.5 nanostores: ^1.0.1 + peerDependenciesMeta: + '@cloudflare/workers-types': + optional: true + + '@better-auth/drizzle-adapter@1.6.0': + resolution: {integrity: sha512-iMgvZlrL4FI63CGaxLqE5rgA2Q9VVmc2fQIP7N5E79nGAEpHtztstHFPlen9RDLRJA4xa3wuyVaPSILylwE+LA==} + peerDependencies: + '@better-auth/core': ^1.6.0 + '@better-auth/utils': 0.4.0 + drizzle-orm: '>=0.41.0' + peerDependenciesMeta: + drizzle-orm: + optional: true + + '@better-auth/kysely-adapter@1.6.0': + resolution: {integrity: sha512-ZLEp2j3jquX7wrPQ7tPOSRAjmMoHhdrsgkuH9Bp/fgNZV7M1eiwAY6fHRGKad6KIldoI+iazMUIm60v11fIHCg==} + peerDependencies: + '@better-auth/core': ^1.6.0 + '@better-auth/utils': 0.4.0 + kysely: ^0.27.0 || ^0.28.0 + peerDependenciesMeta: + kysely: + optional: true + + '@better-auth/memory-adapter@1.6.0': + resolution: {integrity: sha512-FbLmz6ujltw8RDUkBzutwIfoV+q9Mu0gLVrfhDAb9INe+jLcaQikiIjFdVwPzpx+bOs6bWTDfylrlI6+Ytxs3Q==} + peerDependencies: + '@better-auth/core': ^1.6.0 + '@better-auth/utils': 0.4.0 + + '@better-auth/mongo-adapter@1.6.0': + resolution: {integrity: sha512-EYZwMpcpoaLRnfhEr+k+MTKS8SKi51TWh1b7bLSy+yHLL0PdbadFsGYZPgzLbZEaq4kUP0asMzXxA+blutjOQQ==} + peerDependencies: + '@better-auth/core': ^1.6.0 + '@better-auth/utils': 0.4.0 + mongodb: ^6.0.0 || ^7.0.0 + peerDependenciesMeta: + mongodb: + optional: true + + '@better-auth/prisma-adapter@1.6.0': + resolution: {integrity: sha512-8x/aqR1NckGiC49P02cxuH0wLzbJXvE/v2NnMEFo6h3uWq4ESYL0jTY9vNlFeVIKDyGSzrbteofzzG+yQv0wAQ==} + peerDependencies: + '@better-auth/core': ^1.6.0 + '@better-auth/utils': 0.4.0 + '@prisma/client': ^5.0.0 || ^6.0.0 || ^7.0.0 + prisma: ^5.0.0 || ^6.0.0 || ^7.0.0 + peerDependenciesMeta: + '@prisma/client': + optional: true + prisma: + optional: true - '@better-auth/telemetry@1.4.18': - resolution: {integrity: sha512-e5rDF8S4j3Um/0LIVATL2in9dL4lfO2fr2v1Wio4qTMRbfxqnUDTa+6SZtwdeJrbc4O+a3c+IyIpjG9Q/6GpfQ==} + '@better-auth/telemetry@1.6.0': + resolution: {integrity: sha512-JrJyx1ioswEAh8rB7mVxEFIDLl6AK3W3rtqc2MK6BgvcmKveWJ730Eoi/PNvi0b4tFk4kczmuQITm69uMbnTvQ==} peerDependencies: - '@better-auth/core': 1.4.18 + '@better-auth/core': ^1.6.0 + '@better-auth/utils': 0.4.0 + '@better-fetch/fetch': 1.1.21 - '@better-auth/utils@0.3.0': - resolution: {integrity: sha512-W+Adw6ZA6mgvnSnhOki270rwJ42t4XzSK6YWGF//BbVXL6SwCLWfyzBc1lN2m/4RM28KubdBKQ4X5VMoLRNPQw==} + '@better-auth/utils@0.4.0': + resolution: {integrity: sha512-RpMtLUIQAEWMgdPLNVbIF5ON2mm+CH0U3rCdUCU1VyeAUui4m38DyK7/aXMLZov2YDjG684pS1D0MBllrmgjQA==} '@better-fetch/fetch@1.1.21': resolution: {integrity: sha512-/ImESw0sskqlVR94jB+5+Pxjf+xBwDZF/N5+y2/q4EqD7IARUTSpPfIo8uf39SYpCxyOCtbyYpUrZ3F/k0zT4A==} @@ -5700,8 +5756,8 @@ packages: resolution: {integrity: sha512-RkaJzeJKDbaDWTIPiJwubyljaEPwpVWkm9Rt5h9Nd6h7tEXTJ3VB4qxdZBioV7JO5yLUaOKwz7vDOzlncUsegw==} engines: {node: '>=10.0.0'} - better-auth@1.4.18: - resolution: {integrity: sha512-bnyifLWBPcYVltH3RhS7CM62MoelEqC6Q+GnZwfiDWNfepXoQZBjEvn4urcERC7NTKgKq5zNBM8rvPvRBa6xcg==} + better-auth@1.6.0: + resolution: {integrity: sha512-reEK4X37w/X0Wi0ZpNSo6w3j9F2tsA7ebWn2AmWTzkceiatkxcadRg9aK+Mirw2PY56GQqX9dBgqBG6XMNU/Zg==} peerDependencies: '@lynx-js/react': '*' '@prisma/client': ^5.0.0 || ^6.0.0 || ^7.0.0 @@ -5762,8 +5818,8 @@ packages: vue: optional: true - better-call@1.1.8: - resolution: {integrity: sha512-XMQ2rs6FNXasGNfMjzbyroSwKwYbZ/T3IxruSS6U2MJRsSYh3wYtG3o6H00ZlKZ/C/UPOAD97tqgQJNsxyeTXw==} + better-call@1.3.5: + resolution: {integrity: sha512-kOFJkBP7utAQLEYrobZm3vkTH8mXq5GNgvjc5/XEST1ilVHaxXUXfeDeFlqoETMtyqS4+3/h4ONX2i++ebZrvA==} peerDependencies: zod: ^4.0.0 peerDependenciesMeta: @@ -7742,8 +7798,8 @@ packages: resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} engines: {node: '>=6'} - kysely@0.28.11: - resolution: {integrity: sha512-zpGIFg0HuoC893rIjYX1BETkVWdDnzTzF5e0kWXJFg5lE0k1/LfNWBejrcnOFu8Q2Rfq/hTDTU7XLUM8QOrpzg==} + kysely@0.28.15: + resolution: {integrity: sha512-r2clcf7HLWvDXaVUEvQymXJY4i3bSOIV3xsL/Upy3ZfSv5HeKsk9tsqbBptLvth5qHEIhxeHTA2jNLyQABkLBA==} engines: {node: '>=20.0.0'} lcm@0.0.3: @@ -8345,8 +8401,8 @@ packages: engines: {node: ^18 || >=20} hasBin: true - nanostores@1.1.0: - resolution: {integrity: sha512-yJBmDJr18xy47dbNVlHcgdPrulSn1nhSE6Ns9vTG+Nx9VPT6iV1MD6aQFp/t52zpf82FhLLTXAXr30NuCnxvwA==} + nanostores@1.2.0: + resolution: {integrity: sha512-F0wCzbsH80G7XXo0Jd9/AVQC7ouWY6idUCTnMwW5t/Rv9W8qmO6endavDwg7TNp5GbugwSukFMVZqzPSrSMndg==} engines: {node: ^20.0.0 || >=22.0.0} negotiator@0.6.3: @@ -9516,8 +9572,8 @@ packages: resolution: {integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==} engines: {node: '>= 0.8.0'} - set-cookie-parser@2.7.2: - resolution: {integrity: sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==} + set-cookie-parser@3.1.0: + resolution: {integrity: sha512-kjnC1DXBHcxaOaOXBHBeRtltsDG2nUiUni+jP92M9gYdW12rsmx92UsfpH7o5tDRs7I1ZZPSQJQGv3UaRfCiuw==} set-function-length@1.2.2: resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} @@ -12123,24 +12179,58 @@ snapshots: optionalDependencies: '@types/react': 19.2.13 - '@better-auth/core@1.4.18(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.0)': + '@better-auth/core@1.6.0(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.3.6))(jose@6.1.3)(kysely@0.28.15)(nanostores@1.2.0)': dependencies: - '@better-auth/utils': 0.3.0 + '@better-auth/utils': 0.4.0 '@better-fetch/fetch': 1.1.21 + '@opentelemetry/api': 1.9.0 + '@opentelemetry/semantic-conventions': 1.39.0 '@standard-schema/spec': 1.1.0 - better-call: 1.1.8(zod@4.3.6) + better-call: 1.3.5(zod@4.3.6) jose: 6.1.3 - kysely: 0.28.11 - nanostores: 1.1.0 + kysely: 0.28.15 + nanostores: 1.2.0 zod: 4.3.6 - '@better-auth/telemetry@1.4.18(@better-auth/core@1.4.18(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.0))': + '@better-auth/drizzle-adapter@1.6.0(@better-auth/core@1.6.0(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.3.6))(jose@6.1.3)(kysely@0.28.15)(nanostores@1.2.0))(@better-auth/utils@0.4.0)': + dependencies: + '@better-auth/core': 1.6.0(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.3.6))(jose@6.1.3)(kysely@0.28.15)(nanostores@1.2.0) + '@better-auth/utils': 0.4.0 + + '@better-auth/kysely-adapter@1.6.0(@better-auth/core@1.6.0(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.3.6))(jose@6.1.3)(kysely@0.28.15)(nanostores@1.2.0))(@better-auth/utils@0.4.0)(kysely@0.28.15)': + dependencies: + '@better-auth/core': 1.6.0(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.3.6))(jose@6.1.3)(kysely@0.28.15)(nanostores@1.2.0) + '@better-auth/utils': 0.4.0 + optionalDependencies: + kysely: 0.28.15 + + '@better-auth/memory-adapter@1.6.0(@better-auth/core@1.6.0(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.3.6))(jose@6.1.3)(kysely@0.28.15)(nanostores@1.2.0))(@better-auth/utils@0.4.0)': dependencies: - '@better-auth/core': 1.4.18(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.0) - '@better-auth/utils': 0.3.0 + '@better-auth/core': 1.6.0(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.3.6))(jose@6.1.3)(kysely@0.28.15)(nanostores@1.2.0) + '@better-auth/utils': 0.4.0 + + '@better-auth/mongo-adapter@1.6.0(@better-auth/core@1.6.0(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.3.6))(jose@6.1.3)(kysely@0.28.15)(nanostores@1.2.0))(@better-auth/utils@0.4.0)': + dependencies: + '@better-auth/core': 1.6.0(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.3.6))(jose@6.1.3)(kysely@0.28.15)(nanostores@1.2.0) + '@better-auth/utils': 0.4.0 + + '@better-auth/prisma-adapter@1.6.0(@better-auth/core@1.6.0(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.3.6))(jose@6.1.3)(kysely@0.28.15)(nanostores@1.2.0))(@better-auth/utils@0.4.0)(@prisma/client@7.3.0(prisma@7.3.0(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(prisma@7.3.0(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))': + dependencies: + '@better-auth/core': 1.6.0(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.3.6))(jose@6.1.3)(kysely@0.28.15)(nanostores@1.2.0) + '@better-auth/utils': 0.4.0 + optionalDependencies: + '@prisma/client': 7.3.0(prisma@7.3.0(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3) + prisma: 7.3.0(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + + '@better-auth/telemetry@1.6.0(@better-auth/core@1.6.0(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.3.6))(jose@6.1.3)(kysely@0.28.15)(nanostores@1.2.0))(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)': + dependencies: + '@better-auth/core': 1.6.0(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.3.6))(jose@6.1.3)(kysely@0.28.15)(nanostores@1.2.0) + '@better-auth/utils': 0.4.0 '@better-fetch/fetch': 1.1.21 - '@better-auth/utils@0.3.0': {} + '@better-auth/utils@0.4.0': + dependencies: + '@noble/hashes': 2.0.1 '@better-fetch/fetch@1.1.21': {} @@ -16500,19 +16590,24 @@ snapshots: basic-ftp@5.1.0: {} - better-auth@1.4.18(@prisma/client@7.3.0(prisma@7.3.0(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(mysql2@3.15.3)(next@16.2.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(pg@8.18.0)(prisma@7.3.0(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@24.10.11)(jiti@2.6.1)(jsdom@26.1.0)(lightningcss@1.30.2)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)): + better-auth@1.6.0(@opentelemetry/api@1.9.0)(@prisma/client@7.3.0(prisma@7.3.0(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(mysql2@3.15.3)(next@16.2.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(pg@8.18.0)(prisma@7.3.0(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@24.10.11)(jiti@2.6.1)(jsdom@26.1.0)(lightningcss@1.30.2)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)): dependencies: - '@better-auth/core': 1.4.18(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.0) - '@better-auth/telemetry': 1.4.18(@better-auth/core@1.4.18(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.0)) - '@better-auth/utils': 0.3.0 + '@better-auth/core': 1.6.0(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.3.6))(jose@6.1.3)(kysely@0.28.15)(nanostores@1.2.0) + '@better-auth/drizzle-adapter': 1.6.0(@better-auth/core@1.6.0(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.3.6))(jose@6.1.3)(kysely@0.28.15)(nanostores@1.2.0))(@better-auth/utils@0.4.0) + '@better-auth/kysely-adapter': 1.6.0(@better-auth/core@1.6.0(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.3.6))(jose@6.1.3)(kysely@0.28.15)(nanostores@1.2.0))(@better-auth/utils@0.4.0)(kysely@0.28.15) + '@better-auth/memory-adapter': 1.6.0(@better-auth/core@1.6.0(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.3.6))(jose@6.1.3)(kysely@0.28.15)(nanostores@1.2.0))(@better-auth/utils@0.4.0) + '@better-auth/mongo-adapter': 1.6.0(@better-auth/core@1.6.0(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.3.6))(jose@6.1.3)(kysely@0.28.15)(nanostores@1.2.0))(@better-auth/utils@0.4.0) + '@better-auth/prisma-adapter': 1.6.0(@better-auth/core@1.6.0(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.3.6))(jose@6.1.3)(kysely@0.28.15)(nanostores@1.2.0))(@better-auth/utils@0.4.0)(@prisma/client@7.3.0(prisma@7.3.0(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(prisma@7.3.0(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)) + '@better-auth/telemetry': 1.6.0(@better-auth/core@1.6.0(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.3.6))(jose@6.1.3)(kysely@0.28.15)(nanostores@1.2.0))(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21) + '@better-auth/utils': 0.4.0 '@better-fetch/fetch': 1.1.21 '@noble/ciphers': 2.1.1 '@noble/hashes': 2.0.1 - better-call: 1.1.8(zod@4.3.6) + better-call: 1.3.5(zod@4.3.6) defu: 6.1.4 jose: 6.1.3 - kysely: 0.28.11 - nanostores: 1.1.0 + kysely: 0.28.15 + nanostores: 1.2.0 zod: 4.3.6 optionalDependencies: '@prisma/client': 7.3.0(prisma@7.3.0(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3) @@ -16523,13 +16618,16 @@ snapshots: react: 19.2.4 react-dom: 19.2.4(react@19.2.4) vitest: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@24.10.11)(jiti@2.6.1)(jsdom@26.1.0)(lightningcss@1.30.2)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) + transitivePeerDependencies: + - '@cloudflare/workers-types' + - '@opentelemetry/api' - better-call@1.1.8(zod@4.3.6): + better-call@1.3.5(zod@4.3.6): dependencies: - '@better-auth/utils': 0.3.0 + '@better-auth/utils': 0.4.0 '@better-fetch/fetch': 1.1.21 rou3: 0.7.12 - set-cookie-parser: 2.7.2 + set-cookie-parser: 3.1.0 optionalDependencies: zod: 4.3.6 @@ -18881,7 +18979,7 @@ snapshots: kleur@3.0.3: {} - kysely@0.28.11: {} + kysely@0.28.15: {} lcm@0.0.3: dependencies: @@ -19750,7 +19848,7 @@ snapshots: nanoid@5.1.6: {} - nanostores@1.1.0: {} + nanostores@1.2.0: {} negotiator@0.6.3: {} @@ -21186,7 +21284,7 @@ snapshots: transitivePeerDependencies: - supports-color - set-cookie-parser@2.7.2: {} + set-cookie-parser@3.1.0: {} set-function-length@1.2.2: dependencies: