Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// This configuration only applies to the package manager root.
/** @type {import("eslint").Linter.Config} */
module.exports = {
ignorePatterns: ["apps/**", "packages/**"],
extends: ["@workspace/eslint-config/library.js"],
parser: "@typescript-eslint/parser",
parserOptions: {
project: true,
},
}
97 changes: 97 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # Needed for turbo affected detection

- uses: pnpm/action-setup@v4

- uses: actions/setup-node@v4
with:
node-version: 24
cache: "pnpm"

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Lint
run: pnpm turbo lint

test-unit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- uses: pnpm/action-setup@v4

- uses: actions/setup-node@v4
with:
node-version: 24
cache: "pnpm"

- name: Install
run: pnpm install --frozen-lockfile

- name: Unit Tests
run: pnpm turbo test --filter='...[origin/${{ github.base_ref || github.ref_name }}]' # affected + dependents

build:
runs-on: ubuntu-latest
needs: [lint, test-unit] # Only build if lint & unit pass
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- uses: pnpm/action-setup@v4

- uses: actions/setup-node@v4
with:
node-version: 24
cache: "pnpm"

- name: Install
run: pnpm install --frozen-lockfile

- name: Build
run: pnpm turbo build --filter='...[origin/${{ github.base_ref || github.ref_name }}]'

- name: Upload build artifacts (optional for later jobs)
uses: actions/upload-artifact@v4
with:
name: next-build
path: |
apps/web/.next/**
apps/web/public/**
retention-days: 1

test-e2e:
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 24
cache: "pnpm"
- run: pnpm install --frozen-lockfile
- run: pnpm --filter @linglix/e2e playwright install --with-deps
- name: Start dev server & run E2E
run: pnpm turbo test:e2e --filter=@linglix/e2e
36 changes: 36 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# Dependencies
node_modules
.pnp
.pnp.js

# Local env files
.env
.env.local
.env.development.local
.env.test.local
.env.production.local

# Testing
coverage

# Turbo
.turbo

# Vercel
.vercel

# Build Outputs
.next/
out/
build
dist


# Debug
npm-debug.log*

# Misc
.DS_Store
*.pem
1 change: 1 addition & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pnpm lint-staged
Empty file added .npmrc
Empty file.
8 changes: 8 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
node_modules
.next
out
build
dist
coverage
.turbo
.vercel
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"tailwindCSS.experimental.configFile": "packages/ui/src/styles/globals.css"
}
32 changes: 31 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,31 @@
# linglix
# shadcn/ui monorepo template

This template is for creating a monorepo with shadcn/ui.

## Usage

```bash
pnpm dlx shadcn@latest init
```

## Adding components

To add components to your app, run the following command at the root of your `web` app:

```bash
pnpm dlx shadcn@latest add button -c apps/web
```

This will place the ui components in the `packages/ui/src/components` directory.

## Tailwind

Your `tailwind.config.ts` and `globals.css` are already set up to use the components from the `ui` package.

## Using components

To use the components in your app, import them from the `ui` package.

```tsx
import { Button } from "@workspace/ui/components/button"
```
Binary file added apps/web/app/favicon.ico
Binary file not shown.
27 changes: 27 additions & 0 deletions apps/web/app/global-error.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"use client";

import * as Sentry from "@sentry/nextjs";
import NextError from "next/error";
import { useEffect } from "react";

export default function GlobalError({
error,
}: {
error: Error & { digest?: string };
}) {
useEffect(() => {
Sentry.captureException(error);
}, [error]);

return (
<html lang="en">
<body>
{/* `NextError` is the default Next.js error page component. Its type
definition requires a `statusCode` prop. However, since the App Router
does not expose status codes for errors, we simply pass 0 to render a
generic error message. */}
<NextError statusCode={0} />
</body>
</html>
);
}
16 changes: 16 additions & 0 deletions apps/web/app/global-not-found.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export default function GlobalNotFound() {
return (
<html lang="en">
<body>
<main className="flex min-h-svh items-center justify-center">
<div className="flex flex-col items-center gap-2 text-center">
<h1 className="text-2xl font-bold">Not Found</h1>
<p className="text-sm text-muted-foreground">
The page you are looking for does not exist.
</p>
</div>
</main>
</body>
</html>
)
}
34 changes: 34 additions & 0 deletions apps/web/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Geist, Geist_Mono } from "next/font/google"

