Skip to content

Commit

Permalink
create a route to show all skill requests and pending skill requests (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
yesyash authored Aug 30, 2024
1 parent 95e04ce commit ad89d7e
Show file tree
Hide file tree
Showing 12 changed files with 189 additions and 34 deletions.
6 changes: 4 additions & 2 deletions src/api/skills/skills.api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ import { client } from "@/utils/client"

import * as Dto from "./skills.dto"
export class SkillsApi {
public static async getAllPendingSkillRequests(): Promise<Dto.GetAllPendingSkillRequestsResDto> {
const { data } = await client.get("/v1/skills/requests")
public static async getAllPendingSkillRequests(
params?: Dto.GetAllPendingSkillsReqDto
): Promise<Dto.GetAllPendingSkillsResDto> {
const { data } = await client.get("/v1/skills/requests", { params })
return data
}

Expand Down
7 changes: 6 additions & 1 deletion src/api/skills/skills.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,15 @@ export type SkillRequests = {
skillId: number
skillName: string
endorseId: string
status: UserSkillStatusEnum
endorsements: TEndorsement[]
}

export type GetAllPendingSkillRequestsResDto = {
export type GetAllPendingSkillsReqDto = {
status?: UserSkillStatusEnum
}

export type GetAllPendingSkillsResDto = {
requests: SkillRequests[]
users: MinimalUser[]
}
Expand Down
6 changes: 3 additions & 3 deletions src/api/skills/skills.enum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ export enum SkillTypeEnum {
}

export enum UserSkillStatusEnum {
APPROVED,
REJECTED,
PENDING,
APPROVED = "APPROVED",
REJECTED = "REJECTED",
PENDING = "PENDING",
}
2 changes: 1 addition & 1 deletion src/components/small-screen-warning.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
// ---
export const SmallScreenWarning = () => {
return (
<div className="fixed left-0 top-0 z-[10000] flex h-screen w-full flex-col items-center justify-center bg-gray-900 p-4 text-center xl:hidden">
<div className="fixed left-0 top-0 z-[10000] flex h-screen w-full flex-col items-center justify-center bg-gray-900 p-4 text-center md:hidden">
<h3 className="text-xl font-bold text-gray-50">Small Screen detected</h3>
<p className="text-gray-400">This application is not supported on devices below 1280px</p>
</div>
Expand Down
36 changes: 36 additions & 0 deletions src/components/tabs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import Link from "next/link"
import { usePathname } from "next/navigation"

import { cn } from "@/utils/classname"

export type TTab = {
order: number
label: string
href: string
}

type TabsProps = {
tabs: TTab[]
}

export const Tabs = ({ tabs }: TabsProps) => {
const pathname = usePathname()
const sortedTabs = tabs.sort((a, b) => a.order - b.order)

return (
<div className="flex w-max items-center rounded-lg bg-gray-100 p-1">
{sortedTabs.map((tab) => (
<Link
href={tab.href}
key={tab.href}
className={cn(
"flex h-8 items-center justify-center rounded px-4 text-xs font-medium",
pathname === tab.href ? "bg-white" : "bg-transparent"
)}
>
{tab.label}
</Link>
))}
</div>
)
}
1 change: 1 addition & 0 deletions src/modules/pending-requests/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { PendingRequests } from "./pending-requests"
72 changes: 72 additions & 0 deletions src/modules/pending-requests/pending-requests.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { useQuery } from "@tanstack/react-query"
import Link from "next/link"

import { SkillsApi } from "@/api/skills"
import { UserSkillStatusEnum } from "@/api/skills/skills.enum"
import { Button } from "@/components/button"
import { PageError } from "@/components/page-error"
import { Shimmer } from "@/components/shimmer"
import { RootLayout } from "@/layouts/root-layout"
import { ROUTES } from "@/routes"
import { useGlobalStore } from "@/store/global-store"

import { RequestsTable } from "../requests/components/requests-table"
import { RequestsTabs } from "../requests/components/requests-tabs"

export const PendingRequests = () => {
const isSuperUser = useGlobalStore((store) => store.user?.roles.super_user)

const { data, isLoading, isError } = useQuery({
queryKey: ["SkillsApi.getAllPendingSkillRequests"],
queryFn: () => SkillsApi.getAllPendingSkillRequests({ status: UserSkillStatusEnum.PENDING }),
})

if (isLoading) {
return (
<RootLayout>
<div className="mx-auto w-full max-w-screen-xl flex-1 space-y-4 p-6 xl:py-10">
<Shimmer />
<Shimmer />
<Shimmer />
<Shimmer />
</div>
</RootLayout>
)
}

if (isError) {
return (
<RootLayout>
<PageError />
</RootLayout>
)
}

return (
<RootLayout>
<div className="w-full p-6">
<div className="mx-auto max-w-screen-lg rounded-lg border border-gray-100 bg-white p-6">
<div className="pb-6">
<RequestsTabs />
</div>

<div className="flex items-center gap-4 pb-6">
<h1 className="flex-1 text-2xl font-semibold text-gray-800">Pending requests</h1>

{isSuperUser && (
<Button asChild size="xs" variant="ghost" className="text-gray-500">
<Link href={ROUTES.skills.create}>Create Skill</Link>
</Button>
)}

<Button asChild size="xs" variant="secondary">
<Link href={ROUTES.endorsements.create}>Create Endorsement</Link>
</Button>
</div>

<RequestsTable data={data ?? { requests: [], users: [] }} />
</div>
</div>
</RootLayout>
)
}
69 changes: 45 additions & 24 deletions src/modules/requests/components/requests-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import toast from "react-hot-toast"
import { MinimalUser } from "@/api/common/minimal-user.types"
import { TUserDetails } from "@/api/common/user.types"
import { SkillsApi } from "@/api/skills"
import { GetAllPendingSkillRequestsResDto } from "@/api/skills/skills.dto"
import { GetAllPendingSkillsResDto } from "@/api/skills/skills.dto"
import { UserSkillStatusEnum } from "@/api/skills/skills.enum"
import { Button } from "@/components/button"
import { useGlobalStore } from "@/store/global-store"
Expand All @@ -21,7 +21,7 @@ type CellProps = {
}

const Cell = ({ className, children }: CellProps) => {
return <div className={cn("px-2 text-sm font-normal", className)}>{children}</div>
return <div className={cn("px-4 text-sm font-normal", className)}>{children}</div>
}

type CommonProps = {
Expand All @@ -36,19 +36,24 @@ const Th = ({ children }: CommonProps) => {
)
}

const Td = ({ children }: CommonProps) => {
type TdProps = CommonProps & {
classNames?: Partial<{ td: string; cell: string }>
}

const Td = ({ children, classNames }: TdProps) => {
return (
<td className="text-sm font-normal text-gray-800">
<Cell className="flex h-11 items-center">{children}</Cell>
<td className={cn("text-sm font-normal text-gray-800", classNames?.td)}>
<Cell className={cn("flex h-11 items-center", classNames?.cell)}>{children}</Cell>
</td>
)
}

const TableHeader = () => {
return (
<thead className="border-y border-gray-100">
<thead className="border-b border-gray-200 bg-gray-50">
<Th>Name</Th>
<Th>Skill</Th>
<Th>Skill status</Th>
<Th>Endorsements</Th>
<Th />
</thead>
Expand All @@ -58,9 +63,10 @@ const TableHeader = () => {
type RequestActionsProps = {
skillId: number
endorseId: string
skillStatus: UserSkillStatusEnum
}

const RequestActions = ({ skillId, endorseId }: RequestActionsProps) => {
const RequestActions = ({ skillId, endorseId, skillStatus }: RequestActionsProps) => {
const queryClient = useQueryClient()

const approveRequestsMutation = useMutation({
Expand All @@ -87,6 +93,10 @@ const RequestActions = ({ skillId, endorseId }: RequestActionsProps) => {
},
})

if (skillStatus !== UserSkillStatusEnum.PENDING) {
return <div className="text-gray-500">--</div>
}

return (
<div className="flex items-center gap-2">
<Button
Expand Down Expand Up @@ -115,11 +125,12 @@ type TFormattedData = {
skillId: number
skillName: string
endorse: TUserDetails
skillStatus: UserSkillStatusEnum
endorsements: TFormattedEndorsement[]
}

type RequestsTableProps = {
data: GetAllPendingSkillRequestsResDto
data: GetAllPendingSkillsResDto
}

export const RequestsTable = ({ data }: RequestsTableProps) => {
Expand Down Expand Up @@ -147,6 +158,7 @@ export const RequestsTable = ({ data }: RequestsTableProps) => {
})),
skillId: request.skillId,
skillName: request.skillName,
skillStatus: request.status,
}))

if (!formattedData.length) {
Expand All @@ -158,21 +170,30 @@ export const RequestsTable = ({ data }: RequestsTableProps) => {
}

return (
<table className="w-full text-left">
<TableHeader />

{formattedData.map((request, index) => (
<tr key={index}>
<Td>{request.endorse.name}</Td>
<Td>{request.skillName}</Td>
<Td>
<EndorsementsGroup endorsements={request.endorsements} />
</Td>
<Td>
{isSuperUser && <RequestActions skillId={request.skillId} endorseId={request.endorse.id} />}
</Td>
</tr>
))}
</table>
<div className="overflow-hidden rounded-lg border border-gray-200">
<table className="w-full text-left">
<TableHeader />

{formattedData.map((request, index) => (
<tr key={index} className="h-12 border-b border-gray-200 last:border-none">
<Td>{request.endorse.name}</Td>
<Td>{request.skillName}</Td>
<Td classNames={{ cell: "capitalize" }}>{request.skillStatus.toLowerCase()}</Td>
<Td>
<EndorsementsGroup endorsements={request.endorsements} />
</Td>
<Td>
{isSuperUser && (
<RequestActions
skillId={request.skillId}
endorseId={request.endorse.id}
skillStatus={request.skillStatus}
/>
)}
</Td>
</tr>
))}
</table>
</div>
)
}
10 changes: 10 additions & 0 deletions src/modules/requests/components/requests-tabs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Tabs, TTab } from "@/components/tabs"

const tabs: TTab[] = [
{ order: 0, label: "All requests", href: "/requests" },
{ order: 1, label: "Pending requests", href: "/requests/pending" },
]

export const RequestsTabs = () => {
return <Tabs tabs={tabs} />
}
11 changes: 8 additions & 3 deletions src/modules/requests/requests.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,20 @@ import { ROUTES } from "@/routes"
import { useGlobalStore } from "@/store/global-store"

import { RequestsTable } from "./components/requests-table"
import { RequestsTabs } from "./components/requests-tabs"

export const Requests = () => {
const isSuperUser = useGlobalStore((store) => store.user?.roles.super_user)

const { data, isLoading, isError } = useQuery({
queryKey: ["SkillsApi.getAllPendingSkillRequests"],
queryFn: SkillsApi.getAllPendingSkillRequests,
queryFn: () => SkillsApi.getAllPendingSkillRequests(),
})

if (isLoading) {
return (
<RootLayout>
<div className="flex-1 space-y-4 p-6">
<div className="mx-auto w-full max-w-screen-xl flex-1 space-y-4 p-6 xl:py-10">
<Shimmer />
<Shimmer />
<Shimmer />
Expand All @@ -44,8 +45,12 @@ export const Requests = () => {
<RootLayout>
<div className="w-full p-6">
<div className="mx-auto max-w-screen-lg rounded-lg border border-gray-100 bg-white p-6">
<div className="pb-6">
<RequestsTabs />
</div>

<div className="flex items-center gap-4 pb-6">
<h1 className="flex-1 text-2xl font-semibold text-gray-800">Requests board</h1>
<h1 className="flex-1 text-2xl font-semibold text-gray-800">All requests</h1>

{isSuperUser && (
<Button asChild size="xs" variant="ghost" className="text-gray-500">
Expand Down
File renamed without changes.
3 changes: 3 additions & 0 deletions src/pages/requests/pending.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { PendingRequests } from "@/modules/pending-requests"

export default PendingRequests

0 comments on commit ad89d7e

Please sign in to comment.