-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
58 changed files
with
1,183 additions
and
453 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
21 changes: 21 additions & 0 deletions
21
web/app/routes/$userName+/edit/functions/queries.server.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import type { Page } from "@prisma/client"; | ||
import type { SafeUser } from "~/types"; | ||
import { prisma } from "~/utils/prisma"; | ||
import type { User } from "@prisma/client"; | ||
|
||
export async function getUserByUserName(userName: string) { | ||
return prisma.user.findUnique({ | ||
where: { | ||
userName, | ||
}, | ||
}); | ||
} | ||
|
||
export async function updateUser(userId: number, data: Partial<User>) { | ||
return prisma.user.update({ | ||
where: { | ||
id: userId, | ||
}, | ||
data, | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
|
||
import type { ActionFunctionArgs, LoaderFunctionArgs } from "@remix-run/node"; | ||
import { redirect } from "@remix-run/node"; | ||
import { Form, useLoaderData } from "@remix-run/react"; | ||
import { z } from "zod"; | ||
import { Button } from "~/components/ui/button"; | ||
import { Input } from "~/components/ui/input"; | ||
import { Label } from "~/components/ui/label"; | ||
import { authenticator } from "~/utils/auth.server"; | ||
import { getUserByUserName, updateUser } from "./functions/queries.server"; | ||
import { typedjson, useTypedLoaderData } from "remix-typedjson"; | ||
|
||
const schema = z.object({ | ||
displayName: z.string().min(1, "Display name is required").max(50, "Display name must be 50 characters or less"), | ||
}); | ||
|
||
export const loader = async ({ params, request }: LoaderFunctionArgs) => { | ||
const currentUser = await authenticator.isAuthenticated(request, { | ||
failureRedirect: "/login", | ||
}); | ||
|
||
const user = await getUserByUserName(params.userName || ""); | ||
if (!user) throw new Response("Not Found", { status: 404 }); | ||
|
||
if (user.userName !== params.userName) { | ||
throw new Response("Unauthorized", { status: 403 }); | ||
} | ||
|
||
return typedjson({ user }); | ||
}; | ||
|
||
export const action = async ({ request, params }: ActionFunctionArgs) => { | ||
const currentUser = await authenticator.isAuthenticated(request, { | ||
failureRedirect: "/login", | ||
}); | ||
|
||
const formData = await request.formData(); | ||
const displayName = formData.get("displayName") as string; | ||
|
||
const result = schema.safeParse({ displayName }); | ||
|
||
if (!result.success) { | ||
return typedjson({ errors: result.error.flatten().fieldErrors }); | ||
} | ||
|
||
await updateUser(currentUser.id, { displayName }); | ||
|
||
return redirect(`/${params.userName}`); | ||
}; | ||
|
||
export default function EditProfile() { | ||
const { user } = useTypedLoaderData<typeof loader>(); | ||
|
||
return ( | ||
<div className="container mx-auto mt-10"> | ||
<h1 className="text-3xl font-bold mb-6">Edit Profile</h1> | ||
<Form method="post" className="space-y-4"> | ||
<div> | ||
<Label htmlFor="displayName">Display Name</Label> | ||
<Input | ||
type="text" | ||
id="displayName" | ||
name="displayName" | ||
defaultValue={user.displayName} | ||
required | ||
/> | ||
</div> | ||
<Button type="submit">Save Changes</Button> | ||
</Form> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import type { Page } from "@prisma/client"; | ||
import type { SafeUser } from "~/types"; | ||
import { prisma } from "~/utils/prisma"; | ||
import type { UserWithPages } from "../types"; | ||
export async function getUserWithPages( | ||
userName: string, | ||
): Promise<UserWithPages | null> { | ||
const user = await prisma.user.findUnique({ | ||
where: { userName }, | ||
include: { | ||
pages: { | ||
orderBy: { createdAt: "desc" }, | ||
take: 10, | ||
}, | ||
}, | ||
}); | ||
|
||
if (!user) return null; | ||
|
||
const safeUser: SafeUser = { | ||
id: user.id, | ||
userName: user.userName, | ||
displayName: user.displayName, | ||
plan: user.plan, | ||
totalPoints: user.totalPoints, | ||
isAI: user.isAI, | ||
provider: user.provider, | ||
image: user.image, | ||
createdAt: user.createdAt, | ||
updatedAt: user.updatedAt, | ||
}; | ||
|
||
const pages: Page[] = user.pages; | ||
|
||
return { | ||
...safeUser, | ||
pages, | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
import type { LoaderFunctionArgs } from "@remix-run/node"; | ||
import { useLoaderData } from "@remix-run/react"; | ||
import { Button } from "~/components/ui/button"; | ||
import { | ||
Card, | ||
CardContent, | ||
CardDescription, | ||
CardHeader, | ||
CardTitle, | ||
} from "~/components/ui/card"; | ||
import { authenticator } from "~/utils/auth.server"; | ||
import { getUserWithPages } from "./functions/queries.server"; | ||
import type { UserWithPages } from "./types"; | ||
import { Link } from "@remix-run/react"; | ||
export const loader = async ({ params, request }: LoaderFunctionArgs) => { | ||
const { userName } = params; | ||
if (!userName) throw new Error("Username is required"); | ||
|
||
const userWithPages = await getUserWithPages(userName); | ||
if (!userWithPages) throw new Response("Not Found", { status: 404 }); | ||
|
||
const currentUser = await authenticator.isAuthenticated(request); | ||
|
||
const isOwnProfile = currentUser?.id === userWithPages.id; | ||
|
||
return { userWithPages: userWithPages, isOwnProfile }; | ||
}; | ||
|
||
export default function UserProfile() { | ||
const { userWithPages, isOwnProfile } = useLoaderData<{ | ||
userWithPages: UserWithPages; | ||
isOwnProfile: boolean; | ||
}>(); | ||
|
||
return ( | ||
<div className="container mx-auto mt-10"> | ||
<div className="flex justify-between items-center mb-6"> | ||
<h1 className="text-3xl font-bold"> | ||
{userWithPages.displayName} | ||
</h1> | ||
</div> | ||
|
||
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3"> | ||
{userWithPages.pages.map((page) => ( | ||
<Link | ||
to={`/${userWithPages.userName}/page/${page.slug}`} | ||
key={page.id} | ||
className="h-full" | ||
> | ||
<Card className="flex flex-col h-full"> | ||
<CardHeader> | ||
<CardTitle className="line-clamp-2">{page.title}</CardTitle> | ||
<CardDescription> | ||
{new Date(page.createdAt).toLocaleDateString()} | ||
</CardDescription> | ||
</CardHeader> | ||
<CardContent className="flex-grow"> | ||
<p className="text-sm text-gray-600 line-clamp-4"> | ||
{page.content} | ||
</p> | ||
</CardContent> | ||
</Card> | ||
</Link> | ||
))} | ||
</div> | ||
|
||
{userWithPages.pages.length === 0 && ( | ||
<p className="text-center text-gray-500 mt-10"> | ||
{isOwnProfile | ||
? "You haven't created any pages yet." | ||
: "No pages yet."} | ||
</p> | ||
)} | ||
</div> | ||
); | ||
} |
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
File renamed without changes.
Oops, something went wrong.