mirror of
https://github.com/ajnart/homarr.git
synced 2026-02-26 16:30:57 +01:00
chore: migrate from tailwind to mantine
This commit is contained in:
6
.gitignore
vendored
6
.gitignore
vendored
@@ -17,12 +17,6 @@ next-env.d.ts
|
||||
.nitro/
|
||||
.output/
|
||||
|
||||
# expo
|
||||
.expo/
|
||||
dist/
|
||||
expo-env.d.ts
|
||||
apps/expo/.gitignore
|
||||
|
||||
# production
|
||||
build
|
||||
|
||||
|
||||
2
.vscode/extensions.json
vendored
2
.vscode/extensions.json
vendored
@@ -1,9 +1,7 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"bradlc.vscode-tailwindcss",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"esbenp.prettier-vscode",
|
||||
"expo.vscode-expo-tools",
|
||||
"yoavbls.pretty-ts-errors"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
"devDependencies": {
|
||||
"@acme/eslint-config": "workspace:^0.2.0",
|
||||
"@acme/prettier-config": "workspace:^0.1.0",
|
||||
"@acme/tailwind-config": "workspace:^0.1.0",
|
||||
"@acme/tsconfig": "workspace:^0.1.0",
|
||||
"eslint": "^8.53.0",
|
||||
"nitropack": "^2.8.1",
|
||||
@@ -30,4 +29,4 @@
|
||||
]
|
||||
},
|
||||
"prettier": "@acme/prettier-config"
|
||||
}
|
||||
}
|
||||
@@ -16,15 +16,28 @@
|
||||
"@acme/api": "workspace:^0.1.0",
|
||||
"@acme/auth": "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",
|
||||
"@tabler/icons-react": "^2.42.0",
|
||||
"@tanstack/react-query": "^5.8.7",
|
||||
"@tanstack/react-query-devtools": "^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/next": "next",
|
||||
"@trpc/react-query": "next",
|
||||
"@trpc/server": "next",
|
||||
"dayjs": "^1.11.10",
|
||||
"next": "^14.0.3",
|
||||
"postcss-preset-mantine": "^1.11.1",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"superjson": "2.2.1",
|
||||
@@ -33,7 +46,6 @@
|
||||
"devDependencies": {
|
||||
"@acme/eslint-config": "workspace:^0.2.0",
|
||||
"@acme/prettier-config": "workspace:^0.1.0",
|
||||
"@acme/tailwind-config": "workspace:^0.1.0",
|
||||
"@acme/tsconfig": "workspace:^0.1.0",
|
||||
"@types/node": "^18.18.13",
|
||||
"@types/react": "^18.2.42",
|
||||
@@ -41,7 +53,6 @@
|
||||
"dotenv-cli": "^7.3.0",
|
||||
"eslint": "^8.53.0",
|
||||
"prettier": "^3.1.0",
|
||||
"tailwindcss": "3.3.5",
|
||||
"typescript": "^5.3.3"
|
||||
},
|
||||
"eslintConfig": {
|
||||
@@ -53,4 +64,4 @@
|
||||
]
|
||||
},
|
||||
"prettier": "@acme/prettier-config"
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,14 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
'postcss-preset-mantine': {},
|
||||
'postcss-simple-vars': {
|
||||
variables: {
|
||||
'mantine-breakpoint-xs': '36em',
|
||||
'mantine-breakpoint-sm': '48em',
|
||||
'mantine-breakpoint-md': '62em',
|
||||
'mantine-breakpoint-lg': '75em',
|
||||
'mantine-breakpoint-xl': '88em',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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"
|
||||
}`}
|
||||
>
|
||||
|
||||
</h2>
|
||||
<p
|
||||
className={`mt-2 w-1/3 rounded bg-current text-sm ${
|
||||
pulse && "animate-pulse"
|
||||
}`}
|
||||
>
|
||||
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,8 +1,11 @@
|
||||
import type { Metadata } from "next";
|
||||
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 { TRPCReactProvider } from "./providers";
|
||||
@@ -22,25 +25,17 @@ export const dynamic = "force-dynamic";
|
||||
export const metadata: Metadata = {
|
||||
title: "Create T3 Turbo",
|
||||
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 }) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<head>
|
||||
<ColorSchemeScript />
|
||||
</head>
|
||||
<body className={["font-sans", fontSans.variable].join(" ")}>
|
||||
<TRPCReactProvider headers={headers()}>
|
||||
{props.children}
|
||||
<MantineProvider>{props.children}</MantineProvider>
|
||||
</TRPCReactProvider>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,38 +1,7 @@
|
||||
import { Suspense } from "react";
|
||||
|
||||
import { AuthShowcase } from "./_components/auth-showcase";
|
||||
import {
|
||||
CreatePostForm,
|
||||
PostCardSkeleton,
|
||||
PostList,
|
||||
} from "./_components/posts";
|
||||
|
||||
export const runtime = "edge";
|
||||
import { Title } from "@mantine/core";
|
||||
|
||||
export default function HomePage() {
|
||||
return (
|
||||
<main className="flex h-screen flex-col items-center bg-gradient-to-b from-[#2e026d] to-[#15162c] text-white">
|
||||
<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>
|
||||
<Title>Home</Title>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "create-t3-turbo",
|
||||
"name": "alparr",
|
||||
"private": true,
|
||||
"engines": {
|
||||
"node": ">=18.18.2"
|
||||
@@ -33,4 +33,4 @@
|
||||
}
|
||||
},
|
||||
"prettier": "@acme/prettier-config"
|
||||
}
|
||||
}
|
||||
7022
pnpm-lock.yaml
generated
7022
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,22 +1,15 @@
|
||||
import { fileURLToPath } from "url";
|
||||
|
||||
/** @typedef {import("prettier").Config} PrettierConfig */
|
||||
/** @typedef {import("prettier-plugin-tailwindcss").PluginOptions} TailwindConfig */
|
||||
/** @typedef {import("@ianvs/prettier-plugin-sort-imports").PluginConfig} SortImportsConfig */
|
||||
|
||||
/** @type { PrettierConfig | SortImportsConfig | TailwindConfig } */
|
||||
/** @type { PrettierConfig | SortImportsConfig } */
|
||||
const config = {
|
||||
plugins: [
|
||||
"@ianvs/prettier-plugin-sort-imports",
|
||||
"prettier-plugin-tailwindcss",
|
||||
"@ianvs/prettier-plugin-sort-imports"
|
||||
],
|
||||
tailwindConfig: fileURLToPath(
|
||||
new URL("../../tooling/tailwind/index.ts", import.meta.url),
|
||||
),
|
||||
importOrder: [
|
||||
"^(react/(.*)$)|^(react$)|^(react-native(.*)$)",
|
||||
"^(next/(.*)$)|^(next$)",
|
||||
"^(expo(.*)$)|^(expo$)",
|
||||
"<THIRD_PARTY_MODULES>",
|
||||
"",
|
||||
"^@acme/(.*)$",
|
||||
|
||||
@@ -10,12 +10,11 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@ianvs/prettier-plugin-sort-imports": "^4.1.1",
|
||||
"prettier": "^3.1.0",
|
||||
"prettier-plugin-tailwindcss": "^0.5.7"
|
||||
"prettier": "^3.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@acme/tsconfig": "workspace:^0.1.0",
|
||||
"typescript": "^5.3.3"
|
||||
},
|
||||
"prettier": "@acme/prettier-config"
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,11 @@
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"lib": ["dom", "dom.iterable", "ES2022"],
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"ES2022"
|
||||
],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
@@ -17,5 +21,10 @@
|
||||
"incremental": true,
|
||||
"noUncheckedIndexedAccess": true
|
||||
},
|
||||
"exclude": ["node_modules", "build", "dist", ".next", ".expo"]
|
||||
}
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"build",
|
||||
"dist",
|
||||
".next"
|
||||
]
|
||||
}
|
||||
35
turbo.json
35
turbo.json
@@ -1,17 +1,22 @@
|
||||
{
|
||||
"$schema": "https://turborepo.org/schema.json",
|
||||
"globalDependencies": ["**/.env"],
|
||||
"globalDependencies": [
|
||||
"**/.env"
|
||||
],
|
||||
"pipeline": {
|
||||
"topo": {
|
||||
"dependsOn": ["^topo"]
|
||||
"dependsOn": [
|
||||
"^topo"
|
||||
]
|
||||
},
|
||||
"build": {
|
||||
"dependsOn": ["^build"],
|
||||
"dependsOn": [
|
||||
"^build"
|
||||
],
|
||||
"outputs": [
|
||||
".next/**",
|
||||
"!.next/cache/**",
|
||||
"next-env.d.ts",
|
||||
".expo/**",
|
||||
".output/**",
|
||||
".vercel/output/**"
|
||||
]
|
||||
@@ -21,16 +26,26 @@
|
||||
"cache": false
|
||||
},
|
||||
"format": {
|
||||
"outputs": ["node_modules/.cache/.prettiercache"],
|
||||
"outputs": [
|
||||
"node_modules/.cache/.prettiercache"
|
||||
],
|
||||
"outputMode": "new-only"
|
||||
},
|
||||
"lint": {
|
||||
"dependsOn": ["^topo"],
|
||||
"outputs": ["node_modules/.cache/.eslintcache"]
|
||||
"dependsOn": [
|
||||
"^topo"
|
||||
],
|
||||
"outputs": [
|
||||
"node_modules/.cache/.eslintcache"
|
||||
]
|
||||
},
|
||||
"typecheck": {
|
||||
"dependsOn": ["^topo"],
|
||||
"outputs": ["node_modules/.cache/tsbuildinfo.json"]
|
||||
"dependsOn": [
|
||||
"^topo"
|
||||
],
|
||||
"outputs": [
|
||||
"node_modules/.cache/tsbuildinfo.json"
|
||||
]
|
||||
},
|
||||
"clean": {
|
||||
"cache": false
|
||||
@@ -47,4 +62,4 @@
|
||||
"AUTH_SECRET",
|
||||
"AUTH_URL"
|
||||
]
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user