Skip to content

Commit 59a24be

Browse files
authored
Merge pull request #132 from CodeForStartup/feat/add-image-for-post
feat: add image for post create/update
2 parents c45413c + 933cb66 commit 59a24be

File tree

5 files changed

+81
-69
lines changed

5 files changed

+81
-69
lines changed

apps/web/@/molecules/post-form/index.tsx

Lines changed: 57 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,34 @@
11
"use client"
22

3-
import React from "react"
3+
import React, { useState } from "react"
44
import dynamic from "next/dynamic"
5+
import Image from "next/image"
56
import Link from "next/link"
67

78
import { zodResolver } from "@hookform/resolvers/zod"
8-
import { Prisma } from "database"
9+
import { Image as ImageType, Prisma, TPostItem } from "database"
10+
import { Upload as UploadIcon, X } from "lucide-react"
911
import { useSession } from "next-auth/react"
1012
import { useTranslations } from "next-intl"
1113
import { Controller, useForm } from "react-hook-form"
1214
import AsyncCreatableSelect from "react-select/async-creatable"
13-
import { buttonVariants, cn, Label, LoadingButton } from "ui"
15+
import { Button, buttonVariants, cn, Label, LoadingButton, Typography } from "ui"
1416
import z from "zod"
1517

1618
import { handleCreateUpdatePost } from "@/actions/protect/postAction"
1719
import APP_ROUTES from "@/constants/routes"
1820
import InputTitle from "@/molecules/input-title"
19-
import { TPostItem } from "@/types/posts"
2021

2122
import Upload from "../upload"
22-
import AssetManagement from "../upload/AssetsManagement"
2323

2424
const Editor = dynamic(() => import("../editor-js"), { ssr: false })
2525