import "@workspace/ui/globals.css"
import { Providers } from "@/components/providers"

const fontSans = Geist({
subsets: ["latin"],
variable: "--font-sans",
})

const fontMono = Geist_Mono({
subsets: ["latin"],
variable: "--font-mono",
})

export const metadata = {
title: "Linglix",
}

export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode
}>) {
return (
<html lang="en" suppressHydrationWarning>
<body
className={`${fontSans.variable} ${fontMono.variable} font-sans antialiased `}
>
<Providers>{children}</Providers>
</body>
</html>
)
}
15 changes: 15 additions & 0 deletions apps/web/app/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Button } from "@workspace/ui/components/button"

export default function Page() {
return (
<div className="flex items-center justify-center min-h-svh">
<div className="flex flex-col items-center justify-center gap-4">
<h1 className="text-2xl font-bold">Linglix</h1>
<div className="flex gap-2">
<Button>Button</Button>
<Button variant="outline">Outline</Button>
</div>
</div>
</div>
)
}
20 changes: 20 additions & 0 deletions apps/web/components.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "radix-nova",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "",
"css": "../../packages/ui/src/styles/globals.css",
"baseColor": "neutral",
"cssVariables": true
},
"iconLibrary": "lucide",
"aliases": {
"components": "@/components",
"hooks": "@/hooks",
"lib": "@/lib",
"utils": "@workspace/ui/lib/utils",
"ui": "@workspace/ui/components"
}
}
Empty file added apps/web/components/.gitkeep
Empty file.
30 changes: 30 additions & 0 deletions apps/web/components/providers.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
"use client";

// Themes
import * as React from "react";
import { ThemeProvider as NextThemesProvider } from "next-themes";

// Posthog
import posthog from "posthog-js";
import { PostHogProvider } from "posthog-js/react";
import { useEffect } from "react";

export function Providers({ children }: { children: React.ReactNode }) {
useEffect(() => {
// init already done in instrumentation, but ensure loaded
}, []);

return (
<PostHogProvider client={posthog}>
<NextThemesProvider
attribute="class"
defaultTheme="system"
enableSystem
disableTransitionOnChange
enableColorScheme
>
{children}
</NextThemesProvider>
</PostHogProvider>
);
}
15 changes: 15 additions & 0 deletions apps/web/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { nextJsConfig } from "@workspace/eslint-config/next-js"

export default [
{
ignores: [".next/**", "node_modules/**", "dist/**"],
},
...nextJsConfig,
{
languageOptions: {
globals: {
process: "readonly",
},
},
},
]
Empty file added apps/web/hooks/.gitkeep
Empty file.
24 changes: 24 additions & 0 deletions apps/web/instrumentation-client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import posthog from 'posthog-js';
import * as Sentry from "@sentry/nextjs";

const isProd = process.env.NODE_ENV === 'production';
const posthogKey = process.env.NEXT_PUBLIC_POSTHOG_KEY;

if (posthogKey) {
posthog.init(posthogKey, {
api_host: process.env.NEXT_PUBLIC_POSTHOG_HOST,
defaults: '2026-01-30'
})
}

Sentry.init({
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
integrations: [Sentry.replayIntegration()],
tracesSampleRate: isProd ? 0.2 : 1.0,
replaysSessionSampleRate: isProd ? 0.05 : 0.1,
replaysOnErrorSampleRate: 1.0,
sendDefaultPii: isProd ? false : true,
enableLogs: true,
});

export const onRouterTransitionStart = Sentry.captureRouterTransitionStart;
13 changes: 13 additions & 0 deletions apps/web/instrumentation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import * as Sentry from "@sentry/nextjs";

export async function register() {
if (process.env.NEXT_RUNTIME === "nodejs") {
await import("./sentry.server.config");
}

if (process.env.NEXT_RUNTIME === "edge") {
await import("./sentry.edge.config");
}
}

export const onRequestError = Sentry.captureRequestError;
Loading
Loading