chore: migrate from tailwind to mantine

This commit is contained in:
Meier Lukas
2023-12-08 23:11:37 +01:00
parent a2cedf73c0
commit d115779c85
16 changed files with 1159 additions and 6218 deletions

6
.gitignore vendored
View File

@@ -17,12 +17,6 @@ next-env.d.ts
.nitro/ .nitro/
.output/ .output/
# expo
.expo/
dist/
expo-env.d.ts
apps/expo/.gitignore
# production # production
build build

View File

@@ -1,9 +1,7 @@
{ {
"recommendations": [ "recommendations": [
"bradlc.vscode-tailwindcss",
"dbaeumer.vscode-eslint", "dbaeumer.vscode-eslint",
"esbenp.prettier-vscode", "esbenp.prettier-vscode",
"expo.vscode-expo-tools",
"yoavbls.pretty-ts-errors" "yoavbls.pretty-ts-errors"
] ]
} }

View File

@@ -16,7 +16,6 @@
"devDependencies": { "devDependencies": {
"@acme/eslint-config": "workspace:^0.2.0", "@acme/eslint-config": "workspace:^0.2.0",
"@acme/prettier-config": "workspace:^0.1.0", "@acme/prettier-config": "workspace:^0.1.0",
"@acme/tailwind-config": "workspace:^0.1.0",
"@acme/tsconfig": "workspace:^0.1.0", "@acme/tsconfig": "workspace:^0.1.0",
"eslint": "^8.53.0", "eslint": "^8.53.0",
"nitropack": "^2.8.1", "nitropack": "^2.8.1",

View File

@@ -16,15 +16,28 @@
"@acme/api": "workspace:^0.1.0", "@acme/api": "workspace:^0.1.0",
"@acme/auth": "workspace:^0.1.0", "@acme/auth": "workspace:^0.1.0",
"@acme/db": "workspace:^0.1.0", "@acme/db": "workspace:^0.1.0",
"@mantine/core": "^7.3.1",
"@mantine/dates": "^7.3.1",
"@mantine/form": "^7.3.1",
"@mantine/hooks": "^7.3.1",
"@mantine/notifications": "^7.3.1",
"@mantine/spotlight": "^7.3.1",
"@mantine/tiptap": "^7.3.1",
"@t3-oss/env-nextjs": "^0.7.1", "@t3-oss/env-nextjs": "^0.7.1",
"@tabler/icons-react": "^2.42.0",
"@tanstack/react-query": "^5.8.7", "@tanstack/react-query": "^5.8.7",
"@tanstack/react-query-devtools": "^5.8.7", "@tanstack/react-query-devtools": "^5.8.7",
"@tanstack/react-query-next-experimental": "5.8.7", "@tanstack/react-query-next-experimental": "5.8.7",
"@tiptap/extension-link": "^2.1.13",
"@tiptap/react": "^2.1.13",
"@tiptap/starter-kit": "^2.1.13",
"@trpc/client": "next", "@trpc/client": "next",
"@trpc/next": "next", "@trpc/next": "next",
"@trpc/react-query": "next", "@trpc/react-query": "next",
"@trpc/server": "next", "@trpc/server": "next",
"dayjs": "^1.11.10",
"next": "^14.0.3", "next": "^14.0.3",
"postcss-preset-mantine": "^1.11.1",
"react": "18.2.0", "react": "18.2.0",
"react-dom": "18.2.0", "react-dom": "18.2.0",
"superjson": "2.2.1", "superjson": "2.2.1",
@@ -33,7 +46,6 @@
"devDependencies": { "devDependencies": {
"@acme/eslint-config": "workspace:^0.2.0", "@acme/eslint-config": "workspace:^0.2.0",
"@acme/prettier-config": "workspace:^0.1.0", "@acme/prettier-config": "workspace:^0.1.0",
"@acme/tailwind-config": "workspace:^0.1.0",
"@acme/tsconfig": "workspace:^0.1.0", "@acme/tsconfig": "workspace:^0.1.0",
"@types/node": "^18.18.13", "@types/node": "^18.18.13",
"@types/react": "^18.2.42", "@types/react": "^18.2.42",
@@ -41,7 +53,6 @@
"dotenv-cli": "^7.3.0", "dotenv-cli": "^7.3.0",
"eslint": "^8.53.0", "eslint": "^8.53.0",
"prettier": "^3.1.0", "prettier": "^3.1.0",
"tailwindcss": "3.3.5",
"typescript": "^5.3.3" "typescript": "^5.3.3"
}, },
"eslintConfig": { "eslintConfig": {

View File

@@ -1,6 +1,14 @@
module.exports = { module.exports = {
plugins: { plugins: {
tailwindcss: {}, 'postcss-preset-mantine': {},
autoprefixer: {}, 'postcss-simple-vars': {
variables: {
'mantine-breakpoint-xs': '36em',
'mantine-breakpoint-sm': '48em',
'mantine-breakpoint-md': '62em',
'mantine-breakpoint-lg': '75em',
'mantine-breakpoint-xl': '88em',
},
},
}, },
}; };

View File

@@ -1,39 +0,0 @@
import { auth, signIn, signOut } from "@acme/auth";
export async function AuthShowcase() {
const session = await auth();
if (!session) {
return (
<form
action={async () => {
"use server";
await signIn("discord");
}}
>
<button className="rounded-full bg-white/10 px-10 py-3 font-semibold no-underline transition hover:bg-white/20">
Sign in with Discord
</button>
</form>
);
}
return (
<div className="flex flex-col items-center justify-center gap-4">
<p className="text-center text-2xl text-white">
{session && <span>Logged in as {session.user.name}</span>}
</p>
<form
action={async () => {
"use server";
await signOut();
}}
>
<button className="rounded-full bg-white/10 px-10 py-3 font-semibold no-underline transition hover:bg-white/20">
Sign out
</button>
</form>
</div>
);
}

View File

@@ -1,148 +0,0 @@
"use client";
import { useState } from "react";
import { api } from "~/utils/api";
import type { RouterOutputs } from "~/utils/api";
export function CreatePostForm() {
const context = api.useContext();
const [title, setTitle] = useState("");
const [content, setContent] = useState("");
const { mutateAsync: createPost, error } = api.post.create.useMutation({
async onSuccess() {
setTitle("");
setContent("");
await context.post.all.invalidate();
},
});
return (
<form
className="flex w-full max-w-2xl flex-col"
onSubmit={async (e) => {
e.preventDefault();
try {
await createPost({
title,
content,
});
setTitle("");
setContent("");
await context.post.all.invalidate();
} catch {
// noop
}
}}
>
<input
className="mb-2 rounded bg-white/10 p-2 text-white"
value={title}
onChange={(e) => setTitle(e.target.value)}
placeholder="Title"
/>
{error?.data?.zodError?.fieldErrors.title && (
<span className="mb-2 text-red-500">
{error.data.zodError.fieldErrors.title}
</span>
)}
<input
className="mb-2 rounded bg-white/10 p-2 text-white"
value={content}
onChange={(e) => setContent(e.target.value)}
placeholder="Content"
/>
{error?.data?.zodError?.fieldErrors.content && (
<span className="mb-2 text-red-500">
{error.data.zodError.fieldErrors.content}
</span>
)}
{}
<button type="submit" className="rounded bg-pink-400 p-2 font-bold">
Create
</button>
{error?.data?.code === "UNAUTHORIZED" && (
<span className="mt-2 text-red-500">You must be logged in to post</span>
)}
</form>
);
}
export function PostList() {
const [posts] = api.post.all.useSuspenseQuery();
if (posts.length === 0) {
return (
<div className="relative flex w-full flex-col gap-4">
<PostCardSkeleton pulse={false} />
<PostCardSkeleton pulse={false} />
<PostCardSkeleton pulse={false} />
<div className="absolute inset-0 flex flex-col items-center justify-center bg-black/10">
<p className="text-2xl font-bold text-white">No posts yet</p>
</div>
</div>
);
}
return (
<div className="flex w-full flex-col gap-4">
{posts.map((p) => {
return <PostCard key={p.id} post={p} />;
})}
</div>
);
}
export function PostCard(props: {
post: RouterOutputs["post"]["all"][number];
}) {
const context = api.useContext();
const deletePost = api.post.delete.useMutation();
return (
<div className="flex flex-row rounded-lg bg-white/10 p-4 transition-all hover:scale-[101%]">
<div className="flex-grow">
<h2 className="text-2xl font-bold text-pink-400">{props.post.title}</h2>
<p className="mt-2 text-sm">{props.post.content}</p>
</div>
<div>
<button
className="cursor-pointer text-sm font-bold uppercase text-pink-400"
onClick={async () => {
await deletePost.mutateAsync(props.post.id);
await context.post.all.invalidate();
}}
>
Delete
</button>
</div>
</div>
);
}
export function PostCardSkeleton(props: { pulse?: boolean }) {
const { pulse = true } = props;
return (
<div className="flex flex-row rounded-lg bg-white/10 p-4 transition-all hover:scale-[101%]">
<div className="flex-grow">
<h2
className={`w-1/4 rounded bg-pink-400 text-2xl font-bold ${
pulse && "animate-pulse"
}`}
>
&nbsp;
</h2>
<p
className={`mt-2 w-1/3 rounded bg-current text-sm ${
pulse && "animate-pulse"
}`}
>
&nbsp;
</p>
</div>
</div>
);
}

View File

@@ -1,8 +1,11 @@
import type { Metadata } from "next"; import type { Metadata } from "next";
import { Inter } from "next/font/google"; import { Inter } from "next/font/google";
import "~/styles/globals.css"; import '@mantine/core/styles.css';
import '@mantine/dates/styles.css';
import '@mantine/notifications/styles.css';
import { MantineProvider, ColorSchemeScript } from '@mantine/core';
import { headers } from "next/headers"; import { headers } from "next/headers";
import { TRPCReactProvider } from "./providers"; import { TRPCReactProvider } from "./providers";
@@ -22,25 +25,17 @@ export const dynamic = "force-dynamic";
export const metadata: Metadata = { export const metadata: Metadata = {
title: "Create T3 Turbo", title: "Create T3 Turbo",
description: "Simple monorepo with shared backend for web & mobile apps", description: "Simple monorepo with shared backend for web & mobile apps",
openGraph: {
title: "Create T3 Turbo",
description: "Simple monorepo with shared backend for web & mobile apps",
url: "https://create-t3-turbo.vercel.app",
siteName: "Create T3 Turbo",
},
twitter: {
card: "summary_large_image",
site: "@jullerino",
creator: "@jullerino",
},
}; };
export default function Layout(props: { children: React.ReactNode }) { export default function Layout(props: { children: React.ReactNode }) {
return ( return (
<html lang="en"> <html lang="en">
<head>
<ColorSchemeScript />
</head>
<body className={["font-sans", fontSans.variable].join(" ")}> <body className={["font-sans", fontSans.variable].join(" ")}>
<TRPCReactProvider headers={headers()}> <TRPCReactProvider headers={headers()}>
{props.children} <MantineProvider>{props.children}</MantineProvider>
</TRPCReactProvider> </TRPCReactProvider>
</body> </body>
</html> </html>

View File

@@ -1,38 +1,7 @@
import { Suspense } from "react"; import { Title } from "@mantine/core";
import { AuthShowcase } from "./_components/auth-showcase";
import {
CreatePostForm,
PostCardSkeleton,
PostList,
} from "./_components/posts";
export const runtime = "edge";
export default function HomePage() { export default function HomePage() {
return ( return (
<main className="flex h-screen flex-col items-center bg-gradient-to-b from-[#2e026d] to-[#15162c] text-white"> <Title>Home</Title>
<div className="container mt-12 flex flex-col items-center justify-center gap-4 py-8">
<h1 className="text-5xl font-extrabold tracking-tight sm:text-[5rem]">
Create <span className="text-pink-400">T3</span> Turbo
</h1>
<AuthShowcase />
<CreatePostForm />
<div className="h-[40vh] w-full max-w-2xl overflow-y-scroll">
<Suspense
fallback={
<div className="flex w-full flex-col gap-4">
<PostCardSkeleton />
<PostCardSkeleton />
<PostCardSkeleton />
</div>
}
>
<PostList />
</Suspense>
</div>
</div>
</main>
); );
} }

View File

@@ -1,5 +1,5 @@
{ {
"name": "create-t3-turbo", "name": "alparr",
"private": true, "private": true,
"engines": { "engines": {
"node": ">=18.18.2" "node": ">=18.18.2"

7022
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,22 +1,15 @@
import { fileURLToPath } from "url";
/** @typedef {import("prettier").Config} PrettierConfig */ /** @typedef {import("prettier").Config} PrettierConfig */
/** @typedef {import("prettier-plugin-tailwindcss").PluginOptions} TailwindConfig */
/** @typedef {import("@ianvs/prettier-plugin-sort-imports").PluginConfig} SortImportsConfig */ /** @typedef {import("@ianvs/prettier-plugin-sort-imports").PluginConfig} SortImportsConfig */
/** @type { PrettierConfig | SortImportsConfig | TailwindConfig } */ /** @type { PrettierConfig | SortImportsConfig } */
const config = { const config = {
plugins: [ plugins: [
"@ianvs/prettier-plugin-sort-imports", "@ianvs/prettier-plugin-sort-imports"
"prettier-plugin-tailwindcss",
], ],
tailwindConfig: fileURLToPath(
new URL("../../tooling/tailwind/index.ts", import.meta.url),
),
importOrder: [ importOrder: [
"^(react/(.*)$)|^(react$)|^(react-native(.*)$)", "^(react/(.*)$)|^(react$)|^(react-native(.*)$)",
"^(next/(.*)$)|^(next$)", "^(next/(.*)$)|^(next$)",
"^(expo(.*)$)|^(expo$)",
"<THIRD_PARTY_MODULES>", "<THIRD_PARTY_MODULES>",
"", "",
"^@acme/(.*)$", "^@acme/(.*)$",

View File

@@ -10,8 +10,7 @@
}, },
"dependencies": { "dependencies": {
"@ianvs/prettier-plugin-sort-imports": "^4.1.1", "@ianvs/prettier-plugin-sort-imports": "^4.1.1",
"prettier": "^3.1.0", "prettier": "^3.1.0"
"prettier-plugin-tailwindcss": "^0.5.7"
}, },
"devDependencies": { "devDependencies": {
"@acme/tsconfig": "workspace:^0.1.0", "@acme/tsconfig": "workspace:^0.1.0",

View File

@@ -2,7 +2,11 @@
"$schema": "https://json.schemastore.org/tsconfig", "$schema": "https://json.schemastore.org/tsconfig",
"compilerOptions": { "compilerOptions": {
"target": "ES2022", "target": "ES2022",
"lib": ["dom", "dom.iterable", "ES2022"], "lib": [
"dom",
"dom.iterable",
"ES2022"
],
"allowJs": true, "allowJs": true,
"skipLibCheck": true, "skipLibCheck": true,
"strict": true, "strict": true,
@@ -17,5 +21,10 @@
"incremental": true, "incremental": true,
"noUncheckedIndexedAccess": true "noUncheckedIndexedAccess": true
}, },
"exclude": ["node_modules", "build", "dist", ".next", ".expo"] "exclude": [
"node_modules",
"build",
"dist",
".next"
]
} }

View File

@@ -1,17 +1,22 @@
{ {
"$schema": "https://turborepo.org/schema.json", "$schema": "https://turborepo.org/schema.json",
"globalDependencies": ["**/.env"], "globalDependencies": [
"**/.env"
],
"pipeline": { "pipeline": {
"topo": { "topo": {
"dependsOn": ["^topo"] "dependsOn": [
"^topo"
]
}, },
"build": { "build": {
"dependsOn": ["^build"], "dependsOn": [
"^build"
],
"outputs": [ "outputs": [
".next/**", ".next/**",
"!.next/cache/**", "!.next/cache/**",
"next-env.d.ts", "next-env.d.ts",
".expo/**",
".output/**", ".output/**",
".vercel/output/**" ".vercel/output/**"
] ]
@@ -21,16 +26,26 @@
"cache": false "cache": false
}, },
"format": { "format": {
"outputs": ["node_modules/.cache/.prettiercache"], "outputs": [
"node_modules/.cache/.prettiercache"
],
"outputMode": "new-only" "outputMode": "new-only"
}, },
"lint": { "lint": {
"dependsOn": ["^topo"], "dependsOn": [
"outputs": ["node_modules/.cache/.eslintcache"] "^topo"
],
"outputs": [
"node_modules/.cache/.eslintcache"
]
}, },
"typecheck": { "typecheck": {
"dependsOn": ["^topo"], "dependsOn": [
"outputs": ["node_modules/.cache/tsbuildinfo.json"] "^topo"
],
"outputs": [
"node_modules/.cache/tsbuildinfo.json"
]
}, },
"clean": { "clean": {
"cache": false "cache": false