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: BadgeFieldName
    action: ActionType
    item: FeedState | BundleState
    setItem?: Dispatch<SetStateAction<FeedState | BundleState>>
    }) => {
    return item[fieldName].length > 0 ? (
    <>
    {item[fieldName].map(oneBadge => (
    <OneBadge
    key={`${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 above
    export type BundleState = {
    name: string
    description: string
    tags: TagObject[]
    feeds: FeedState[]
    }
    export type FeedState = {
    id?: number
    name: string
    url: string
    tags: 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 | FeedObject
    action: ActionType
    currentItem?: FeedState | BundleState
    fieldName: BadgeFieldName
    setItem?: Dispatch<SetStateAction<FeedState | BundleState>>
    }) => {
    const color =
    fieldName === BadgeFieldName.tags
    ? `blue`
    : fieldName === BadgeFieldName.feeds
    ? `green`
    : `purple`
    return (
    <div className="inline-block align-middle">
    <span
    className={`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">
    <img
    className="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 above
    export const Question = ({ className }) => (
    <svg
    xmlns="http://www.w3.org/2000/svg"
    fill="none"
    viewBox="0 0 24 24"
    stroke="currentColor"
    className={className}
    >
    <path
    strokeLinecap="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>
    )