diff --git a/.env.development b/.env.development
new file mode 100644
index 000000000..c0451365a
--- /dev/null
+++ b/.env.development
@@ -0,0 +1,11 @@
+# A random 32-character secret key used to encrypt user sessions
+SECRET_PASSWORD=abcdef1234567890abcdef1234567890
+# The base url where this instance is accessible, including the scheme.
+# Example: https://example.com
+NEXT_PUBLIC_BASE_URL=http://localhost:3000
+NEXTAUTH_URL=$NEXT_PUBLIC_BASE_URL
+# A connection string to your Postgres database
+DATABASE_URL="postgres://postgres:postgres@localhost:5450/db"
+
+# Suppress warning from sentry during local development
+SENTRY_IGNORE_API_RESOLUTION_ERROR=1
\ No newline at end of file
diff --git a/.eslintrc.json b/.eslintrc.json
deleted file mode 100644
index 20647c2ab..000000000
--- a/.eslintrc.json
+++ /dev/null
@@ -1,25 +0,0 @@
-{
- "extends": ["next/core-web-vitals", "turbo"],
- "plugins": ["simple-import-sort", "@typescript-eslint"],
- "ignorePatterns": ["**/playwright-report/*.js"],
- "overrides": [
- {
- "files": ["**/*.ts", "**/*.tsx"],
- "parser": "@typescript-eslint/parser",
- "plugins": ["@typescript-eslint"],
- "extends": ["plugin:@typescript-eslint/recommended"],
- "rules": {
- "@typescript-eslint/no-unused-vars": "error"
- }
- }
- ],
- "rules": {
- "simple-import-sort/imports": "error",
- "simple-import-sort/exports": "error",
- "import/first": "error",
- "import/newline-after-import": "error",
- "import/no-duplicates": "error",
- "no-console": ["error", { "allow": ["warn", "error", "info"] }],
- "no-unused-vars": "error"
- }
-}
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 1a7dca149..ad8a66ef4 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -40,7 +40,7 @@ jobs:
runs-on: ubuntu-latest
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
- TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
+ TURBO_TEAM: ${{ vars.TURBO_TEAM }}
steps:
# Downloads a copy of the code in your repository before running CI tests
- name: Check out repository code
@@ -56,21 +56,21 @@ jobs:
node-version: 18
cache: yarn
- - name: Set environment variables
- run: |
- echo "DATABASE_URL=postgresql://postgres:password@localhost:5432/rallly" >> $GITHUB_ENV
-
- name: Install dependencies
run: yarn install --frozen-lockfile
+ - name: Set environment variables
+ run: |
+ echo "DATABASE_URL=postgresql://postgres:password@localhost:5450/db" >> $GITHUB_ENV
+
- name: Run db
run: |
docker pull postgres:14.2
- docker run -d -p 5432:5432 -e POSTGRES_PASSWORD=password -e POSTGRES_DB=rallly postgres:14.2
- yarn wait-on --timeout 60000 tcp:localhost:5432
+ docker run -d -p 5450:5432 -e POSTGRES_PASSWORD=password -e POSTGRES_DB=rallly postgres:14.2
+ yarn wait-on --timeout 60000 tcp:localhost:5450
- name: Deploy migrations
- run: yarn db:deploy
+ run: yarn db:setup
- name: Install playwright dependencies
run: yarn playwright install --with-deps chromium
diff --git a/README.md b/README.md
index 92817a6b9..958315c1a 100644
--- a/README.md
+++ b/README.md
@@ -24,45 +24,53 @@ Built with [Next.js](https://github.com/vercel/next.js/), [Prisma](https://githu
Check out the [self-hosting docs](https://support.rallly.co/self-hosting) for more information on running your own instance of Rallly.
-## Development
+## Get started
-Clone this repo and change directory to the root of the repository.
+1. Clone the repository switch to the project directory
-```bash
-git clone https://github.com/lukevella/rallly.git
-cd rallly
-```
+ ```bash
+ git clone https://github.com/lukevella/rallly.git
+ cd rallly
+ ```
-Install dependencies
+2. Install dependencies
-```
-yarn
-```
+ ```
+ yarn
+ ```
-Copy the sample `.env` file then open it and set the required [configuration options](https://support.rallly.co/self-hosting/configuration-options).
+3. Setup environment variables
-```bash
-cp sample.env .env
-```
+ ```bash
+ cp sample.env .env
+ ```
-Next, run the following command:
+ Create a `.env` file by copying `sample.env` then open it and set the required [configuration options](https://support.rallly.co/self-hosting/configuration-options).
-```
-yarn db:generate && yarn db:reset
-```
+4. Setup the database
-This will:
+ If you don't have a postgres database running locally, you can spin up a new database using docker by running:
-- generate the prisma database client
-- run migrations to create the database schema
-- seed the database with some random data
+ ```
+ yarn dx
+ ```
-Start the Next.js server
+ If you already have a postgres database, you can run the migrations and seed the database by running:
-```
-# For development
-yarn dev
-```
+ ```
+ yarn db:setup
+ ```
+
+ This will:
+
+ - run migrations to create the database schema
+ - seed the database with test users and random data
+
+5. Start the Next.js server
+
+ ```
+ yarn dev
+ ```
## Contributors
diff --git a/apps/docs/.eslintrc.js b/apps/docs/.eslintrc.js
new file mode 100644
index 000000000..3d9be6e61
--- /dev/null
+++ b/apps/docs/.eslintrc.js
@@ -0,0 +1,4 @@
+/** @type {import("eslint").Linter.Config} */
+module.exports = {
+ ...require("@rallly/eslint-config")(__dirname),
+};
diff --git a/apps/docs/package.json b/apps/docs/package.json
index 2f0cdf097..49e3d38ab 100644
--- a/apps/docs/package.json
+++ b/apps/docs/package.json
@@ -1,5 +1,8 @@
{
"name": "@rallly/docs",
"version": "0.0.0",
- "private": true
+ "private": true,
+ "devDependencies": {
+ "@rallly/tsconfig": "*"
+ }
}
diff --git a/apps/docs/tsconfig.json b/apps/docs/tsconfig.json
new file mode 100644
index 000000000..4ad2dd442
--- /dev/null
+++ b/apps/docs/tsconfig.json
@@ -0,0 +1,5 @@
+{
+ "extends": "@rallly/tsconfig/next.json",
+ "include": ["**/*.ts", "**/*.tsx", "**/*.js"],
+ "exclude": ["node_modules"]
+}
diff --git a/apps/landing/.eslintrc.js b/apps/landing/.eslintrc.js
new file mode 100644
index 000000000..3d9be6e61
--- /dev/null
+++ b/apps/landing/.eslintrc.js
@@ -0,0 +1,4 @@
+/** @type {import("eslint").Linter.Config} */
+module.exports = {
+ ...require("@rallly/eslint-config")(__dirname),
+};
diff --git a/apps/landing/package.json b/apps/landing/package.json
index 4d64deceb..ba031e129 100644
--- a/apps/landing/package.json
+++ b/apps/landing/package.json
@@ -20,7 +20,6 @@
"@svgr/webpack": "^6.5.1",
"@tailwindcss/typography": "^0.5.9",
"@vercel/analytics": "^0.1.8",
- "@vercel/og": "^0.5.11",
"autoprefixer": "^10.4.13",
"class-variance-authority": "^0.6.0",
"dayjs": "^1.11.7",
@@ -50,15 +49,8 @@
"@types/smoothscroll-polyfill": "^0.3.1",
"cheerio": "^1.0.0-rc.12",
"cross-env": "^7.0.3",
- "eslint-config-turbo": "^0.0.9",
- "eslint-import-resolver-typescript": "^2.7.0",
- "eslint-plugin-import": "^2.22.1",
- "eslint-plugin-react": "^7.23.2",
- "eslint-plugin-react-hooks": "^4.2.0",
- "eslint-plugin-simple-import-sort": "^7.0.0",
"i18next-scanner": "^4.2.0",
"i18next-scanner-typescript": "^1.1.1",
- "prettier-plugin-tailwindcss": "^0.1.8",
"smtp-tester": "^2.0.1",
"wait-on": "^6.0.1"
}
diff --git a/apps/landing/src/components/error-page.tsx b/apps/landing/src/components/error-page.tsx
index 12621b667..9259230e8 100644
--- a/apps/landing/src/components/error-page.tsx
+++ b/apps/landing/src/components/error-page.tsx
@@ -1,4 +1,4 @@
-import { FrownIcon } from "@rallly/icons";
+import { FrownIcon } from "lucide-react";
import Head from "next/head";
import Link from "next/link";
import { useTranslation } from "next-i18next";
diff --git a/apps/landing/src/components/home/bonus.tsx b/apps/landing/src/components/home/bonus.tsx
index 6a4fc1e76..fa98a7cf2 100644
--- a/apps/landing/src/components/home/bonus.tsx
+++ b/apps/landing/src/components/home/bonus.tsx
@@ -1,11 +1,11 @@
+import { cn } from "@rallly/ui";
+import { m } from "framer-motion";
import {
CalendarCheck2Icon,
LanguagesIcon,
Users2Icon,
ZapIcon,
-} from "@rallly/icons";
-import { cn } from "@rallly/ui";
-import { m } from "framer-motion";
+} from "lucide-react";
import { Trans } from "@/components/trans";
import { IconComponent } from "@/types";
diff --git a/apps/landing/src/components/home/color-hash.ts b/apps/landing/src/components/home/color-hash.ts
index 9e03aaedd..93721495b 100644
--- a/apps/landing/src/components/home/color-hash.ts
+++ b/apps/landing/src/components/home/color-hash.ts
@@ -40,7 +40,7 @@ function isBright(color: RGBColor): boolean {
export const getRandomAvatarColor = (str: string) => {
const strSum = str.split("").reduce((acc, val) => acc + val.charCodeAt(0), 0);
const randomIndex = strSum % avatarBackgroundColors.length;
- const color = avatarBackgroundColors[randomIndex];
+ const color = avatarBackgroundColors[randomIndex] as RGBColor;
const [r, g, b] = color;
return { color: `rgb(${r}, ${g}, ${b})`, requiresDarkText: isBright(color) };
};
diff --git a/apps/landing/src/components/home/hero.tsx b/apps/landing/src/components/home/hero.tsx
index 9668bf887..cd5fa3e56 100644
--- a/apps/landing/src/components/home/hero.tsx
+++ b/apps/landing/src/components/home/hero.tsx
@@ -1,8 +1,8 @@
-import { ChevronRightIcon } from "@rallly/icons";
import { Badge } from "@rallly/ui/badge";
import { Button } from "@rallly/ui/button";
import { preventWidows } from "@rallly/utils";
import { m } from "framer-motion";
+import { ChevronRightIcon } from "lucide-react";
import Image from "next/image";
import Link from "next/link";
import * as React from "react";
diff --git a/apps/landing/src/components/layouts/blog-layout.tsx b/apps/landing/src/components/layouts/blog-layout.tsx
index 969d6ed5f..06194eb06 100644
--- a/apps/landing/src/components/layouts/blog-layout.tsx
+++ b/apps/landing/src/components/layouts/blog-layout.tsx
@@ -1,4 +1,4 @@
-import { NewspaperIcon } from "@rallly/icons";
+import { NewspaperIcon } from "lucide-react";
import Script from "next/script";
import PageLayout from "@/components/layouts/page-layout";
diff --git a/apps/landing/src/components/layouts/page-layout.tsx b/apps/landing/src/components/layouts/page-layout.tsx
index d73055342..d4a1fa3b6 100644
--- a/apps/landing/src/components/layouts/page-layout.tsx
+++ b/apps/landing/src/components/layouts/page-layout.tsx
@@ -1,4 +1,3 @@
-import { ChevronRightIcon, MenuIcon } from "@rallly/icons";
import { cn } from "@rallly/ui";
import {
DropdownMenu,
@@ -7,6 +6,7 @@ import {
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@rallly/ui/dropdown-menu";
+import { ChevronRightIcon, MenuIcon } from "lucide-react";
import Image from "next/image";
import Link from "next/link";
import { useRouter } from "next/router";
diff --git a/apps/landing/src/components/layouts/page-layout/footer.tsx b/apps/landing/src/components/layouts/page-layout/footer.tsx
index c70de6683..485acd739 100644
--- a/apps/landing/src/components/layouts/page-layout/footer.tsx
+++ b/apps/landing/src/components/layouts/page-layout/footer.tsx
@@ -1,10 +1,4 @@
-import {
- DiscordIcon,
- GithubIcon,
- LanguagesIcon,
- LinkedinIcon,
- TwitterIcon,
-} from "@rallly/icons";
+import { DiscordIcon } from "@rallly/icons";
import languages from "@rallly/languages";
import {
Select,
@@ -13,6 +7,12 @@ import {
SelectTrigger,
SelectValue,
} from "@rallly/ui/select";
+import {
+ GithubIcon,
+ LanguagesIcon,
+ LinkedinIcon,
+ TwitterIcon,
+} from "lucide-react";
import Image from "next/image";
import Link from "next/link";
import { useRouter } from "next/router";
diff --git a/apps/landing/src/components/marketing.tsx b/apps/landing/src/components/marketing.tsx
index 893eba245..d42e92f44 100644
--- a/apps/landing/src/components/marketing.tsx
+++ b/apps/landing/src/components/marketing.tsx
@@ -1,5 +1,5 @@
-import { ArrowUpRight } from "@rallly/icons";
import { m } from "framer-motion";
+import { ArrowUpRight } from "lucide-react";
import Image from "next/image";
import Link from "next/link";
import { NextSeo } from "next-seo";
diff --git a/apps/landing/src/pages/404.tsx b/apps/landing/src/pages/404.tsx
index 6a0cdc20c..e1a076c16 100644
--- a/apps/landing/src/pages/404.tsx
+++ b/apps/landing/src/pages/404.tsx
@@ -1,4 +1,4 @@
-import { FileSearchIcon } from "@rallly/icons";
+import { FileSearchIcon } from "lucide-react";
import { GetStaticProps } from "next";
import { useTranslation } from "next-i18next";
import { serverSideTranslations } from "next-i18next/serverSideTranslations";
diff --git a/apps/landing/src/pages/api/og-image.tsx b/apps/landing/src/pages/api/og-image.tsx
index 2cbe6d767..16d7235af 100644
--- a/apps/landing/src/pages/api/og-image.tsx
+++ b/apps/landing/src/pages/api/og-image.tsx
@@ -1,5 +1,5 @@
/* eslint-disable @next/next/no-img-element */
-import { ImageResponse } from "@vercel/og";
+import { ImageResponse } from "next/og";
import { NextRequest } from "next/server";
export const config = {
diff --git a/apps/landing/src/pages/blog/[slug].tsx b/apps/landing/src/pages/blog/[slug].tsx
index fc6db6656..ab817380b 100644
--- a/apps/landing/src/pages/blog/[slug].tsx
+++ b/apps/landing/src/pages/blog/[slug].tsx
@@ -1,4 +1,4 @@
-import { ArrowLeftIcon } from "@rallly/icons";
+import { ArrowLeftIcon } from "lucide-react";
import { GetStaticPropsContext } from "next";
import ErrorPage from "next/error";
import Head from "next/head";
@@ -117,6 +117,8 @@ export async function getStaticProps(ctx: GetStaticPropsContext) {
},
};
}
+
+ return res;
}
export async function getStaticPaths() {
diff --git a/apps/landing/src/pages/pricing.tsx b/apps/landing/src/pages/pricing.tsx
index a7474a6d8..d833277ad 100644
--- a/apps/landing/src/pages/pricing.tsx
+++ b/apps/landing/src/pages/pricing.tsx
@@ -1,4 +1,3 @@
-import { TrendingUpIcon } from "@rallly/icons";
import {
BillingPlan,
BillingPlanDescription,
@@ -11,6 +10,7 @@ import {
} from "@rallly/ui/billing-plan";
import { Button } from "@rallly/ui/button";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@rallly/ui/tabs";
+import { TrendingUpIcon } from "lucide-react";
import Link from "next/link";
import { useTranslation } from "next-i18next";
import { NextSeo } from "next-seo";
diff --git a/apps/landing/tsconfig.json b/apps/landing/tsconfig.json
index bfe5fb18a..e259cb9d5 100644
--- a/apps/landing/tsconfig.json
+++ b/apps/landing/tsconfig.json
@@ -1,24 +1,13 @@
{
+ "extends": "@rallly/tsconfig/next.json",
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"],
"~/*": ["public/*"]
},
- "lib": ["dom", "dom.iterable", "esnext"],
- "allowJs": true,
- "skipLibCheck": true,
- "strict": true,
- "forceConsistentCasingInFileNames": true,
- "noEmit": true,
- "incremental": true,
- "esModuleInterop": true,
- "module": "esnext",
- "moduleResolution": "node",
- "resolveJsonModule": true,
- "isolatedModules": true,
- "jsx": "preserve"
+ "checkJs": false
},
- "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "next-i18next.config.js"],
- "exclude": ["node_modules", "**/*.js"]
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
+ "exclude": ["node_modules"]
}
diff --git a/.env.test b/apps/web/.env.test
similarity index 59%
rename from .env.test
rename to apps/web/.env.test
index 294497791..bfd50acbf 100644
--- a/.env.test
+++ b/apps/web/.env.test
@@ -2,5 +2,5 @@ PORT=3002
NEXT_PUBLIC_BASE_URL=http://localhost:3002
NEXTAUTH_URL=http://localhost:3002
SECRET_PASSWORD=abcdefghijklmnopqrstuvwxyz1234567890
-DATABASE_URL=postgres://postgres:postgres@localhost:5432/rallly
-SUPPORT_EMAIL=support@rallly.co
\ No newline at end of file
+DATABASE_URL=postgres://postgres:postgres@localhost:5450/db
+SUPPORT_EMAIL=support@rallly.co
diff --git a/apps/web/.eslintrc.js b/apps/web/.eslintrc.js
new file mode 100644
index 000000000..3d9be6e61
--- /dev/null
+++ b/apps/web/.eslintrc.js
@@ -0,0 +1,4 @@
+/** @type {import("eslint").Linter.Config} */
+module.exports = {
+ ...require("@rallly/eslint-config")(__dirname),
+};
diff --git a/apps/web/declarations/i18next.d.ts b/apps/web/declarations/i18next.d.ts
index 25e3fcb59..9ded39e18 100644
--- a/apps/web/declarations/i18next.d.ts
+++ b/apps/web/declarations/i18next.d.ts
@@ -1,4 +1,4 @@
-import "react-i18next";
+import "i18next";
import app from "../public/locales/en/app.json";
diff --git a/apps/web/next-env.d.ts b/apps/web/next-env.d.ts
index 4f11a03dc..fd36f9494 100644
--- a/apps/web/next-env.d.ts
+++ b/apps/web/next-env.d.ts
@@ -1,5 +1,6 @@
///
///
+///
// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.
diff --git a/apps/web/next.config.js b/apps/web/next.config.js
index ed2cad4dc..02cedd428 100644
--- a/apps/web/next.config.js
+++ b/apps/web/next.config.js
@@ -4,17 +4,16 @@
// https://docs.sentry.io/platforms/javascript/guides/nextjs/
const { withSentryConfig } = require("@sentry/nextjs");
-const i18n = require("./i18n.config.js");
const withBundleAnalyzer = require("@next/bundle-analyzer")({
enabled: process.env.ANALYZE === "true",
});
+/** @type {import('next').NextConfig} */
const nextConfig = {
- i18n: { ...i18n, localeDetection: false },
productionBrowserSourceMaps: true,
- output: "standalone",
transpilePackages: [
"@rallly/backend",
+ "@rallly/database",
"@rallly/icons",
"@rallly/ui",
"@rallly/tailwind-config",
@@ -22,12 +21,14 @@ const nextConfig = {
webpack(config) {
config.module.rules.push({
test: /\.svg$/,
- issuer: /\.[jt]sx?$/,
use: ["@svgr/webpack"],
});
return config;
},
+ eslint: {
+ ignoreDuringBuilds: true,
+ },
typescript: {
ignoreBuildErrors: true,
},
@@ -61,6 +62,7 @@ const sentryWebpackPluginOptions = {
// recommended:
// release, url, org, project, authToken, configFile, stripPrefix,
// urlPrefix, include, ignore
+ authToken: process.env.SENTRY_AUTH_TOKEN,
dryRun: !process.env.SENTRY_AUTH_TOKEN,
silent: true, // Suppresses all logs
// For all available options, see:
diff --git a/apps/web/package.json b/apps/web/package.json
index b7a75243a..a7d8711ee 100644
--- a/apps/web/package.json
+++ b/apps/web/package.json
@@ -3,8 +3,9 @@
"version": "0.0.0",
"private": true,
"scripts": {
- "dev": "cross-env TAILWIND_MODE=watch next dev",
+ "dev": "next dev",
"build": "next build",
+ "build:test": "dotenv -e .env.test -- next build",
"analyze": "cross-env ANALYZE=true next build",
"start": "next start",
"lint": "eslint .",
@@ -29,7 +30,6 @@
"@rallly/languages": "*",
"@rallly/tailwind-config": "*",
"@rallly/ui": "*",
- "@sentry/nextjs": "^7.74.1",
"@svgr/webpack": "^6.5.1",
"@tailwindcss/typography": "^0.5.9",
"@tanstack/react-query": "^4.0.0",
@@ -37,7 +37,6 @@
"@trpc/client": "^10.13.0",
"@trpc/next": "^10.13.0",
"@trpc/react-query": "^10.13.0",
- "@vercel/og": "^0.5.13",
"accept-language-parser": "^1.5.0",
"autoprefixer": "^10.4.13",
"class-variance-authority": "^0.6.0",
@@ -48,6 +47,7 @@
"dayjs": "^1.11.10",
"i18next": "^22.4.9",
"i18next-icu": "^2.3.0",
+ "i18next-resources-to-backend": "^1.1.4",
"ics": "^3.1.0",
"intl-messageformat": "^10.3.4",
"iron-session": "^6.3.1",
@@ -72,11 +72,12 @@
"smoothscroll-polyfill": "^0.4.4",
"spacetime": "^7.4.7",
"superjson": "^2.0.0",
- "timezone-soft": "^1.4.1"
+ "timezone-soft": "^1.5.1"
},
"devDependencies": {
- "@playwright/test": "^1.35.1",
+ "@playwright/test": "^1.39.0",
"@rallly/tsconfig": "*",
+ "@rallly/eslint-config": "*",
"@types/accept-language-parser": "^1.5.3",
"@types/color-hash": "^1.0.2",
"@types/lodash": "^4.14.178",
@@ -87,15 +88,8 @@
"@types/smoothscroll-polyfill": "^0.3.1",
"cheerio": "^1.0.0-rc.12",
"cross-env": "^7.0.3",
- "eslint-config-turbo": "^0.0.9",
- "eslint-import-resolver-typescript": "^2.7.0",
- "eslint-plugin-import": "^2.22.1",
- "eslint-plugin-react": "^7.23.2",
- "eslint-plugin-react-hooks": "^4.2.0",
- "eslint-plugin-simple-import-sort": "^7.0.0",
"i18next-scanner": "^4.2.0",
"i18next-scanner-typescript": "^1.1.1",
- "prettier-plugin-tailwindcss": "^0.1.8",
"smtp-tester": "^2.0.1",
"wait-on": "^6.0.1"
}
diff --git a/apps/web/playwright.config.ts b/apps/web/playwright.config.ts
index f480b3281..2389bbaa3 100644
--- a/apps/web/playwright.config.ts
+++ b/apps/web/playwright.config.ts
@@ -4,7 +4,7 @@ import path from "path";
const ci = process.env.CI === "true";
-dotenv.config({ path: path.resolve(__dirname, "../../", ".env.test") });
+dotenv.config({ path: path.resolve(__dirname, ".env.test") });
// Use process.env.PORT by default and fallback to port 3000
const PORT = process.env.PORT || 3000;
@@ -24,13 +24,9 @@ const config: PlaywrightTestConfig = {
trace: "retain-on-failure",
},
webServer: {
- command: `NODE_ENV=test yarn dev --port ${PORT}`,
+ command: `NODE_ENV=test yarn start --port ${PORT}`,
url: baseURL,
- timeout: 120 * 1000,
- reuseExistingServer: !ci,
- },
- expect: {
- timeout: 10000, // 10 seconds
+ reuseExistingServer: !process.env.CI,
},
reporter: [
[ci ? "github" : "list"],
diff --git a/apps/web/postcss.config.js b/apps/web/postcss.config.js
index f46b31c99..12a703d90 100644
--- a/apps/web/postcss.config.js
+++ b/apps/web/postcss.config.js
@@ -1,3 +1,6 @@
module.exports = {
- plugins: ["tailwindcss", "autoprefixer"],
+ plugins: {
+ tailwindcss: {},
+ autoprefixer: {},
+ },
};
diff --git a/apps/web/public/locales/en/app.json b/apps/web/public/locales/en/app.json
index d96ea55ba..f9153ac6f 100644
--- a/apps/web/public/locales/en/app.json
+++ b/apps/web/public/locales/en/app.json
@@ -225,5 +225,6 @@
"authErrorCta": "Go to login page",
"continueAs": "Continue as",
"finalizeFeature": "Finalize",
- "duplicateFeature": "Duplicate"
+ "duplicateFeature": "Duplicate",
+ "pageMovedDescription": "Redirecting to {newUrl}"
}
diff --git a/apps/web/src/app/[locale]/(admin)/layout.tsx b/apps/web/src/app/[locale]/(admin)/layout.tsx
new file mode 100644
index 000000000..dbe60c4ce
--- /dev/null
+++ b/apps/web/src/app/[locale]/(admin)/layout.tsx
@@ -0,0 +1,6 @@
+"use client";
+import { StandardLayout } from "@/components/layouts/standard-layout";
+
+export default function Layout({ children }: { children: React.ReactNode }) {
+ return {children};
+}
diff --git a/apps/web/src/app/[locale]/(admin)/new/page.tsx b/apps/web/src/app/[locale]/(admin)/new/page.tsx
new file mode 100644
index 000000000..9d80dd373
--- /dev/null
+++ b/apps/web/src/app/[locale]/(admin)/new/page.tsx
@@ -0,0 +1,17 @@
+import { getTranslation } from "@/app/i18n";
+import { CreatePoll } from "@/components/create-poll";
+
+export default function Page() {
+ return ;
+}
+
+export async function generateMetadata({
+ params,
+}: {
+ params: { locale: string };
+}) {
+ const { t } = await getTranslation(params.locale);
+ return {
+ title: t("newPoll"),
+ };
+}
diff --git a/apps/web/src/pages/poll/[urlId]/duplicate.tsx b/apps/web/src/app/[locale]/(admin)/poll/[urlId]/duplicate/page.tsx
similarity index 89%
rename from apps/web/src/pages/poll/[urlId]/duplicate.tsx
rename to apps/web/src/app/[locale]/(admin)/poll/[urlId]/duplicate/page.tsx
index fa6c7cde8..97f05078e 100644
--- a/apps/web/src/pages/poll/[urlId]/duplicate.tsx
+++ b/apps/web/src/app/[locale]/(admin)/poll/[urlId]/duplicate/page.tsx
@@ -1,3 +1,4 @@
+"use client";
import { zodResolver } from "@hookform/resolvers/zod";
import { Button } from "@rallly/ui/button";
import {
@@ -17,18 +18,16 @@ import {
} from "@rallly/ui/form";
import { Input } from "@rallly/ui/input";
import Link from "next/link";
-import { useRouter } from "next/router";
+import { useRouter } from "next/navigation";
import { useForm } from "react-hook-form";
import { z } from "zod";
-import { getPollLayout } from "@/components/layouts/poll-layout";
import { PayWall } from "@/components/pay-wall";
import { usePoll } from "@/components/poll-context";
import { Trans } from "@/components/trans";
import { NextPageWithLayout } from "@/types";
import { usePostHog } from "@/utils/posthog";
import { trpc } from "@/utils/trpc/client";
-import { getStaticTranslations } from "@/utils/with-page-translations";
const formSchema = z.object({
title: z.string().trim().min(1),
@@ -63,7 +62,7 @@ const Page: NextPageWithLayout = () => {
pollId: poll.id,
newPollId: res.id,
});
- await router.push(`/poll/${res.id}`);
+ router.push(`/poll/${res.id}`);
},
},
);
@@ -124,15 +123,4 @@ const Page: NextPageWithLayout = () => {
);
};
-Page.getLayout = getPollLayout;
-
-export const getStaticPaths = async () => {
- return {
- paths: [],
- fallback: "blocking",
- };
-};
-
-export const getStaticProps = getStaticTranslations;
-
export default Page;
diff --git a/apps/web/src/pages/poll/[urlId]/edit-details.tsx b/apps/web/src/app/[locale]/(admin)/poll/[urlId]/edit-details/page.tsx
similarity index 86%
rename from apps/web/src/pages/poll/[urlId]/edit-details.tsx
rename to apps/web/src/app/[locale]/(admin)/poll/[urlId]/edit-details/page.tsx
index cb117ec77..7e9efbfcc 100644
--- a/apps/web/src/pages/poll/[urlId]/edit-details.tsx
+++ b/apps/web/src/app/[locale]/(admin)/poll/[urlId]/edit-details/page.tsx
@@ -1,3 +1,4 @@
+"use client";
import { Button } from "@rallly/ui/button";
import {
Card,
@@ -9,19 +10,17 @@ import {
} from "@rallly/ui/card";
import { Form } from "@rallly/ui/form";
import Link from "next/link";
-import { useRouter } from "next/router";
+import { useRouter } from "next/navigation";
import { useForm } from "react-hook-form";
import {
PollDetailsData,
PollDetailsForm,
} from "@/components/forms/poll-details-form";
-import { getPollLayout } from "@/components/layouts/poll-layout";
import { useUpdatePollMutation } from "@/components/poll/mutations";
import { usePoll } from "@/components/poll-context";
import { Trans } from "@/components/trans";
import { NextPageWithLayout } from "@/types";
-import { getStaticTranslations } from "@/utils/with-page-translations";
const Page: NextPageWithLayout = () => {
const { poll } = usePoll();
@@ -87,15 +86,4 @@ const Page: NextPageWithLayout = () => {
);
};
-Page.getLayout = getPollLayout;
-
-export const getStaticPaths = async () => {
- return {
- paths: [],
- fallback: "blocking",
- };
-};
-
-export const getStaticProps = getStaticTranslations;
-
export default Page;
diff --git a/apps/web/src/pages/poll/[urlId]/edit-options.tsx b/apps/web/src/app/[locale]/(admin)/poll/[urlId]/edit-options/page.tsx
similarity index 88%
rename from apps/web/src/pages/poll/[urlId]/edit-options.tsx
rename to apps/web/src/app/[locale]/(admin)/poll/[urlId]/edit-options/page.tsx
index 6015e422b..fdde82681 100644
--- a/apps/web/src/pages/poll/[urlId]/edit-options.tsx
+++ b/apps/web/src/app/[locale]/(admin)/poll/[urlId]/edit-options/page.tsx
@@ -1,22 +1,20 @@
+"use client";
import { Button } from "@rallly/ui/button";
import { CardFooter } from "@rallly/ui/card";
import { Form } from "@rallly/ui/form";
import dayjs from "dayjs";
import Link from "next/link";
-import { useRouter } from "next/router";
+import { useRouter } from "next/navigation";
import { useTranslation } from "next-i18next";
import { useForm } from "react-hook-form";
import { PollOptionsData } from "@/components/forms";
import PollOptionsForm from "@/components/forms/poll-options-form";
-import { getPollLayout } from "@/components/layouts/poll-layout";
import { useModalContext } from "@/components/modal/modal-provider";
import { useUpdatePollMutation } from "@/components/poll/mutations";
import { usePoll } from "@/components/poll-context";
import { Trans } from "@/components/trans";
-import { NextPageWithLayout } from "@/types";
import { encodeDateOption } from "@/utils/date-time-utils";
-import { getStaticTranslations } from "@/utils/with-page-translations";
const convertOptionToString = (option: { start: Date; duration: number }) => {
const start = dayjs(option.start).utc();
@@ -27,7 +25,7 @@ const convertOptionToString = (option: { start: Date; duration: number }) => {
.format("YYYY-MM-DDTHH:mm:ss")}`;
};
-const Page: NextPageWithLayout = () => {
+const Page = () => {
const { poll, getParticipantsWhoVotedForOption } = usePoll();
const { mutate: updatePollMutation, isLoading: isUpdating } =
useUpdatePollMutation();
@@ -136,15 +134,4 @@ const Page: NextPageWithLayout = () => {
);
};
-Page.getLayout = getPollLayout;
-
-export const getStaticPaths = async () => {
- return {
- paths: [], //indicates that no page needs be created at build time
- fallback: "blocking", //indicates the type of fallback
- };
-};
-
-export const getStaticProps = getStaticTranslations;
-
export default Page;
diff --git a/apps/web/src/pages/poll/[urlId]/edit-settings.tsx b/apps/web/src/app/[locale]/(admin)/poll/[urlId]/edit-settings/page.tsx
similarity index 79%
rename from apps/web/src/pages/poll/[urlId]/edit-settings.tsx
rename to apps/web/src/app/[locale]/(admin)/poll/[urlId]/edit-settings/page.tsx
index a56388169..b6ef51852 100644
--- a/apps/web/src/pages/poll/[urlId]/edit-settings.tsx
+++ b/apps/web/src/app/[locale]/(admin)/poll/[urlId]/edit-settings/page.tsx
@@ -1,22 +1,20 @@
+"use client";
import { Button } from "@rallly/ui/button";
import { CardFooter } from "@rallly/ui/card";
import { Form } from "@rallly/ui/form";
import Link from "next/link";
-import { useRouter } from "next/router";
+import { useRouter } from "next/navigation";
import { useForm } from "react-hook-form";
import {
PollSettingsForm,
PollSettingsFormData,
} from "@/components/forms/poll-settings";
-import { getPollLayout } from "@/components/layouts/poll-layout";
import { useUpdatePollMutation } from "@/components/poll/mutations";
import { Trans } from "@/components/trans";
import { usePoll } from "@/contexts/poll";
-import { NextPageWithLayout } from "@/types";
-import { getStaticTranslations } from "@/utils/with-page-translations";
-const Page: NextPageWithLayout = () => {
+const Page = () => {
const poll = usePoll();
const router = useRouter();
@@ -69,15 +67,4 @@ const Page: NextPageWithLayout = () => {
);
};
-Page.getLayout = getPollLayout;
-
-export const getStaticPaths = async () => {
- return {
- paths: [],
- fallback: "blocking",
- };
-};
-
-export const getStaticProps = getStaticTranslations;
-
export default Page;
diff --git a/apps/web/src/pages/poll/[urlId]/finalize.tsx b/apps/web/src/app/[locale]/(admin)/poll/[urlId]/finalize/page.tsx
similarity index 86%
rename from apps/web/src/pages/poll/[urlId]/finalize.tsx
rename to apps/web/src/app/[locale]/(admin)/poll/[urlId]/finalize/page.tsx
index 949d29631..3ffecaf78 100644
--- a/apps/web/src/pages/poll/[urlId]/finalize.tsx
+++ b/apps/web/src/app/[locale]/(admin)/poll/[urlId]/finalize/page.tsx
@@ -1,3 +1,4 @@
+"use client";
import { Button } from "@rallly/ui/button";
import {
CardContent,
@@ -6,10 +7,9 @@ import {
CardHeader,
CardTitle,
} from "@rallly/ui/card";
-import { useRouter } from "next/router";
+import { useRouter } from "next/navigation";
import { Card } from "@/components/card";
-import { getPollLayout } from "@/components/layouts/poll-layout";
import { PayWall } from "@/components/pay-wall";
import { FinalizePollForm } from "@/components/poll/manage-poll/finalize-poll-dialog";
import { Trans } from "@/components/trans";
@@ -18,7 +18,6 @@ import { usePoll } from "@/contexts/poll";
import { NextPageWithLayout } from "@/types";
import { usePostHog } from "@/utils/posthog";
import { trpc } from "@/utils/trpc/client";
-import { getStaticTranslations } from "@/utils/with-page-translations";
const FinalizationForm = () => {
const plan = usePlan();
@@ -94,15 +93,4 @@ const Page: NextPageWithLayout = () => {
);
};
-Page.getLayout = getPollLayout;
-
-export const getStaticPaths = async () => {
- return {
- paths: [],
- fallback: "blocking",
- };
-};
-
-export const getStaticProps = getStaticTranslations;
-
export default Page;
diff --git a/apps/web/src/app/[locale]/(admin)/poll/[urlId]/layout.tsx b/apps/web/src/app/[locale]/(admin)/poll/[urlId]/layout.tsx
new file mode 100644
index 000000000..91a446536
--- /dev/null
+++ b/apps/web/src/app/[locale]/(admin)/poll/[urlId]/layout.tsx
@@ -0,0 +1,31 @@
+import { prisma } from "@rallly/database";
+import { notFound } from "next/navigation";
+
+import { PollLayout } from "@/components/layouts/poll-layout";
+
+export default async function Layout({
+ children,
+ params,
+}: React.PropsWithChildren<{ params: { urlId: string } }>) {
+ const poll = await prisma.poll.findUnique({ where: { id: params.urlId } });
+ if (!poll) {
+ notFound();
+ }
+
+ return {children};
+}
+
+export async function generateMetadata({
+ params,
+}: {
+ params: { locale: string; urlId: string };
+}) {
+ const poll = await prisma.poll.findUnique({ where: { id: params.urlId } });
+
+ if (!poll) {
+ return notFound();
+ }
+ return {
+ title: poll.title,
+ };
+}
diff --git a/apps/web/src/pages/poll/[urlId].tsx b/apps/web/src/app/[locale]/(admin)/poll/[urlId]/page.tsx
similarity index 72%
rename from apps/web/src/pages/poll/[urlId].tsx
rename to apps/web/src/app/[locale]/(admin)/poll/[urlId]/page.tsx
index 313f9d8b2..3159f05fa 100644
--- a/apps/web/src/pages/poll/[urlId].tsx
+++ b/apps/web/src/app/[locale]/(admin)/poll/[urlId]/page.tsx
@@ -1,17 +1,14 @@
-import { InfoIcon } from "@rallly/icons";
+"use client";
import { cn } from "@rallly/ui";
import { Alert, AlertDescription, AlertTitle } from "@rallly/ui/alert";
+import { InfoIcon } from "lucide-react";
import { Trans } from "next-i18next";
-import { getPollLayout } from "@/components/layouts/poll-layout";
import { LoginLink } from "@/components/login-link";
import { Poll } from "@/components/poll";
import { RegisterLink } from "@/components/register-link";
import { useUser } from "@/components/user-provider";
import { usePoll } from "@/contexts/poll";
-import { NextPageWithLayout } from "@/types";
-import { isSelfHosted } from "@/utils/constants";
-import { getStaticTranslations } from "@/utils/with-page-translations";
const GuestPollAlert = () => {
const poll = usePoll();
@@ -49,25 +46,11 @@ const GuestPollAlert = () => {
);
};
-const Page: NextPageWithLayout = () => {
+export default function Page() {
return (
);
-};
-
-Page.getLayout = getPollLayout;
-Page.isAuthRequired = isSelfHosted;
-
-export const getStaticPaths = async () => {
- return {
- paths: [],
- fallback: "blocking",
- };
-};
-
-export const getStaticProps = getStaticTranslations;
-
-export default Page;
+}
diff --git a/apps/web/src/app/[locale]/(admin)/polls/page.tsx b/apps/web/src/app/[locale]/(admin)/polls/page.tsx
new file mode 100644
index 000000000..d6257a7fd
--- /dev/null
+++ b/apps/web/src/app/[locale]/(admin)/polls/page.tsx
@@ -0,0 +1,18 @@
+import { getTranslation } from "@/app/i18n";
+
+import { PollsPage } from "./polls-page";
+
+export default function Page() {
+ return ;
+}
+
+export async function generateMetadata({
+ params,
+}: {
+ params: { locale: string };
+}) {
+ const { t } = await getTranslation(params.locale);
+ return {
+ title: t("polls"),
+ };
+}
diff --git a/apps/web/src/pages/polls.tsx b/apps/web/src/app/[locale]/(admin)/polls/polls-page.tsx
similarity index 91%
rename from apps/web/src/pages/polls.tsx
rename to apps/web/src/app/[locale]/(admin)/polls/polls-page.tsx
index da92b3611..8e5d2a2b2 100644
--- a/apps/web/src/pages/polls.tsx
+++ b/apps/web/src/app/[locale]/(admin)/polls/polls-page.tsx
@@ -1,19 +1,17 @@
+"use client";
+import { Button } from "@rallly/ui/button";
+import dayjs from "dayjs";
import {
InboxIcon,
PauseCircleIcon,
PlusIcon,
RadioIcon,
VoteIcon,
-} from "@rallly/icons";
-import { Button } from "@rallly/ui/button";
-import dayjs from "dayjs";
-import Head from "next/head";
+} from "lucide-react";
import Link from "next/link";
-import { useTranslation } from "next-i18next";
import { Container } from "@/components/container";
import { DateIcon } from "@/components/date-icon";
-import { getStandardLayout } from "@/components/layouts/standard-layout";
import {
TopBar,
TopBarTitle,
@@ -22,11 +20,8 @@ import { ParticipantAvatarBar } from "@/components/participant-avatar-bar";
import { PollStatusBadge } from "@/components/poll-status";
import { Skeleton } from "@/components/skeleton";
import { Trans } from "@/components/trans";
-import { NextPageWithLayout } from "@/types";
-import { isSelfHosted } from "@/utils/constants";
import { useDayjs } from "@/utils/dayjs";
import { trpc } from "@/utils/trpc/client";
-import { getStaticTranslations } from "@/utils/with-page-translations";
const EmptyState = () => {
return (
@@ -57,16 +52,12 @@ const EmptyState = () => {
);
};
-const Page: NextPageWithLayout = () => {
+export function PollsPage() {
const { data } = trpc.polls.list.useQuery();
- const { t } = useTranslation();
const { adjustTimeZone } = useDayjs();
return (
-
-
{t("polls")}
-
} icon={VoteIcon} />
@@ -185,11 +176,4 @@ const Page: NextPageWithLayout = () => {
);
-};
-
-Page.getLayout = getStandardLayout;
-Page.isAuthRequired = isSelfHosted;
-
-export default Page;
-
-export const getStaticProps = getStaticTranslations;
+}
diff --git a/apps/web/src/pages/settings/billing.tsx b/apps/web/src/app/[locale]/(admin)/settings/billing/billing-page.tsx
similarity index 69%
rename from apps/web/src/pages/settings/billing.tsx
rename to apps/web/src/app/[locale]/(admin)/settings/billing/billing-page.tsx
index 36095037f..1a89a24cc 100644
--- a/apps/web/src/pages/settings/billing.tsx
+++ b/apps/web/src/app/[locale]/(admin)/settings/billing/billing-page.tsx
@@ -1,16 +1,15 @@
-import { ArrowUpRight, CreditCardIcon, SendIcon } from "@rallly/icons";
+"use client";
import { Button } from "@rallly/ui/button";
import { Card } from "@rallly/ui/card";
import { Label } from "@rallly/ui/label";
import dayjs from "dayjs";
-import { GetStaticProps } from "next";
+import { ArrowUpRight, CreditCardIcon, SendIcon } from "lucide-react";
import Head from "next/head";
import Link from "next/link";
import Script from "next/script";
import { useTranslation } from "next-i18next";
import { BillingPlans } from "@/components/billing/billing-plans";
-import { getProfileLayout } from "@/components/layouts/profile-layout";
import {
Settings,
SettingsContent,
@@ -19,12 +18,8 @@ import {
} from "@/components/settings/settings";
import { Trans } from "@/components/trans";
import { useSubscription } from "@/contexts/plan";
-import { isSelfHosted } from "@/utils/constants";
import { trpc } from "@/utils/trpc/client";
-import { NextPageWithLayout } from "../../types";
-import { getStaticTranslations } from "../../utils/with-page-translations";
-
declare global {
interface Window {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -61,11 +56,7 @@ const BillingPortal = () => {
);
};
-export const proPlanIdMonthly = process.env
- .NEXT_PUBLIC_PRO_PLAN_ID_MONTHLY as string;
-
-export const proPlanIdYearly = process.env
- .NEXT_PUBLIC_PRO_PLAN_ID_YEARLY as string;
+const proPlanIdMonthly = process.env.NEXT_PUBLIC_PRO_PLAN_ID_MONTHLY as string;
const SubscriptionStatus = () => {
const data = useSubscription();
@@ -106,7 +97,7 @@ const LegacyBilling = () => {
}
return (
-
+ <>
{process.env.NEXT_PUBLIC_PADDLE_VENDOR_ID ? (