diff --git a/apps/landing/README.md b/apps/landing/README.md new file mode 100644 index 00000000..c4033664 --- /dev/null +++ b/apps/landing/README.md @@ -0,0 +1,36 @@ +This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). + +## Getting Started + +First, run the development server: + +```bash +npm run dev +# or +yarn dev +# or +pnpm dev +# or +bun dev +``` + +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. + +You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. + +This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. + +## Learn More + +To learn more about Next.js, take a look at the following resources: + +- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. +- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. + +You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! + +## Deploy on Vercel + +The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. + +Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. diff --git a/apps/landing/app/favicon.ico b/apps/landing/app/favicon.ico new file mode 100644 index 00000000..750e3c04 Binary files /dev/null and b/apps/landing/app/favicon.ico differ diff --git a/apps/landing/app/globals.css b/apps/landing/app/globals.css new file mode 100644 index 00000000..8abdb15c --- /dev/null +++ b/apps/landing/app/globals.css @@ -0,0 +1,76 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer base { + :root { + --background: 0 0% 100%; + --foreground: 222.2 84% 4.9%; + + --card: 0 0% 100%; + --card-foreground: 222.2 84% 4.9%; + + --popover: 0 0% 100%; + --popover-foreground: 222.2 84% 4.9%; + + --primary: 222.2 47.4% 11.2%; + --primary-foreground: 210 40% 98%; + + --secondary: 210 40% 96.1%; + --secondary-foreground: 222.2 47.4% 11.2%; + + --muted: 210 40% 96.1%; + --muted-foreground: 215.4 16.3% 46.9%; + + --accent: 210 40% 96.1%; + --accent-foreground: 222.2 47.4% 11.2%; + + --destructive: 0 84.2% 60.2%; + --destructive-foreground: 210 40% 98%; + + --border: 214.3 31.8% 91.4%; + --input: 214.3 31.8% 91.4%; + --ring: 222.2 84% 4.9%; + + --radius: 0.5rem; + } + + .dark { + --background: 222.2 84% 4.9%; + --foreground: 210 40% 98%; + + --card: 222.2 84% 4.9%; + --card-foreground: 210 40% 98%; + + --popover: 222.2 84% 4.9%; + --popover-foreground: 210 40% 98%; + + --primary: 210 40% 98%; + --primary-foreground: 222.2 47.4% 11.2%; + + --secondary: 217.2 32.6% 17.5%; + --secondary-foreground: 210 40% 98%; + + --muted: 217.2 32.6% 17.5%; + --muted-foreground: 215 20.2% 65.1%; + + --accent: 217.2 32.6% 17.5%; + --accent-foreground: 210 40% 98%; + + --destructive: 0 62.8% 30.6%; + --destructive-foreground: 210 40% 98%; + + --border: 217.2 32.6% 17.5%; + --input: 217.2 32.6% 17.5%; + --ring: 212.7 26.8% 83.9%; + } +} + +@layer base { + * { + @apply border-border; + } + body { + @apply bg-background text-foreground; + } +} diff --git a/apps/landing/app/layout.tsx b/apps/landing/app/layout.tsx new file mode 100644 index 00000000..331c8b11 --- /dev/null +++ b/apps/landing/app/layout.tsx @@ -0,0 +1,25 @@ +import type { Metadata } from "next"; +import { Inter } from "next/font/google"; + +import "./globals.css"; + +import React from "react"; + +const inter = Inter({ subsets: ["latin"] }); + +export const metadata: Metadata = { + title: "Hoarder", + description: "Your AI powered second brain", +}; + +export default async function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return ( + + {children} + + ); +} diff --git a/apps/web/components/landing/LandingPage.tsx b/apps/landing/app/page.tsx similarity index 95% rename from apps/web/components/landing/LandingPage.tsx rename to apps/landing/app/page.tsx index cf8e8abd..d87962bb 100644 --- a/apps/web/components/landing/LandingPage.tsx +++ b/apps/landing/app/page.tsx @@ -1,11 +1,10 @@ import Image from "next/image"; import Link from "next/link"; +import { Button, buttonVariants } from "@/components/ui/button"; import { cn } from "@/lib/utils"; -import screenshot from "@/public/landing/screenshot.png"; +import screenshot from "@/public/screenshot.png"; import { ExternalLink, Github, PackageOpen } from "lucide-react"; -import { Button, buttonVariants } from "../ui/button"; - const GITHUB_LINK = "https://github.com/MohamedBassem/hoarder-app"; function NavBar() { diff --git a/apps/landing/components.json b/apps/landing/components.json new file mode 100644 index 00000000..fa674c93 --- /dev/null +++ b/apps/landing/components.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "default", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "tailwind.config.ts", + "css": "app/globals.css", + "baseColor": "slate", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils" + } +} diff --git a/apps/landing/components/ui/button.tsx b/apps/landing/components/ui/button.tsx new file mode 100644 index 00000000..5ed8df91 --- /dev/null +++ b/apps/landing/components/ui/button.tsx @@ -0,0 +1,56 @@ +import type { VariantProps } from "class-variance-authority"; +import * as React from "react"; +import { cn } from "@/lib/utils"; +import { Slot } from "@radix-ui/react-slot"; +import { cva } from "class-variance-authority"; + +const buttonVariants = cva( + "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", + { + variants: { + variant: { + default: "bg-primary text-primary-foreground hover:bg-primary/90", + destructive: + "bg-destructive text-destructive-foreground hover:bg-destructive/90", + outline: + "border border-input bg-background hover:bg-accent hover:text-accent-foreground", + secondary: + "bg-secondary text-secondary-foreground hover:bg-secondary/80", + ghost: "hover:bg-accent hover:text-accent-foreground", + link: "text-primary underline-offset-4 hover:underline", + }, + size: { + default: "h-10 px-4 py-2", + sm: "h-9 rounded-md px-3", + lg: "h-11 rounded-md px-8", + icon: "size-10", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + }, +); + +export interface ButtonProps + extends React.ButtonHTMLAttributes, + VariantProps { + asChild?: boolean; +} + +const Button = React.forwardRef( + ({ className, variant, size, asChild = false, ...props }, ref) => { + const Comp = asChild ? Slot : "button"; + return ( + + ); + }, +); +Button.displayName = "Button"; + +export { Button, buttonVariants }; diff --git a/apps/landing/lib/utils.ts b/apps/landing/lib/utils.ts new file mode 100644 index 00000000..88283f01 --- /dev/null +++ b/apps/landing/lib/utils.ts @@ -0,0 +1,7 @@ +import type { ClassValue } from "clsx"; +import { clsx } from "clsx"; +import { twMerge } from "tailwind-merge"; + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); +} diff --git a/apps/landing/next-env.d.ts b/apps/landing/next-env.d.ts new file mode 100644 index 00000000..4f11a03d --- /dev/null +++ b/apps/landing/next-env.d.ts @@ -0,0 +1,5 @@ +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/apps/landing/next.config.mjs b/apps/landing/next.config.mjs new file mode 100644 index 00000000..743e4d69 --- /dev/null +++ b/apps/landing/next.config.mjs @@ -0,0 +1,8 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = { + /** We already do linting and typechecking as separate tasks in CI */ + eslint: { ignoreDuringBuilds: true }, + typescript: { ignoreBuildErrors: true }, +}; + +export default nextConfig; diff --git a/apps/landing/package.json b/apps/landing/package.json new file mode 100644 index 00000000..565b7a23 --- /dev/null +++ b/apps/landing/package.json @@ -0,0 +1,49 @@ +{ + "$schema": "https://json.schemastore.org/package.json", + "name": "@hoarder/landing", + "version": "0.1.0", + "private": true, + "type": "module", + "scripts": { + "dev": "next dev", + "clean": "git clean -xdf .next .turbo node_modules", + "build": "next build", + "start": "next start", + "lint": "next lint", + "typecheck": "tsc --noEmit", + "format": "prettier --check . --ignore-path ../../.gitignore" + }, + "dependencies": { + "@radix-ui/react-slot": "^1.0.2", + "class-variance-authority": "^0.7.0", + "clsx": "^2.1.0", + "lucide-react": "^0.330.0", + "next": "14.1.1", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-select": "^5.8.0", + "tailwind-merge": "^2.2.1", + "tailwindcss-animate": "^1.0.7" + }, + "devDependencies": { + "@hoarder/eslint-config": "workspace:^0.2.0", + "@hoarder/prettier-config": "workspace:^0.1.0", + "@hoarder/tailwind-config": "workspace:^0.1.0", + "@hoarder/tsconfig": "workspace:^0.1.0", + "@tailwindcss/typography": "^0.5.10", + "@types/react": "^18.2.55", + "@types/react-dom": "^18.2.19", + "autoprefixer": "^10.4.17", + "postcss": "^8.4.35", + "tailwindcss": "^3.4.1" + }, + "eslintConfig": { + "root": true, + "extends": [ + "@hoarder/eslint-config/base", + "@hoarder/eslint-config/nextjs", + "@hoarder/eslint-config/react" + ] + }, + "prettier": "@hoarder/prettier-config" +} diff --git a/apps/landing/postcss.config.cjs b/apps/landing/postcss.config.cjs new file mode 100644 index 00000000..12a703d9 --- /dev/null +++ b/apps/landing/postcss.config.cjs @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/apps/landing/public/app-store-badge.png b/apps/landing/public/app-store-badge.png new file mode 100644 index 00000000..059e2133 Binary files /dev/null and b/apps/landing/public/app-store-badge.png differ diff --git a/apps/landing/public/extension-badge.png b/apps/landing/public/extension-badge.png new file mode 100644 index 00000000..224c12bf Binary files /dev/null and b/apps/landing/public/extension-badge.png differ diff --git a/apps/landing/public/screenshot.png b/apps/landing/public/screenshot.png new file mode 100644 index 00000000..d4403659 Binary files /dev/null and b/apps/landing/public/screenshot.png differ diff --git a/apps/landing/tailwind.config.ts b/apps/landing/tailwind.config.ts new file mode 100644 index 00000000..521ba51c --- /dev/null +++ b/apps/landing/tailwind.config.ts @@ -0,0 +1,89 @@ +import type { Config } from "tailwindcss"; + +const config = { + darkMode: ["class"], + content: [ + "./pages/**/*.{ts,tsx}", + "./components/**/*.{ts,tsx}", + "./app/**/*.{ts,tsx}", + "./src/**/*.{ts,tsx}", + ], + prefix: "", + theme: { + container: { + center: true, + padding: "2rem", + screens: { + "2xl": "1400px", + }, + }, + extend: { + colors: { + border: "hsl(var(--border))", + input: "hsl(var(--input))", + ring: "hsl(var(--ring))", + background: "hsl(var(--background))", + foreground: "hsl(var(--foreground))", + primary: { + DEFAULT: "hsl(var(--primary))", + foreground: "hsl(var(--primary-foreground))", + }, + secondary: { + DEFAULT: "hsl(var(--secondary))", + foreground: "hsl(var(--secondary-foreground))", + }, + destructive: { + DEFAULT: "hsl(var(--destructive))", + foreground: "hsl(var(--destructive-foreground))", + }, + muted: { + DEFAULT: "hsl(var(--muted))", + foreground: "hsl(var(--muted-foreground))", + }, + accent: { + DEFAULT: "hsl(var(--accent))", + foreground: "hsl(var(--accent-foreground))", + }, + popover: { + DEFAULT: "hsl(var(--popover))", + foreground: "hsl(var(--popover-foreground))", + }, + card: { + DEFAULT: "hsl(var(--card))", + foreground: "hsl(var(--card-foreground))", + }, + }, + borderRadius: { + lg: "var(--radius)", + md: "calc(var(--radius) - 2px)", + sm: "calc(var(--radius) - 4px)", + }, + keyframes: { + "accordion-down": { + from: { height: "0" }, + to: { height: "var(--radix-accordion-content-height)" }, + }, + "accordion-up": { + from: { height: "var(--radix-accordion-content-height)" }, + to: { height: "0" }, + }, + "pulse-border": { + "0%, 100%": { + "box-shadow": "0 0 0 0 gray", + }, + "50%": { + "box-shadow": "0 0 0 2px gray", + }, + }, + }, + animation: { + "accordion-down": "accordion-down 0.2s ease-out", + "accordion-up": "accordion-up 0.2s ease-out", + "pulse-border": "pulse-border 1s ease-in-out infinite", + }, + }, + }, + plugins: [require("tailwindcss-animate"), require("@tailwindcss/typography")], +} satisfies Config; + +export default config; diff --git a/apps/landing/tsconfig.json b/apps/landing/tsconfig.json new file mode 100644 index 00000000..db90cf17 --- /dev/null +++ b/apps/landing/tsconfig.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@hoarder/tsconfig/base.json", + "compilerOptions": { + "baseUrl": ".", + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./*"] + } + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "exclude": ["node_modules"] +} diff --git a/apps/web/app/page.tsx b/apps/web/app/page.tsx index d86c91be..c5d57e8a 100644 --- a/apps/web/app/page.tsx +++ b/apps/web/app/page.tsx @@ -1,13 +1,11 @@ import { redirect } from "next/navigation"; -import LandingPage from "@/components/landing/LandingPage"; import { getServerAuthSession } from "@/server/auth"; export default async function Home() { - // TODO: Home currently just redirects between pages until we build a proper landing page const session = await getServerAuthSession(); if (session) { redirect("/dashboard/bookmarks"); + } else { + redirect("/signin"); } - - return ; } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 024ca436..97aabd3b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -135,6 +135,70 @@ importers: specifier: ^5.1.0 version: 5.1.4(@types/node@20.11.20) + apps/landing: + dependencies: + '@radix-ui/react-slot': + specifier: ^1.0.2 + version: 1.0.2(@types/react@18.2.58)(react@18.2.0) + class-variance-authority: + specifier: ^0.7.0 + version: 0.7.0 + clsx: + specifier: ^2.1.0 + version: 2.1.0 + lucide-react: + specifier: ^0.330.0 + version: 0.330.0(react@18.2.0) + next: + specifier: 14.1.1 + version: 14.1.1(@babel/core@7.24.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + react: + specifier: ^18.2.0 + version: 18.2.0 + react-dom: + specifier: ^18.2.0 + version: 18.2.0(react@18.2.0) + react-select: + specifier: ^5.8.0 + version: 5.8.0(@types/react@18.2.58)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + tailwind-merge: + specifier: ^2.2.1 + version: 2.2.1 + tailwindcss-animate: + specifier: ^1.0.7 + version: 1.0.7(tailwindcss@3.4.1) + devDependencies: + '@hoarder/eslint-config': + specifier: workspace:^0.2.0 + version: link:../../tooling/eslint + '@hoarder/prettier-config': + specifier: workspace:^0.1.0 + version: link:../../tooling/prettier + '@hoarder/tailwind-config': + specifier: workspace:^0.1.0 + version: link:../../tooling/tailwind + '@hoarder/tsconfig': + specifier: workspace:^0.1.0 + version: link:../../tooling/typescript + '@tailwindcss/typography': + specifier: ^0.5.10 + version: 0.5.10(tailwindcss@3.4.1) + '@types/react': + specifier: ^18.2.55 + version: 18.2.58 + '@types/react-dom': + specifier: ^18.2.19 + version: 18.2.19 + autoprefixer: + specifier: ^10.4.17 + version: 10.4.17(postcss@8.4.35) + postcss: + specifier: ^8.4.35 + version: 8.4.35 + tailwindcss: + specifier: ^3.4.1 + version: 3.4.1 + apps/mobile: dependencies: '@hoarder/trpc': @@ -432,10 +496,10 @@ importers: version: 3.4.1 vite-tsconfig-paths: specifier: ^4.3.1 - version: 4.3.1 + version: 4.3.1(typescript@5.3.3) vitest: specifier: ^1.3.1 - version: 1.3.1 + version: 1.3.1(@types/node@20.11.20) apps/workers: dependencies: @@ -26588,9 +26652,6 @@ snapshots: ts-interface-checker@0.1.13: {} - tsconfck@3.0.2: - dev: true - tsconfck@3.0.2(typescript@5.3.3): dependencies: typescript: 5.3.3 @@ -27033,24 +27094,6 @@ snapshots: video-extensions@1.2.0: dev: false - vite-node@1.3.1: - dependencies: - cac: 6.7.14 - debug: 4.3.4 - pathe: 1.1.2 - picocolors: 1.0.0 - vite: 5.1.4 - transitivePeerDependencies: - - '@types/node' - - less - - lightningcss - - sass - - stylus - - sugarss - - supports-color - - terser - dev: true - vite-node@1.3.1(@types/node@20.11.20): dependencies: cac: 6.7.14 @@ -27069,16 +27112,6 @@ snapshots: - terser dev: true - vite-tsconfig-paths@4.3.1: - dependencies: - debug: 4.3.4 - globrex: 0.1.2 - tsconfck: 3.0.2 - transitivePeerDependencies: - - supports-color - - typescript - dev: true - vite-tsconfig-paths@4.3.1(typescript@5.3.3): dependencies: debug: 4.3.4 @@ -27089,15 +27122,6 @@ snapshots: - typescript dev: true - vite@5.1.4: - dependencies: - esbuild: 0.19.12 - postcss: 8.4.35 - rollup: 4.12.0 - optionalDependencies: - fsevents: 2.3.3 - dev: true - vite@5.1.4(@types/node@20.11.20): dependencies: '@types/node': 20.11.20 @@ -27108,38 +27132,6 @@ snapshots: fsevents: 2.3.3 dev: true - vitest@1.3.1: - dependencies: - '@vitest/expect': 1.3.1 - '@vitest/runner': 1.3.1 - '@vitest/snapshot': 1.3.1 - '@vitest/spy': 1.3.1 - '@vitest/utils': 1.3.1 - acorn-walk: 8.3.2 - chai: 4.4.1 - debug: 4.3.4 - execa: 8.0.1 - local-pkg: 0.5.0 - magic-string: 0.30.7 - pathe: 1.1.2 - picocolors: 1.0.0 - std-env: 3.7.0 - strip-literal: 2.0.0 - tinybench: 2.6.0 - tinypool: 0.8.2 - vite: 5.1.4 - vite-node: 1.3.1 - why-is-node-running: 2.2.2 - transitivePeerDependencies: - - less - - lightningcss - - sass - - stylus - - sugarss - - supports-color - - terser - dev: true - vitest@1.3.1(@types/node@20.11.20): dependencies: '@types/node': 20.11.20