2626
const PostForm = ({ post: postData }: { post?: TPostItem }) => {
2727
const { title = "", content = "", tagOnPost = [], id: postId } = postData || {}
28+
2829
const t = useTranslations()
2930
const session = useSession()
31+
const [image, setImage] = useState<ImageType | null>(postData?.Image)
3032

3133
const userId = session?.data?.user?.id
3234

@@ -78,7 +80,18 @@ const PostForm = ({ post: postData }: { post?: TPostItem }) => {
7880
}
7981

8082
const onSubmit = async (data) =>
81-
await handleCreateUpdatePost({ postId: postId as string, data, userId })
83+
await handleCreateUpdatePost({
84+
postId: postId as string,
85+
data: {
86+
...data,
87+
Image: {
88+
connect: {
89+
id: image?.id,
90+
},
91+
},
92+
},
93+
userId,
94+
})
8295

8396
return (
8497
<div className="w-full">
@@ -117,11 +130,44 @@ const PostForm = ({ post: postData }: { post?: TPostItem }) => {
117130
</div>
118131
</div>
119132
<div className="col-span-1">
120-
<Upload>
121-
<div className="flex h-[150px] cursor-pointer items-center justify-center rounded-sm bg-slate-300">
122-
<div>Upload</div>
123-
</div>
124-
</Upload>
133+
<div className="relative overflow-hidden rounded-lg border-2 p-2">
134+
<Upload onSelect={setImage}>
135+
{image ? (
136+
<div className="group relative cursor-pointer">
137+
<Image
138+
src={image.url}
139+
alt="image"
140+
width={480}
141+
height={270}
142+
className="border-1 flex aspect-video w-full rounded-sm object-cover hover:scale-90 hover:opacity-50"
143+
/>
144+
<div className="invisible absolute inset-0 flex-col items-center justify-center gap-2 group-hover:flex">
145+
<UploadIcon className="invisible group-hover:visible" />
146+
<Typography
147+
className="bold invisible group-hover:visible"
148+
variant="mutedText"
149+
>
150+
{t("uploads.upload_image")}
151+
</Typography>
152+
</div>
153+
</div>
154+
) : (
155+
<div className="border-1 flex aspect-video w-full cursor-pointer flex-col items-center justify-center gap-2 rounded-sm bg-slate-200">
156+
<UploadIcon />
157+
<Typography variant="mutedText">{t("uploads.upload_image")}</Typography>
158+
</div>
159+
)}
160+
</Upload>
161+
{image && (
162+
<Button
163+
onClick={() => setImage(null)}
164+
variant="outline"
165+
className="absolute right-2 top-2 h-6 w-6 p-0"
166+
>
167+
<X className="text-destructive" />
168+
</Button>
169+
)}
170+
</div>
125171
<div className="mt-4">
126172
<Label>Tags</Label>
127173
<Controller

apps/web/@/molecules/upload/index.tsx

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ReactNode } from "react"
1+
import { ReactNode, useState } from "react"
22

33
import { Image } from "database"
44
import { X } from "lucide-react"
@@ -95,10 +95,19 @@ const TotalItems: React.FC = () => {
9595

9696
const Upload: React.FC<UploadProps> = ({ children, onSelect }) => {
9797
const t = useTranslations("uploads")
98+
const [open, setOpen] = useState(false)
99+
100+
const onSelectImage = (image?: Image) => {
101+
onSelect(image)
102+
setOpen(false)
103+
}
98104

99105
return (
100106
<FileManagerContainer>
101-
<Dialog>
107+
<Dialog
108+
open={open}
109+
onOpenChange={setOpen}
110+
>
102111
<DialogTrigger asChild>{children}</DialogTrigger>
103112
<DialogContent className="w-full max-w-[800px] gap-0 p-0">
104113
<DialogHeader className="mb-0 flex flex-row items-center gap-4 border-b px-4 py-1">
@@ -115,7 +124,7 @@ const Upload: React.FC<UploadProps> = ({ children, onSelect }) => {
115124
<TotalItems />
116125
<SelectedFiles />
117126
</div>
118-
<SelectButton onSelect={onSelect} />
127+
<SelectButton onSelect={onSelectImage} />
119128
</DialogFooter>
120129
</DialogContent>
121130
</Dialog>

apps/web/@/types/posts.ts

Lines changed: 0 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,5 @@
11
import { PostOnUserType, Prisma } from "database"
22

3-
export const postSelect = {
4-
id: true,
5-
title: true,
6-
content: true,
7-
createdAt: true,
8-
updatedAt: true,
9-
slug: true,
10-
postStatus: true,
11-
totalLike: true,
12-
totalFollow: true,
13-
author: {
14-
select: {
15-
id: true,
16-
name: true,
17-
email: true,
18-
image: true,
19-
},
20-
},
21-
postOnUser: {
22-
select: {
23-
type: true,
24-
userId: true,
25-
postId: true,
26-
},
27-
},
28-
_count: {
29-
select: {
30-
comments: true,
31-
postOnUser: true,
32-
},
33-
},
34-
tagOnPost: {
35-
select: {
36-
tag: {
37-
select: {
38-
id: true,
39-
name: true,
40-
slug: true,
41-
},
42-
},
43-
},
44-
},
45-
} satisfies Prisma.PostSelect
46-
47-
const getPostItem = Prisma.validator<Prisma.PostDefaultArgs>()({
48-
select: postSelect,
49-
})
50-
51-
export type TPostItem = Prisma.PostGetPayload<typeof getPostItem>
52-
533
export type TCreatePostInput = Prisma.PostCreateInput & {
544
tags: {
555
value: string
Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
11
import { Metadata } from "next"
22

3-
import { getPostById } from "@/actions/protect/posts"
3+
import { getPost } from "database"
4+
45
import PostForm from "@/molecules/post-form"
56

67
export async function generateMetadata({ params }): Promise<Metadata> {
7-
const post = await getPostById(params?.postId as string)
8+
const post = await getPost({ postIdOrSlug: params?.postId as string })
89

910
return {
10-
title: post?.title,
11+
title: post?.data?.title,
1112
description: "", // post?.content.slice(0, 160),
1213
}
1314
}
1415

1516
export default async function Page({ params }: { params: { postId: string } }) {
16-
const post = await getPostById(params?.postId as string)
17+
const post = await getPost({ postIdOrSlug: params?.postId as string })
1718

18-
return <PostForm post={post} />
19+
return <PostForm post={post?.data} />
1920
}

packages/database/src/posts/selects.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ export const postSelect = {
1010
postStatus: true,
1111
totalLike: true,
1212
totalFollow: true,
13+
Image: {
14+
select: {
15+
id: true,
16+
url: true,
17+
},
18+
},
1319
author: {
1420
select: {
1521
id: true,

0 commit comments

Comments
 (0)