End to End React with Prisma 2
Start at the beginning and work your way through this project. The code for each step as well as the finished project can be found in the Github repository.
20. Add Badges
Objective: Create badge components to see tags, feeds and bundles associated with an item.
Our <OneListItem>
component is off to a good start and in this step we will add the <BadgeList>
component so that we can list out the bundles, feeds and tags that belong to the current item. Let's start by creating the <BadgeList>
.
To keep things a little streamlined, we will build out the component as it will eventually end up being even though we won't use all the features right now. If all the functionality doesn't make 100% sense, don't worry because we'll be coming back to it.
components/badgeList.tsx
tsx
import { Dispatch, SetStateAction } from 'react'import {ActionType,BadgeFieldName,BundleState,FeedState,} from '../utils/types'import { OneBadge } from './oneBadge'export const BadgeList = ({fieldName,action,setItem,item,}: {fieldName: BadgeFieldNameaction: ActionTypeitem: FeedState | BundleStatesetItem?: Dispatch<SetStateAction<FeedState | BundleState>>}) => {return item[fieldName].length > 0 ? (<>{item[fieldName].map(oneBadge => (<OneBadgekey={`${item['id']}-${oneBadge.name}`}fieldName={fieldName}item={oneBadge}action={action}setItem={setItem}currentItem={item}/>))}</>) : (<p className="text-gray-400">None found</p>)}
Our Badge will be used for representing feedTags, bundleTags, bundles, and feeds. We specify what kind of feed it is by using the BadgeFieldName
type which will be a string that specifies what kind it is. Next, the item
specifies what the parent type is that these tags belong to. In this step, we only be displaying badges, with no ability to add or remove them so we will not be using setItem
yet and due to this static nature of this component at this time, action
prop will be set to NONE
which we'll show you how to set in a little bit.
Aside from some of these props that aren't quite in use yet, this component simply takes the item
object and then will take from that a property, fieldName
which will either be tags
, feeds
, or bundles
depending on what we are trying to show.
To fill in those missing types, add them to the types.ts file:
We need to first add the missing types:
utils/types.ts
ts
// other tupes aboveexport type BundleState = {name: stringdescription: stringtags: TagObject[]feeds: FeedState[]}export type FeedState = {id?: numbername: stringurl: stringtags: TagObject[]}export enum BadgeFieldName {tags = 'tags',feeds = 'feeds',bundles = 'bundles',}export enum ActionType {ADD = 'ADD',CREATE = 'CREATE',NONE = 'NONE',}
Now that we have built out our list component, we have to build the <OneBadge>
component so we can actually use it.
components/oneBadge.tsx
tsx
import { BundleTag, FeedTag } from '@prisma/client'import { Dispatch, SetStateAction } from 'react'import {ActionType,BadgeFieldName,BundleState,FeedObject,FeedState,} from '../utils/types'export const OneBadge = ({item,action,currentItem,fieldName,setItem,}: {item: FeedTag | BundleTag | FeedObjectaction: ActionTypecurrentItem?: FeedState | BundleStatefieldName: BadgeFieldNamesetItem?: Dispatch<SetStateAction<FeedState | BundleState>>}) => {const color =fieldName === BadgeFieldName.tags? `blue`: fieldName === BadgeFieldName.feeds? `green`: `purple`return (<div className="inline-block align-middle"><spanclassName={`flex justify-center text-sm py-2 px-2 rounded-lg bg-${color}-200`}><p className={`text-xs text-${color}-600 text-center`}>{item.name}</p></span></div>)}
Here, the currentItem
is the parent item that this badge belongs to, setItem
will be a future setting function for adding or deleting a badge from new or edit functionality, and action
just as in the <BadgeList>
component will be determining whether there is any actions associated with that badge. In our case though, action will be set to NONE
which won't do anything at the moment.
The only thing that is important to pay attention to is that we will return back a tag with the name of the item. Here, you can see that it is a <p>
tag nested inside a <span>
and <div>
tag.
Before wrapping up, let's make the <ProfilePic>
component. This will pull a picture from the auth0 user object and we will display it alongside the profile name.
components/profilePic.tsx
tsx
import { AuthorObject } from '../utils/types'import { Question } from './svg'export const ProfilePic = ({ author }: { author: AuthorObject }) => (<>{author.picture ? (<div className="rounded-full"><imgclassName="rounded-full p-0.5 w-8 h-8 border-2 border-gray-300"src={author.picture}/></div>) : (<Question className="w-6 h-6 text-gray-500" />)}<p>{author.nickname}</p></>)
components/svg.tsx
tsx
// other svgs aboveexport const Question = ({ className }) => (<svgxmlns="http://www.w3.org/2000/svg"fill="none"viewBox="0 0 24 24"stroke="currentColor"className={className}><pathstrokeLinecap="round"strokeLinejoin="round"strokeWidth={2}d="M8.228 9c.549-1.165 2.03-2 3.772-2 2.21 0 4 1.343 4 3 0 1.4-1.278 2.575-3.006 2.907-.542.104-.994.54-.994 1.093m0 3h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>)