mirror of
https://github.com/lukevella/rallly.git
synced 2025-05-14 09:26:49 +02:00
Update major dependencies (#408)
This commit is contained in:
parent
6332d6459f
commit
e845d36c51
30 changed files with 1294 additions and 775 deletions
|
@ -24,7 +24,7 @@ module.exports = {
|
||||||
"sv",
|
"sv",
|
||||||
"zh",
|
"zh",
|
||||||
],
|
],
|
||||||
localePath: path.resolve("./public/locales"),
|
|
||||||
reloadOnPrerender: process.env.NODE_ENV === "development",
|
|
||||||
},
|
},
|
||||||
|
reloadOnPrerender: process.env.NODE_ENV === "development",
|
||||||
|
localePath: path.resolve("./public/locales"),
|
||||||
};
|
};
|
||||||
|
|
|
@ -9,15 +9,13 @@ const withBundleAnalyzer = require("@next/bundle-analyzer")({
|
||||||
enabled: process.env.ANALYZE === "true",
|
enabled: process.env.ANALYZE === "true",
|
||||||
});
|
});
|
||||||
|
|
||||||
const moduleExports = {
|
const nextConfig = {
|
||||||
future: {
|
|
||||||
webpack5: false,
|
|
||||||
},
|
|
||||||
i18n: i18n,
|
i18n: i18n,
|
||||||
productionBrowserSourceMaps: true,
|
productionBrowserSourceMaps: true,
|
||||||
webpack(config) {
|
webpack(config) {
|
||||||
config.module.rules.push({
|
config.module.rules.push({
|
||||||
test: /\.svg$/,
|
test: /\.svg$/,
|
||||||
|
issuer: /\.[jt]sx?$/,
|
||||||
use: ["@svgr/webpack"],
|
use: ["@svgr/webpack"],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -56,6 +54,9 @@ const moduleExports = {
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
|
sentry: {
|
||||||
|
hideSourceMaps: false,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const sentryWebpackPluginOptions = {
|
const sentryWebpackPluginOptions = {
|
||||||
|
@ -73,5 +74,5 @@ const sentryWebpackPluginOptions = {
|
||||||
// Make sure adding Sentry options is the last code to run before exporting, to
|
// Make sure adding Sentry options is the last code to run before exporting, to
|
||||||
// ensure that your source maps include changes from all other Webpack plugins
|
// ensure that your source maps include changes from all other Webpack plugins
|
||||||
module.exports = withBundleAnalyzer(
|
module.exports = withBundleAnalyzer(
|
||||||
withSentryConfig(moduleExports, sentryWebpackPluginOptions),
|
withSentryConfig(nextConfig, sentryWebpackPluginOptions),
|
||||||
);
|
);
|
||||||
|
|
55
package.json
55
package.json
|
@ -15,46 +15,54 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@floating-ui/react-dom-interactions": "^0.4.0",
|
"@floating-ui/react-dom-interactions": "^0.4.0",
|
||||||
"@headlessui/react": "^1.5.0",
|
"@headlessui/react": "^1.6.6",
|
||||||
"@next/bundle-analyzer": "^12.1.0",
|
"@next/bundle-analyzer": "^12.1.0",
|
||||||
"@prisma/client": "^4.1.0",
|
"@prisma/client": "^4.9.0",
|
||||||
"@sentry/nextjs": "^7.7.0",
|
"@radix-ui/react-radio-group": "^1.1.0",
|
||||||
|
"@radix-ui/react-slider": "^1.0.0",
|
||||||
|
"@radix-ui/react-slot": "^1.0.1",
|
||||||
|
"@radix-ui/react-tabs": "^1.0.1",
|
||||||
|
"@sentry/nextjs": "^7.17.3",
|
||||||
"@svgr/webpack": "^6.2.1",
|
"@svgr/webpack": "^6.2.1",
|
||||||
"@tailwindcss/forms": "^0.4.0",
|
"@tailwindcss/forms": "^0.4.0",
|
||||||
"@tailwindcss/typography": "^0.5.2",
|
"@tailwindcss/typography": "^0.5.2",
|
||||||
"@trpc/client": "^9.23.2",
|
"@tanstack/react-query": "^4.16.1",
|
||||||
"@trpc/next": "^9.23.2",
|
"@trpc/client": "^10.0.0-rc.8",
|
||||||
"@trpc/react": "^9.23.2",
|
"@trpc/next": "^10.0.0-rc.8",
|
||||||
"@trpc/server": "^9.23.2",
|
"@trpc/react-query": "^10.0.0-rc.8",
|
||||||
|
"@trpc/server": "^10.0.0-rc.8",
|
||||||
"accept-language-parser": "^1.5.0",
|
"accept-language-parser": "^1.5.0",
|
||||||
"axios": "^0.24.0",
|
"axios": "^0.24.0",
|
||||||
"clsx": "^1.1.1",
|
"clsx": "^1.1.1",
|
||||||
"dayjs": "^1.11.3",
|
"dayjs": "^1.11.7",
|
||||||
"eta": "^1.12.3",
|
"eta": "^1.12.3",
|
||||||
"framer-motion": "^6.3.11",
|
"framer-motion": "^6.3.11",
|
||||||
|
"i18next": "^22.0.4",
|
||||||
|
"immer": "^9.0.15",
|
||||||
"iron-session": "^6.1.3",
|
"iron-session": "^6.1.3",
|
||||||
"jose": "^4.5.1",
|
"jose": "^4.5.1",
|
||||||
"js-cookie": "^3.0.1",
|
"js-cookie": "^3.0.1",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"nanoid": "^3.1.30",
|
"nanoid": "^3.1.30",
|
||||||
"next": "^12.2.2",
|
"next": "^13.1.2",
|
||||||
"next-i18next": "^10.5.0",
|
"next-i18next": "^12.1.0",
|
||||||
"next-plausible": "^3.1.9",
|
"next-plausible": "^3.6.4",
|
||||||
"next-seo": "^5.14.1",
|
"next-seo": "^5.15.0",
|
||||||
"nodemailer": "^6.7.2",
|
"nodemailer": "^6.7.2",
|
||||||
"prisma": "^4.1.0",
|
"prisma": "^4.9.0",
|
||||||
"react": "17.0.2",
|
"react": "^18.2.0",
|
||||||
"react-big-calendar": "^0.38.9",
|
"react-big-calendar": "^1.5.0",
|
||||||
"react-dom": "17.0.2",
|
"react-dom": "^18.2.0",
|
||||||
"react-hook-form": "^7.31.3",
|
"react-hook-form": "^7.39.3",
|
||||||
"react-hot-toast": "^2.2.0",
|
"react-hot-toast": "^2.4.0",
|
||||||
"react-i18next": "^11.16.9",
|
"react-i18next": "^12.0.0",
|
||||||
"react-linkify": "^1.0.0-alpha",
|
"react-linkify": "^1.0.0-alpha",
|
||||||
"react-query": "^3.34.12",
|
"react-textarea-autosize": "^8.3.4",
|
||||||
"react-use": "^17.3.2",
|
"react-use": "^17.4.0",
|
||||||
"smoothscroll-polyfill": "^0.4.4",
|
"smoothscroll-polyfill": "^0.4.4",
|
||||||
"spacetime": "^7.1.4",
|
"spacetime": "^7.1.4",
|
||||||
"superjson": "^1.9.1",
|
"superjson": "^1.9.1",
|
||||||
|
"tailwind-scrollbar": "^2.0.1",
|
||||||
"timezone-soft": "^1.3.1",
|
"timezone-soft": "^1.3.1",
|
||||||
"typescript": "^4.5.2",
|
"typescript": "^4.5.2",
|
||||||
"zod": "^3.16.0"
|
"zod": "^3.16.0"
|
||||||
|
@ -73,7 +81,7 @@
|
||||||
"@typescript-eslint/parser": "^5.21.0",
|
"@typescript-eslint/parser": "^5.21.0",
|
||||||
"autoprefixer": "^10.4.2",
|
"autoprefixer": "^10.4.2",
|
||||||
"eslint": "^7.26.0",
|
"eslint": "^7.26.0",
|
||||||
"eslint-config-next": "^12.2.2",
|
"eslint-config-next": "^13.0.1",
|
||||||
"eslint-import-resolver-typescript": "^2.7.0",
|
"eslint-import-resolver-typescript": "^2.7.0",
|
||||||
"eslint-plugin-import": "^2.22.1",
|
"eslint-plugin-import": "^2.22.1",
|
||||||
"eslint-plugin-react": "^7.23.2",
|
"eslint-plugin-react": "^7.23.2",
|
||||||
|
@ -83,7 +91,8 @@
|
||||||
"postcss": "^8.4.6",
|
"postcss": "^8.4.6",
|
||||||
"prettier": "^2.3.0",
|
"prettier": "^2.3.0",
|
||||||
"prettier-plugin-tailwindcss": "^0.1.8",
|
"prettier-plugin-tailwindcss": "^0.1.8",
|
||||||
"tailwindcss": "^3.0.23",
|
"tailwindcss": "^3.2.4",
|
||||||
|
"tailwindcss-writing-mode": "^1.0.0",
|
||||||
"wait-on": "^6.0.1"
|
"wait-on": "^6.0.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
-- DropIndex
|
||||||
|
DROP INDEX "Participant_id_pollId_key";
|
||||||
|
|
||||||
|
-- DropIndex
|
||||||
|
DROP INDEX "participants_id_poll_id_key";
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "comments_poll_id_idx" ON "comments" USING HASH ("poll_id");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "options_poll_id_idx" ON "options" USING HASH ("poll_id");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "polls_user_id_idx" ON "polls" USING HASH ("user_id");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "votes_poll_id_idx" ON "votes" USING HASH ("poll_id");
|
|
@ -0,0 +1,2 @@
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "comments_user_id_idx" ON "comments" USING HASH ("user_id");
|
|
@ -0,0 +1,8 @@
|
||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- A unique constraint covering the columns `[id,poll_id]` on the table `participants` will be added. If there are existing duplicate values, this will fail.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "participants_id_poll_id_key" ON "participants"("id", "poll_id");
|
|
@ -1,13 +1,12 @@
|
||||||
datasource db {
|
datasource db {
|
||||||
provider = "postgresql"
|
provider = "postgresql"
|
||||||
url = env("DATABASE_URL")
|
url = env("DATABASE_URL")
|
||||||
referentialIntegrity = "prisma"
|
relationMode = "prisma"
|
||||||
}
|
}
|
||||||
|
|
||||||
generator client {
|
generator client {
|
||||||
provider = "prisma-client-js"
|
provider = "prisma-client-js"
|
||||||
previewFeatures = ["referentialIntegrity"]
|
binaryTargets = ["native"]
|
||||||
binaryTargets = ["native"]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
model User {
|
model User {
|
||||||
|
@ -16,6 +15,7 @@ model User {
|
||||||
email String @unique() @db.Citext
|
email String @unique() @db.Citext
|
||||||
createdAt DateTime @default(now()) @map("created_at")
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
updatedAt DateTime? @updatedAt @map("updated_at")
|
updatedAt DateTime? @updatedAt @map("updated_at")
|
||||||
|
comments Comment[]
|
||||||
polls Poll[]
|
polls Poll[]
|
||||||
|
|
||||||
@@map("users")
|
@@map("users")
|
||||||
|
@ -55,6 +55,7 @@ model Poll {
|
||||||
participantUrlId String @unique @map("participant_url_id")
|
participantUrlId String @unique @map("participant_url_id")
|
||||||
adminUrlId String @unique @map("admin_url_id")
|
adminUrlId String @unique @map("admin_url_id")
|
||||||
|
|
||||||
|
@@index([userId], type: Hash)
|
||||||
@@map("polls")
|
@@map("polls")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,7 +69,6 @@ model Participant {
|
||||||
createdAt DateTime @default(now()) @map("created_at")
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
updatedAt DateTime? @updatedAt @map("updated_at")
|
updatedAt DateTime? @updatedAt @map("updated_at")
|
||||||
|
|
||||||
|
|
||||||
@@index([pollId], type: Hash)
|
@@index([pollId], type: Hash)
|
||||||
@@unique([id, pollId])
|
@@unique([id, pollId])
|
||||||
@@map("participants")
|
@@map("participants")
|
||||||
|
@ -81,8 +81,8 @@ model Option {
|
||||||
poll Poll @relation(fields: [pollId], references: [id])
|
poll Poll @relation(fields: [pollId], references: [id])
|
||||||
createdAt DateTime @default(now()) @map("created_at")
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
updatedAt DateTime? @updatedAt @map("updated_at")
|
updatedAt DateTime? @updatedAt @map("updated_at")
|
||||||
votes Vote[]
|
|
||||||
|
|
||||||
|
@@index([pollId], type: Hash)
|
||||||
@@map("options")
|
@@map("options")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,7 +98,6 @@ model Vote {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
participant Participant @relation(fields: [participantId], references: [id], onDelete: Cascade)
|
participant Participant @relation(fields: [participantId], references: [id], onDelete: Cascade)
|
||||||
participantId String @map("participant_id")
|
participantId String @map("participant_id")
|
||||||
option Option @relation(fields: [optionId], references: [id], onDelete: Cascade)
|
|
||||||
optionId String @map("option_id")
|
optionId String @map("option_id")
|
||||||
poll Poll @relation(fields: [pollId], references: [id])
|
poll Poll @relation(fields: [pollId], references: [id])
|
||||||
pollId String @map("poll_id")
|
pollId String @map("poll_id")
|
||||||
|
@ -107,6 +106,7 @@ model Vote {
|
||||||
updatedAt DateTime? @updatedAt @map("updated_at")
|
updatedAt DateTime? @updatedAt @map("updated_at")
|
||||||
|
|
||||||
@@index([participantId], type: Hash)
|
@@index([participantId], type: Hash)
|
||||||
|
@@index([pollId], type: Hash)
|
||||||
@@map("votes")
|
@@map("votes")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,10 +116,13 @@ model Comment {
|
||||||
poll Poll @relation(fields: [pollId], references: [id])
|
poll Poll @relation(fields: [pollId], references: [id])
|
||||||
pollId String @map("poll_id")
|
pollId String @map("poll_id")
|
||||||
authorName String @map("author_name")
|
authorName String @map("author_name")
|
||||||
|
user User? @relation(fields:[userId], references: [id])
|
||||||
userId String? @map("user_id")
|
userId String? @map("user_id")
|
||||||
createdAt DateTime @default(now()) @map("created_at")
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
updatedAt DateTime? @updatedAt @map("updated_at")
|
updatedAt DateTime? @updatedAt @map("updated_at")
|
||||||
|
|
||||||
@@unique([id, pollId])
|
@@unique([id, pollId])
|
||||||
|
@@index([userId], type: Hash)
|
||||||
|
@@index([pollId], type: Hash)
|
||||||
@@map("comments")
|
@@map("comments")
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,17 +38,18 @@ const CookieConsentPopover: React.VoidFunctionComponent = () => {
|
||||||
browsing experience on this website.
|
browsing experience on this website.
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center space-x-6">
|
<div className="flex items-center space-x-6">
|
||||||
<Link href="/privacy-policy">
|
<Link
|
||||||
<a className="hover:text-primary-500 text-slate-400">
|
href="/privacy-policy"
|
||||||
Privacy Policy
|
className="text-slate-400 hover:text-primary-500"
|
||||||
</a>
|
>
|
||||||
|
Privacy Policy
|
||||||
</Link>
|
</Link>
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
Cookies.set("rallly_cookie_consent", "1", { expires: 365 });
|
Cookies.set("rallly_cookie_consent", "1", { expires: 365 });
|
||||||
setVisible(false);
|
setVisible(false);
|
||||||
}}
|
}}
|
||||||
className="bg-primary-500 hover:bg-primary-500/90 focus:ring-primary-200 active:bg-primary-600/90 grow rounded-md px-5 py-1 font-semibold text-white shadow-sm transition-all focus:ring-2"
|
className="grow rounded-md bg-primary-500 px-5 py-1 font-semibold text-white shadow-sm transition-all hover:bg-primary-500/90 focus:ring-2 focus:ring-primary-200 active:bg-primary-600/90"
|
||||||
>
|
>
|
||||||
OK
|
OK
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -89,16 +89,16 @@ const AnchorLink: React.VoidFunctionComponent<{
|
||||||
className?: string;
|
className?: string;
|
||||||
}> = ({ href = "", className, children, ...forwardProps }) => {
|
}> = ({ href = "", className, children, ...forwardProps }) => {
|
||||||
return (
|
return (
|
||||||
<Link href={href} passHref>
|
<Link
|
||||||
<a
|
href={href}
|
||||||
className={clsx(
|
passHref
|
||||||
"font-normal hover:text-white hover:no-underline",
|
className={clsx(
|
||||||
className,
|
"font-normal hover:text-white hover:no-underline",
|
||||||
)}
|
className,
|
||||||
{...forwardProps}
|
)}
|
||||||
>
|
{...forwardProps}
|
||||||
{children}
|
>
|
||||||
</a>
|
{children}
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -22,7 +22,7 @@ const ErrorPage: React.VoidFunctionComponent<ComponentProps> = ({
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation("errors");
|
const { t } = useTranslation("errors");
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto flex h-full max-w-full items-center justify-center bg-gray-50 px-4 py-8 lg:w-[1024px]">
|
<div className="mx-auto flex h-full max-w-full items-center justify-center px-4 py-8 lg:w-[1024px]">
|
||||||
<Head>
|
<Head>
|
||||||
<title>{title}</title>
|
<title>{title}</title>
|
||||||
<meta name="robots" content="noindex,nofollow" />
|
<meta name="robots" content="noindex,nofollow" />
|
||||||
|
@ -35,8 +35,13 @@ const ErrorPage: React.VoidFunctionComponent<ComponentProps> = ({
|
||||||
</div>
|
</div>
|
||||||
<p>{description}</p>
|
<p>{description}</p>
|
||||||
<div className="flex justify-center space-x-3">
|
<div className="flex justify-center space-x-3">
|
||||||
<Link href="/" passHref={true}>
|
<Link
|
||||||
<a className="btn-default">{t("goToHome")}</a>
|
href="/"
|
||||||
|
passHref={true}
|
||||||
|
className="btn-default"
|
||||||
|
legacyBehavior
|
||||||
|
>
|
||||||
|
{t("goToHome")}
|
||||||
</Link>
|
</Link>
|
||||||
<Button icon={<Chat />} onClick={showCrispChat}>
|
<Button icon={<Chat />} onClick={showCrispChat}>
|
||||||
{t("startChat")}
|
{t("startChat")}
|
||||||
|
|
|
@ -1,24 +1,20 @@
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import dynamic from "next/dynamic";
|
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { Trans, useTranslation } from "next-i18next";
|
import { Trans, useTranslation } from "next-i18next";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { createBreakpoint } from "react-use";
|
|
||||||
|
|
||||||
import DotsVertical from "@/components/icons/dots-vertical.svg";
|
import DotsVertical from "@/components/icons/dots-vertical.svg";
|
||||||
import Github from "@/components/icons/github.svg";
|
import Github from "@/components/icons/github.svg";
|
||||||
import Logo from "~/public/logo.svg";
|
import Logo from "~/public/logo.svg";
|
||||||
|
|
||||||
import Footer from "./page-layout/footer";
|
import Footer from "./page-layout/footer";
|
||||||
|
import Popover from "./popover";
|
||||||
|
|
||||||
const Popover = dynamic(() => import("./popover"), { ssr: false });
|
|
||||||
export interface PageLayoutProps {
|
export interface PageLayoutProps {
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
const useBreakpoint = createBreakpoint({ sm: 640, md: 768, lg: 1024 });
|
|
||||||
|
|
||||||
const Menu: React.VoidFunctionComponent<{ className: string }> = ({
|
const Menu: React.VoidFunctionComponent<{ className: string }> = ({
|
||||||
className,
|
className,
|
||||||
}) => {
|
}) => {
|
||||||
|
@ -26,27 +22,24 @@ const Menu: React.VoidFunctionComponent<{ className: string }> = ({
|
||||||
const { t } = useTranslation("common");
|
const { t } = useTranslation("common");
|
||||||
return (
|
return (
|
||||||
<nav className={className}>
|
<nav className={className}>
|
||||||
<Link href="/">
|
<Link
|
||||||
<a
|
href="/"
|
||||||
className={clsx(
|
className={clsx(
|
||||||
"text-gray-400 transition-colors hover:text-primary-500 hover:no-underline hover:underline-offset-2",
|
"text-gray-400 transition-colors hover:text-primary-500 hover:no-underline hover:underline-offset-2",
|
||||||
{
|
{
|
||||||
"pointer-events-none font-bold text-gray-600":
|
"pointer-events-none font-bold text-gray-600": pathname === "/home",
|
||||||
pathname === "/home",
|
},
|
||||||
},
|
)}
|
||||||
)}
|
>
|
||||||
>
|
{t("home")}
|
||||||
{t("home")}
|
|
||||||
</a>
|
|
||||||
</Link>
|
</Link>
|
||||||
<Link href="https://blog.rallly.co">
|
<Link
|
||||||
<a
|
href="https://blog.rallly.co"
|
||||||
className={clsx(
|
className={clsx(
|
||||||
"text-gray-400 transition-colors hover:text-primary-500 hover:no-underline hover:underline-offset-2",
|
"text-gray-400 transition-colors hover:text-primary-500 hover:no-underline hover:underline-offset-2",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{t("blog")}
|
{t("blog")}
|
||||||
</a>
|
|
||||||
</Link>
|
</Link>
|
||||||
<a
|
<a
|
||||||
href="https://support.rallly.co"
|
href="https://support.rallly.co"
|
||||||
|
@ -54,10 +47,11 @@ const Menu: React.VoidFunctionComponent<{ className: string }> = ({
|
||||||
>
|
>
|
||||||
{t("support")}
|
{t("support")}
|
||||||
</a>
|
</a>
|
||||||
<Link href="https://github.com/lukevella/rallly">
|
<Link
|
||||||
<a className="text-gray-400 transition-colors hover:text-primary-500 hover:no-underline hover:underline-offset-2">
|
href="https://github.com/lukevella/rallly"
|
||||||
<Github className="w-6" />
|
className="text-gray-400 transition-colors hover:text-primary-500 hover:no-underline hover:underline-offset-2"
|
||||||
</a>
|
>
|
||||||
|
<Github className="w-6" />
|
||||||
</Link>
|
</Link>
|
||||||
</nav>
|
</nav>
|
||||||
);
|
);
|
||||||
|
@ -66,7 +60,6 @@ const Menu: React.VoidFunctionComponent<{ className: string }> = ({
|
||||||
const PageLayout: React.VoidFunctionComponent<PageLayoutProps> = ({
|
const PageLayout: React.VoidFunctionComponent<PageLayoutProps> = ({
|
||||||
children,
|
children,
|
||||||
}) => {
|
}) => {
|
||||||
const breakpoint = useBreakpoint();
|
|
||||||
const { t } = useTranslation("homepage");
|
const { t } = useTranslation("homepage");
|
||||||
return (
|
return (
|
||||||
<div className="bg-pattern min-h-full overflow-x-hidden">
|
<div className="bg-pattern min-h-full overflow-x-hidden">
|
||||||
|
@ -74,9 +67,7 @@ const PageLayout: React.VoidFunctionComponent<PageLayoutProps> = ({
|
||||||
<div className="grow">
|
<div className="grow">
|
||||||
<div className="relative inline-block">
|
<div className="relative inline-block">
|
||||||
<Link href="/">
|
<Link href="/">
|
||||||
<a>
|
<Logo className="w-40 text-primary-500" alt="Rallly" />
|
||||||
<Logo className="w-40 text-primary-500" alt="Rallly" />
|
|
||||||
</a>
|
|
||||||
</Link>
|
</Link>
|
||||||
<span className="absolute -bottom-6 right-0 text-sm text-slate-400 transition-colors">
|
<span className="absolute -bottom-6 right-0 text-sm text-slate-400 transition-colors">
|
||||||
<Trans t={t} i18nKey="3Ls" components={{ e: <em /> }} />
|
<Trans t={t} i18nKey="3Ls" components={{ e: <em /> }} />
|
||||||
|
@ -84,18 +75,16 @@ const PageLayout: React.VoidFunctionComponent<PageLayoutProps> = ({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Menu className="hidden items-center space-x-8 md:flex" />
|
<Menu className="hidden items-center space-x-8 md:flex" />
|
||||||
{breakpoint === "sm" ? (
|
<Popover
|
||||||
<Popover
|
placement="left-start"
|
||||||
placement="left-start"
|
trigger={
|
||||||
trigger={
|
<button className="text-gray-400 transition-colors hover:text-primary-500 hover:no-underline hover:underline-offset-2 sm:hidden">
|
||||||
<button className="text-gray-400 transition-colors hover:text-primary-500 hover:no-underline hover:underline-offset-2">
|
<DotsVertical className="w-5" />
|
||||||
<DotsVertical className="w-5" />
|
</button>
|
||||||
</button>
|
}
|
||||||
}
|
>
|
||||||
>
|
<Menu className="flex flex-col space-y-2" />
|
||||||
<Menu className="flex flex-col space-y-2" />
|
</Popover>
|
||||||
</Popover>
|
|
||||||
) : null}
|
|
||||||
</div>
|
</div>
|
||||||
<div className="md:min-h-[calc(100vh-460px)]">{children}</div>
|
<div className="md:min-h-[calc(100vh-460px)]">{children}</div>
|
||||||
<Footer />
|
<Footer />
|
||||||
|
|
|
@ -86,10 +86,11 @@ const Footer: React.VoidFunctionComponent = () => {
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<Link href="https://blog.rallly.co">
|
<Link
|
||||||
<a className="inline-block font-normal text-slate-400 hover:text-slate-800 hover:no-underline">
|
href="https://blog.rallly.co"
|
||||||
{t("blog")}
|
className="inline-block font-normal text-slate-400 hover:text-slate-800 hover:no-underline"
|
||||||
</a>
|
>
|
||||||
|
{t("blog")}
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
|
@ -101,10 +102,11 @@ const Footer: React.VoidFunctionComponent = () => {
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<Link href="/privacy-policy">
|
<Link
|
||||||
<a className="inline-block font-normal text-slate-400 hover:text-slate-800 hover:no-underline">
|
href="/privacy-policy"
|
||||||
{t("privacyPolicy")}
|
className="inline-block font-normal text-slate-400 hover:text-slate-800 hover:no-underline"
|
||||||
</a>
|
>
|
||||||
|
{t("privacyPolicy")}
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
@ -204,7 +204,7 @@ const PollPage: NextPage = () => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
<div className="md:card mb-4 border-t bg-white md:overflow-hidden md:p-0">
|
<div className="mb-4 border border-t bg-white md:overflow-hidden md:rounded-md">
|
||||||
<div className="p-4 md:border-b md:p-6">
|
<div className="p-4 md:border-b md:p-6">
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
|
|
|
@ -65,11 +65,9 @@ export const Profile: React.VoidFunctionComponent = () => {
|
||||||
<div className="card p-0">
|
<div className="card p-0">
|
||||||
<div className="flex items-center justify-between border-b p-4 shadow-sm">
|
<div className="flex items-center justify-between border-b p-4 shadow-sm">
|
||||||
<div className="text-lg text-slate-700">{t("yourPolls")}</div>
|
<div className="text-lg text-slate-700">{t("yourPolls")}</div>
|
||||||
<Link href="/new">
|
<Link href="/new" className="btn-default">
|
||||||
<a className="btn-default">
|
<Pencil className="mr-1 h-5" />
|
||||||
<Pencil className="mr-1 h-5" />
|
{t("newPoll")}
|
||||||
{t("newPoll")}
|
|
||||||
</a>
|
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
{createdPolls.length > 0 ? (
|
{createdPolls.length > 0 ? (
|
||||||
|
@ -81,10 +79,11 @@ export const Profile: React.VoidFunctionComponent = () => {
|
||||||
<div>
|
<div>
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<Calendar className="mr-2 mt-[1px] h-5 text-primary-500" />
|
<Calendar className="mr-2 mt-[1px] h-5 text-primary-500" />
|
||||||
<Link href={`/admin/${poll.adminUrlId}`}>
|
<Link
|
||||||
<a className="text-slate-700 hover:text-primary-500 hover:no-underline">
|
href={`/admin/${poll.adminUrlId}`}
|
||||||
<div>{poll.title}</div>
|
className="text-slate-700 hover:text-primary-500 hover:no-underline"
|
||||||
</a>
|
>
|
||||||
|
<div>{poll.title}</div>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
<div className="ml-7 text-sm text-slate-500">
|
<div className="ml-7 text-sm text-slate-500">
|
||||||
|
|
|
@ -32,9 +32,7 @@ import { useSession } from "./session";
|
||||||
const HomeLink = () => {
|
const HomeLink = () => {
|
||||||
return (
|
return (
|
||||||
<Link href="/">
|
<Link href="/">
|
||||||
<a>
|
<Logo className="inline-block w-28 text-primary-500 transition-colors active:text-primary-600 lg:w-32" />
|
||||||
<Logo className="inline-block w-28 text-primary-500 transition-colors active:text-primary-600 lg:w-32" />
|
|
||||||
</a>
|
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -128,13 +126,15 @@ const AppMenu: React.VoidFunctionComponent<{ className?: string }> = ({
|
||||||
className,
|
className,
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation(["common", "app"]);
|
const { t } = useTranslation(["common", "app"]);
|
||||||
|
console.log("logo", Logo);
|
||||||
return (
|
return (
|
||||||
<div className={clsx("space-y-1", className)}>
|
<div className={clsx("space-y-1", className)}>
|
||||||
<Link href="/new">
|
<Link
|
||||||
<a className="flex cursor-pointer items-center space-x-2 whitespace-nowrap rounded-md px-2 py-1 pr-4 font-medium text-slate-600 transition-colors hover:bg-gray-200 hover:text-slate-600 hover:no-underline active:bg-gray-300">
|
href="/new"
|
||||||
<Pencil className="h-5 opacity-75 " />
|
className="flex cursor-pointer items-center space-x-2 whitespace-nowrap rounded-md px-2 py-1 pr-4 font-medium text-slate-600 transition-colors hover:bg-gray-200 hover:text-slate-600 hover:no-underline active:bg-gray-300"
|
||||||
<span className="inline-block">{t("app:newPoll")}</span>
|
>
|
||||||
</a>
|
<Pencil className="h-5 opacity-75 " />
|
||||||
|
<span className="inline-block">{t("app:newPoll")}</span>
|
||||||
</Link>
|
</Link>
|
||||||
<a
|
<a
|
||||||
target="_blank"
|
target="_blank"
|
||||||
|
@ -267,11 +267,12 @@ const StandardLayout: React.VoidFunctionComponent<{
|
||||||
<HomeLink />
|
<HomeLink />
|
||||||
</div>
|
</div>
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
<Link href="/new">
|
<Link
|
||||||
<a className="group mb-1 flex items-center space-x-3 whitespace-nowrap rounded-md px-3 py-1 font-medium text-slate-600 transition-colors hover:bg-slate-500/10 hover:text-slate-600 hover:no-underline active:bg-slate-500/20">
|
href="/new"
|
||||||
<Pencil className="h-5 opacity-75 group-hover:text-primary-500 group-hover:opacity-100" />
|
className="group mb-1 flex items-center space-x-3 whitespace-nowrap rounded-md px-3 py-1 font-medium text-slate-600 transition-colors hover:bg-slate-500/10 hover:text-slate-600 hover:no-underline active:bg-slate-500/20"
|
||||||
<span className="grow text-left">{t("app:newPoll")}</span>
|
>
|
||||||
</a>
|
<Pencil className="h-5 opacity-75 group-hover:text-primary-500 group-hover:opacity-100" />
|
||||||
|
<span className="grow text-left">{t("app:newPoll")}</span>
|
||||||
</Link>
|
</Link>
|
||||||
<a
|
<a
|
||||||
target="_blank"
|
target="_blank"
|
||||||
|
@ -350,10 +351,11 @@ const StandardLayout: React.VoidFunctionComponent<{
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col items-center space-y-4 px-6 pt-3 pb-6 text-slate-400 lg:h-16 lg:flex-row lg:space-y-0 lg:space-x-6 lg:py-0 lg:px-8 lg:pb-3">
|
<div className="flex flex-col items-center space-y-4 px-6 pt-3 pb-6 text-slate-400 lg:h-16 lg:flex-row lg:space-y-0 lg:space-x-6 lg:py-0 lg:px-8 lg:pb-3">
|
||||||
<div>
|
<div>
|
||||||
<Link href="https://rallly.co">
|
<Link
|
||||||
<a className="text-sm text-slate-400 transition-colors hover:text-primary-500 hover:no-underline">
|
href="https://rallly.co"
|
||||||
<Logo className="h-5" />
|
className="text-sm text-slate-400 transition-colors hover:text-primary-500 hover:no-underline"
|
||||||
</a>
|
>
|
||||||
|
<Logo className="h-5" />
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
<div className="hidden text-slate-300 lg:block">•</div>
|
<div className="hidden text-slate-300 lg:block">•</div>
|
||||||
|
@ -366,15 +368,17 @@ const StandardLayout: React.VoidFunctionComponent<{
|
||||||
>
|
>
|
||||||
{t("common:support")}
|
{t("common:support")}
|
||||||
</a>
|
</a>
|
||||||
<Link href="https://github.com/lukevella/rallly/discussions">
|
<Link
|
||||||
<a className="text-sm text-slate-400 transition-colors hover:text-primary-500 hover:no-underline">
|
href="https://github.com/lukevella/rallly/discussions"
|
||||||
{t("common:discussions")}
|
className="text-sm text-slate-400 transition-colors hover:text-primary-500 hover:no-underline"
|
||||||
</a>
|
>
|
||||||
|
{t("common:discussions")}
|
||||||
</Link>
|
</Link>
|
||||||
<Link href="https://blog.rallly.co">
|
<Link
|
||||||
<a className="text-sm text-slate-400 transition-colors hover:text-primary-500 hover:no-underline">
|
href="https://blog.rallly.co"
|
||||||
{t("common:blog")}
|
className="text-sm text-slate-400 transition-colors hover:text-primary-500 hover:no-underline"
|
||||||
</a>
|
>
|
||||||
|
{t("common:blog")}
|
||||||
</Link>
|
</Link>
|
||||||
<div className="hidden text-slate-300 lg:block">•</div>
|
<div className="hidden text-slate-300 lg:block">•</div>
|
||||||
<div className="flex items-center space-x-6">
|
<div className="flex items-center space-x-6">
|
||||||
|
|
|
@ -28,9 +28,8 @@ export function middleware({ headers, cookies, nextUrl }: NextRequest) {
|
||||||
|
|
||||||
// Check if locale is specified in cookie
|
// Check if locale is specified in cookie
|
||||||
const localeCookie = cookies.get("NEXT_LOCALE");
|
const localeCookie = cookies.get("NEXT_LOCALE");
|
||||||
|
if (localeCookie && supportedLocales.includes(localeCookie.value)) {
|
||||||
if (localeCookie && supportedLocales.includes(localeCookie)) {
|
newUrl.pathname = `/${localeCookie.value}${newUrl.pathname}`;
|
||||||
newUrl.pathname = `/${localeCookie}${newUrl.pathname}`;
|
|
||||||
return NextResponse.rewrite(newUrl);
|
return NextResponse.rewrite(newUrl);
|
||||||
} else {
|
} else {
|
||||||
// Check if locale is specified in header
|
// Check if locale is specified in header
|
||||||
|
@ -53,5 +52,13 @@ export function middleware({ headers, cookies, nextUrl }: NextRequest) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const config = {
|
export const config = {
|
||||||
matcher: ["/admin/:id", "/demo", "/p/:id", "/profile", "/new", "/login"],
|
matcher: [
|
||||||
|
"/admin/:id",
|
||||||
|
"/demo",
|
||||||
|
"/p/:id",
|
||||||
|
"/profile",
|
||||||
|
"/new",
|
||||||
|
"/login",
|
||||||
|
"/polls",
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,7 +2,6 @@ import "react-big-calendar/lib/css/react-big-calendar.css";
|
||||||
import "tailwindcss/tailwind.css";
|
import "tailwindcss/tailwind.css";
|
||||||
import "~/style.css";
|
import "~/style.css";
|
||||||
|
|
||||||
import { withTRPC } from "@trpc/next";
|
|
||||||
import { NextPage } from "next";
|
import { NextPage } from "next";
|
||||||
import { AppProps } from "next/app";
|
import { AppProps } from "next/app";
|
||||||
import dynamic from "next/dynamic";
|
import dynamic from "next/dynamic";
|
||||||
|
@ -10,14 +9,12 @@ import Head from "next/head";
|
||||||
import { appWithTranslation } from "next-i18next";
|
import { appWithTranslation } from "next-i18next";
|
||||||
import PlausibleProvider from "next-plausible";
|
import PlausibleProvider from "next-plausible";
|
||||||
import { DefaultSeo } from "next-seo";
|
import { DefaultSeo } from "next-seo";
|
||||||
import toast, { Toaster } from "react-hot-toast";
|
import { Toaster } from "react-hot-toast";
|
||||||
import { MutationCache } from "react-query";
|
|
||||||
import superjson from "superjson";
|
|
||||||
|
|
||||||
import Maintenance from "@/components/maintenance";
|
import Maintenance from "@/components/maintenance";
|
||||||
|
|
||||||
import { absoluteUrl } from "../utils/absolute-url";
|
import { absoluteUrl } from "../utils/absolute-url";
|
||||||
import { AppRouter } from "./api/trpc/[trpc]";
|
import { trpcNext } from "../utils/trpc";
|
||||||
|
|
||||||
const CrispChat = dynamic(() => import("@/components/crisp-chat"), {
|
const CrispChat = dynamic(() => import("@/components/crisp-chat"), {
|
||||||
ssr: false,
|
ssr: false,
|
||||||
|
@ -67,26 +64,4 @@ const MyApp: NextPage<AppProps> = ({ Component, pageProps }) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default withTRPC<AppRouter>({
|
export default trpcNext.withTRPC(appWithTranslation(MyApp));
|
||||||
config() {
|
|
||||||
const url = "/api/trpc";
|
|
||||||
|
|
||||||
return {
|
|
||||||
transformer: superjson,
|
|
||||||
url,
|
|
||||||
/**
|
|
||||||
* @link https://react-query.tanstack.com/reference/QueryClient
|
|
||||||
*/
|
|
||||||
queryClientConfig: {
|
|
||||||
mutationCache: new MutationCache({
|
|
||||||
onError: () => {
|
|
||||||
toast.error(
|
|
||||||
"Uh oh! Something went wrong. The issue has been logged and we'll fix it as soon as possible. Please try again later.",
|
|
||||||
);
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
ssr: false, // doesn't play well with how we're fetching legacy endpoints. consider switching it on when we don't need to get legacy polls
|
|
||||||
})(appWithTranslation(MyApp));
|
|
||||||
|
|
|
@ -1,24 +1,9 @@
|
||||||
import * as trpcNext from "@trpc/server/adapters/next";
|
import * as trpcNext from "@trpc/server/adapters/next";
|
||||||
import superjson from "superjson";
|
|
||||||
|
|
||||||
import { createContext } from "../../../server/context";
|
import { createContext } from "../../../server/context";
|
||||||
import { createRouter } from "../../../server/createRouter";
|
import { appRouter } from "../../../server/routers/_app";
|
||||||
import { login } from "../../../server/routers/login";
|
|
||||||
import { polls } from "../../../server/routers/polls";
|
|
||||||
import { session } from "../../../server/routers/session";
|
|
||||||
import { user } from "../../../server/routers/user";
|
|
||||||
import { withSessionRoute } from "../../../utils/auth";
|
import { withSessionRoute } from "../../../utils/auth";
|
||||||
|
|
||||||
export const appRouter = createRouter()
|
|
||||||
.transformer(superjson)
|
|
||||||
.merge("session.", session)
|
|
||||||
.merge("polls.", polls)
|
|
||||||
.merge(login)
|
|
||||||
.merge("user.", user);
|
|
||||||
|
|
||||||
// export type definition of API
|
|
||||||
export type AppRouter = typeof appRouter;
|
|
||||||
|
|
||||||
export const config = {
|
export const config = {
|
||||||
api: {
|
api: {
|
||||||
externalResolver: true,
|
externalResolver: true,
|
||||||
|
|
|
@ -3,9 +3,9 @@ import { serverSideTranslations } from "next-i18next/serverSideTranslations";
|
||||||
|
|
||||||
import Home from "@/components/home";
|
import Home from "@/components/home";
|
||||||
|
|
||||||
const Page = () => <Home />;
|
export default function Page() {
|
||||||
|
return <Home />;
|
||||||
export default Page;
|
}
|
||||||
|
|
||||||
export const getServerSideProps: GetServerSideProps = async ({
|
export const getServerSideProps: GetServerSideProps = async ({
|
||||||
locale = "en",
|
locale = "en",
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
import { GetServerSideProps } from "next";
|
import { GetServerSideProps } from "next";
|
||||||
import dynamic from "next/dynamic";
|
|
||||||
|
import CreatePoll from "@/components/create-poll";
|
||||||
|
|
||||||
import { withSessionSsr } from "../utils/auth";
|
import { withSessionSsr } from "../utils/auth";
|
||||||
import { withPageTranslations } from "../utils/with-page-translations";
|
import { withPageTranslations } from "../utils/with-page-translations";
|
||||||
|
|
||||||
|
export default CreatePoll;
|
||||||
|
|
||||||
export const getServerSideProps: GetServerSideProps = withSessionSsr(
|
export const getServerSideProps: GetServerSideProps = withSessionSsr(
|
||||||
withPageTranslations(["common", "app"]),
|
withPageTranslations(["common", "app"]),
|
||||||
);
|
);
|
||||||
|
|
||||||
export default dynamic(() => import("@/components/create-poll"), {
|
|
||||||
ssr: false,
|
|
||||||
});
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import { GetServerSideProps, NextPage } from "next";
|
import { GetServerSideProps, NextPage } from "next";
|
||||||
import dynamic from "next/dynamic";
|
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { useTranslation } from "next-i18next";
|
import { useTranslation } from "next-i18next";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import FullPageLoader from "@/components/full-page-loader";
|
import FullPageLoader from "@/components/full-page-loader";
|
||||||
|
import PollPage from "@/components/poll";
|
||||||
import { PollContextProvider } from "@/components/poll-context";
|
import { PollContextProvider } from "@/components/poll-context";
|
||||||
import { withSession } from "@/components/session";
|
import { withSession } from "@/components/session";
|
||||||
|
|
||||||
|
@ -15,8 +15,6 @@ import { trpc } from "../utils/trpc";
|
||||||
import { withPageTranslations } from "../utils/with-page-translations";
|
import { withPageTranslations } from "../utils/with-page-translations";
|
||||||
import Custom404 from "./404";
|
import Custom404 from "./404";
|
||||||
|
|
||||||
const PollPage = dynamic(() => import("@/components/poll"), { ssr: false });
|
|
||||||
|
|
||||||
const PollPageLoader: NextPage = () => {
|
const PollPageLoader: NextPage = () => {
|
||||||
const { query, asPath } = useRouter();
|
const { query, asPath } = useRouter();
|
||||||
const { t } = useTranslation("app");
|
const { t } = useTranslation("app");
|
||||||
|
|
|
@ -1,9 +1,27 @@
|
||||||
|
import { createProxySSGHelpers } from "@trpc/react-query/ssg";
|
||||||
|
import * as trpc from "@trpc/server";
|
||||||
import * as trpcNext from "@trpc/server/adapters/next";
|
import * as trpcNext from "@trpc/server/adapters/next";
|
||||||
|
import { GetServerSidePropsContext } from "next";
|
||||||
|
import superjson from "superjson";
|
||||||
|
|
||||||
export async function createContext(opts: trpcNext.CreateNextContextOptions) {
|
import { getCurrentUser } from "../utils/auth";
|
||||||
const session = opts.req.session;
|
import { appRouter } from "./routers/_app";
|
||||||
|
|
||||||
return {
|
export async function createContext(
|
||||||
session,
|
opts: trpcNext.CreateNextContextOptions | GetServerSidePropsContext,
|
||||||
};
|
) {
|
||||||
|
const user = await getCurrentUser(opts.req.session);
|
||||||
|
|
||||||
|
return { user, session: opts.req.session };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type Context = trpc.inferAsyncReturnType<typeof createContext>;
|
||||||
|
|
||||||
|
export const createSSGHelperFromContext = async (
|
||||||
|
ctx: GetServerSidePropsContext,
|
||||||
|
) =>
|
||||||
|
createProxySSGHelpers({
|
||||||
|
router: appRouter,
|
||||||
|
ctx: await createContext(ctx),
|
||||||
|
transformer: superjson,
|
||||||
|
});
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
import { inferAsyncReturnType } from "@trpc/server";
|
|
||||||
import * as trpc from "@trpc/server";
|
import * as trpc from "@trpc/server";
|
||||||
|
|
||||||
import { createContext } from "./context";
|
import { Context } from "./context";
|
||||||
|
|
||||||
type Context = inferAsyncReturnType<typeof createContext>;
|
|
||||||
|
|
||||||
// Helper function to create a router with your app's context
|
// Helper function to create a router with your app's context
|
||||||
export function createRouter() {
|
export function createRouter() {
|
||||||
|
|
42
src/server/routers/_app.ts
Normal file
42
src/server/routers/_app.ts
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { prisma } from "~/prisma/db";
|
||||||
|
|
||||||
|
import { createRouter } from "../createRouter";
|
||||||
|
import { mergeRouters, publicProcedure, router } from "../trpc";
|
||||||
|
import { login } from "./login";
|
||||||
|
import { polls } from "./polls";
|
||||||
|
import { session } from "./session";
|
||||||
|
import { user } from "./user";
|
||||||
|
|
||||||
|
const legacyRouter = createRouter()
|
||||||
|
.merge("user.", user)
|
||||||
|
.merge(login)
|
||||||
|
.merge("polls.", polls)
|
||||||
|
.merge("session.", session);
|
||||||
|
|
||||||
|
export const appRouter = mergeRouters(
|
||||||
|
legacyRouter.interop(),
|
||||||
|
router({
|
||||||
|
p: router({
|
||||||
|
touch: publicProcedure
|
||||||
|
.input(
|
||||||
|
z.object({
|
||||||
|
pollId: z.string(),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.mutation(async ({ input }) => {
|
||||||
|
await prisma.poll.update({
|
||||||
|
where: {
|
||||||
|
id: input.pollId,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
touchedAt: new Date(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
export type AppRouter = typeof appRouter;
|
|
@ -33,13 +33,10 @@ export const participants = createRouter()
|
||||||
pollId: z.string(),
|
pollId: z.string(),
|
||||||
participantId: z.string(),
|
participantId: z.string(),
|
||||||
}),
|
}),
|
||||||
resolve: async ({ input: { participantId, pollId } }) => {
|
resolve: async ({ input: { participantId } }) => {
|
||||||
await prisma.participant.delete({
|
await prisma.participant.delete({
|
||||||
where: {
|
where: {
|
||||||
id_pollId: {
|
id: participantId,
|
||||||
id: participantId,
|
|
||||||
pollId: pollId,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -100,10 +97,7 @@ export const participants = createRouter()
|
||||||
resolve: async ({ input: { pollId, participantId, votes, name } }) => {
|
resolve: async ({ input: { pollId, participantId, votes, name } }) => {
|
||||||
const participant = await prisma.participant.update({
|
const participant = await prisma.participant.update({
|
||||||
where: {
|
where: {
|
||||||
id_pollId: {
|
id: participantId,
|
||||||
id: participantId,
|
|
||||||
pollId: pollId,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
votes: {
|
votes: {
|
||||||
|
|
19
src/server/trpc.ts
Normal file
19
src/server/trpc.ts
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import { initTRPC } from "@trpc/server";
|
||||||
|
import superjson from "superjson";
|
||||||
|
|
||||||
|
import { Context } from "./context";
|
||||||
|
|
||||||
|
const t = initTRPC.context<Context>().create({
|
||||||
|
transformer: superjson,
|
||||||
|
errorFormatter({ shape }) {
|
||||||
|
return shape;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const router = t.router;
|
||||||
|
|
||||||
|
export const publicProcedure = t.procedure;
|
||||||
|
|
||||||
|
export const middleware = t.middleware;
|
||||||
|
|
||||||
|
export const mergeRouters = t.mergeRouters;
|
|
@ -1,4 +1,9 @@
|
||||||
import { IronSessionOptions, sealData, unsealData } from "iron-session";
|
import {
|
||||||
|
IronSession,
|
||||||
|
IronSessionOptions,
|
||||||
|
sealData,
|
||||||
|
unsealData,
|
||||||
|
} from "iron-session";
|
||||||
import { withIronSessionApiRoute, withIronSessionSsr } from "iron-session/next";
|
import { withIronSessionApiRoute, withIronSessionSsr } from "iron-session/next";
|
||||||
import { GetServerSideProps, NextApiHandler } from "next";
|
import { GetServerSideProps, NextApiHandler } from "next";
|
||||||
|
|
||||||
|
@ -96,3 +101,15 @@ export const mergeGuestsIntoUser = async (
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getCurrentUser = async (
|
||||||
|
session: IronSession,
|
||||||
|
): Promise<{ isGuest: boolean; id: string }> => {
|
||||||
|
const user = session.user;
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
throw new Error("Tried to get user but no user found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return user;
|
||||||
|
};
|
||||||
|
|
|
@ -1,5 +1,52 @@
|
||||||
import { createReactQueryHooks } from "@trpc/react";
|
import { MutationCache } from "@tanstack/react-query";
|
||||||
|
import { httpBatchLink } from "@trpc/client";
|
||||||
|
import { createTRPCNext } from "@trpc/next";
|
||||||
|
import { createReactQueryHooks } from "@trpc/react-query";
|
||||||
|
import toast from "react-hot-toast";
|
||||||
|
import superjson from "superjson";
|
||||||
|
|
||||||
import type { AppRouter } from "../pages/api/trpc/[trpc]";
|
import { AppRouter } from "../server/routers/_app";
|
||||||
|
|
||||||
export const trpc = createReactQueryHooks<AppRouter>();
|
export const trpc = createReactQueryHooks<AppRouter>();
|
||||||
|
|
||||||
|
export const trpcNext = createTRPCNext<AppRouter>({
|
||||||
|
unstable_overrides: {
|
||||||
|
useMutation: {
|
||||||
|
async onSuccess(opts) {
|
||||||
|
/**
|
||||||
|
* @note that order here matters:
|
||||||
|
* The order here allows route changes in `onSuccess` without
|
||||||
|
* having a flash of content change whilst redirecting.
|
||||||
|
**/
|
||||||
|
await opts.originalFn();
|
||||||
|
await opts.queryClient.invalidateQueries();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
config() {
|
||||||
|
return {
|
||||||
|
links: [
|
||||||
|
httpBatchLink({
|
||||||
|
url: `/api/trpc`,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
transformer: superjson,
|
||||||
|
queryClientConfig: {
|
||||||
|
defaultOptions: {
|
||||||
|
queries: {
|
||||||
|
cacheTime: Infinity,
|
||||||
|
staleTime: 30 * 1000, // 30 seconds
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mutationCache: new MutationCache({
|
||||||
|
onError: () => {
|
||||||
|
toast.error(
|
||||||
|
"Uh oh! Something went wrong. The issue has been logged and we'll fix it as soon as possible. Please try again later.",
|
||||||
|
);
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
ssr: false,
|
||||||
|
});
|
||||||
|
|
|
@ -25,8 +25,13 @@
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": ["src/*"],
|
"@/*": ["src/*"],
|
||||||
"~/*": ["./*"]
|
"~/*": ["./*"]
|
||||||
}
|
},
|
||||||
|
"plugins": [
|
||||||
|
{
|
||||||
|
"name": "next"
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"exclude": ["node_modules", "**/.*/", "**/*.js"],
|
"exclude": ["node_modules", "**/.*/", "**/*.js"],
|
||||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"]
|
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"]
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue