Skip to content

Commit

Permalink
Merge pull request #135 from joshtyf/feature/integrate-pagination
Browse files Browse the repository at this point in the history
Integrate pagination (for SRs only)
  • Loading branch information
joshtyf authored May 13, 2024
2 parents b83be15 + 6d6ba06 commit ae6d26c
Show file tree
Hide file tree
Showing 9 changed files with 136 additions and 30 deletions.
7 changes: 4 additions & 3 deletions backend/src/server/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ package server
import "errors"

var (
ErrInternalServerError = errors.New("internal server error")
ErrWrongStepType = errors.New("wrong step type")
ErrJsonParseError = errors.New("unable to parse json request body")
ErrInternalServerError = errors.New("internal server error")
ErrWrongStepType = errors.New("wrong step type")
ErrJsonParseError = errors.New("unable to parse json request body")
ErrPaginationParamsError = errors.New("invalid pagination parameters")

ErrPipelineCreateFail = errors.New("failed to create pipeline")
ErrInvalidPipelineId = errors.New("invalid pipeline id")
Expand Down
46 changes: 39 additions & 7 deletions backend/src/server/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -597,11 +597,27 @@ func handleGetServiceRequestsByUserAndOrganization(logger logger.ServerLogger, c
}
queryFilters.Statuses = strings.Split(statusFilters, ",")
}

logger.Info(fmt.Sprintf("querying for service requests: user_id=%s org_id=%d, query_filters=%v", userId, orgId, queryFilters))
logger.Info(fmt.Sprintf("query: %v", r.URL.Query()))
pageParam, err := extractQueryParam[int](r.URL.Query(), "page", false, 1, integerConverter)
if err != nil {
logger.Error(fmt.Sprintf("unable to extract page from query params: %s", err))
encode(w, r, http.StatusBadRequest, newHandlerError(ErrPaginationParamsError, http.StatusBadRequest))
return
}
pageSizeParam, err := extractQueryParam[int](r.URL.Query(), "page_size", false, 10, integerConverter)
if err != nil {
logger.Error(fmt.Sprintf("unable to extract page_size from query params: %s", err))
encode(w, r, http.StatusBadRequest, newHandlerError(ErrPaginationParamsError, http.StatusBadRequest))
return
}
if pageParam < 1 || pageSizeParam < 1 {
logger.Error(fmt.Sprintf("invalid page or page_size: page=%d, page_size=%d", pageParam, pageSizeParam))
encode(w, r, http.StatusBadRequest, newHandlerError(ErrPaginationParamsError, http.StatusBadRequest))
return
}
logger.Info(fmt.Sprintf("querying for service requests: org_id=%d, query_filters=%v, page=%d, page_size=%d", orgId, queryFilters, pageParam, pageSizeParam))
result, err := database.NewServiceRequest(client).GetAllServiceRequestByOrg(orgId, queryFilters, database.Pagination{
Page: 1, // TODO: implement pagination from query params
PageSize: 10,
Page: pageParam, PageSize: pageSizeParam,
})
if err != nil {
logger.Error(fmt.Sprintf("error encountered while handling API request: %s", err))
Expand Down Expand Up @@ -648,10 +664,26 @@ func handleGetServiceRequestsForAdminByOrganization(logger logger.ServerLogger,
queryFilters.Statuses = strings.Split(statusFilters, ",")
}

logger.Info(fmt.Sprintf("querying for service requests: org_id=%d, query_filters=%v", orgId, queryFilters))
pageParam, err := extractQueryParam[int](r.URL.Query(), "page", false, 1, integerConverter)
if err != nil {
logger.Error(fmt.Sprintf("unable to extract page from query params: %s", err))
encode(w, r, http.StatusBadRequest, newHandlerError(ErrPaginationParamsError, http.StatusBadRequest))
return
}
pageSizeParam, err := extractQueryParam[int](r.URL.Query(), "page_size", false, 10, integerConverter)
if err != nil {
logger.Error(fmt.Sprintf("unable to extract page_size from query params: %s", err))
encode(w, r, http.StatusBadRequest, newHandlerError(ErrPaginationParamsError, http.StatusBadRequest))
return
}
if pageParam < 1 || pageSizeParam < 1 {
logger.Error(fmt.Sprintf("invalid page or page_size: page=%d, page_size=%d", pageParam, pageSizeParam))
encode(w, r, http.StatusBadRequest, newHandlerError(ErrPaginationParamsError, http.StatusBadRequest))
return
}
logger.Info(fmt.Sprintf("querying for service requests: org_id=%d, query_filters=%v, page=%d, page_size=%d", orgId, queryFilters, pageParam, pageSizeParam))
result, err := database.NewServiceRequest(client).GetAllServiceRequestByOrg(orgId, queryFilters, database.Pagination{
Page: 1, // TODO: implement pagination from query params
PageSize: 10,
Page: pageParam, PageSize: pageSizeParam,
})
if err != nil {
logger.Error(fmt.Sprintf("error encountered while handling API request: %s", err))
Expand Down
4 changes: 3 additions & 1 deletion flowforge_api_bruno/service_request/all service requests.bru
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ meta {
}

get {
url: {{HOST}}/service_request?org_id=1
url: {{HOST}}/service_request?org_id=1&page=1&page_size=10
body: none
auth: inherit
}

query {
org_id: 1
page: 1
page_size: 10
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { FormFieldType, JsonFormComponents } from "@/types/json-form-components"
import { StepStatus } from "@/types/pipeline"
import { ServiceRequest, ServiceRequestStatus } from "@/types/service-request"
import { useQuery } from "@tanstack/react-query"
import { useMemo } from "react"

const DUMMY_PIPELINE_FORM: JsonFormComponents = {
fields: [
Expand Down Expand Up @@ -249,28 +250,43 @@ const DUMMY_SERVICE_REQUESTS: ServiceRequest[] = [
},
},
]
interface UseServiceRequestProps {
page: number
pageSize: number
}

const useServiceRequests = () => {
const useServiceRequests = ({ page, pageSize }: UseServiceRequestProps) => {
const { organizationId } = useOrganizationId()
const { isLoading, data } = useQuery({
queryKey: ["user_service_requests"],
queryKey: ["user_service_requests", page, pageSize],
queryFn: () => {
return getAllServiceRequest(organizationId).catch((err) => {
console.error(err)
toast({
title: "Fetching Service Requests Error",
description:
"Failed to fetch Service Requests for user. Please try again later.",
variant: "destructive",
})
})
return getAllServiceRequest(organizationId, page, pageSize).catch(
(err) => {
console.error(err)
toast({
title: "Fetching Service Requests Error",
description:
"Failed to fetch Service Requests for user. Please try again later.",
variant: "destructive",
})
}
)
},
refetchInterval: 2000,
})

const noOfPages = useMemo(
() =>
data?.metadata.total_count
? Math.ceil(data?.metadata.total_count / pageSize)
: undefined,
[data, pageSize]
)

return {
response: data,
isLoading,
noOfPages,
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,14 @@ import { DataTable } from "@/components/data-table/data-table"
import HeaderAccessory from "@/components/ui/header-accessory"
import useServiceRequests from "./_hooks/use-service-requests"
import { columns } from "./columns"
import { usePagination } from "@/hooks/use-pagination"

export default function ServiceRequestDashboardPage() {
const { response } = useServiceRequests()
const { onPaginationChange, pagination } = usePagination()
const { response, noOfPages } = useServiceRequests({
page: pagination.pageIndex + 1, // API is 1-based
pageSize: pagination.pageSize,
})

return (
<div className="flex flex-col justify-start py-10">
Expand All @@ -15,7 +20,13 @@ export default function ServiceRequestDashboardPage() {
<p className="font-bold text-3xl pt-5">Your Service Requests</p>
</div>
<div className="py-10">
<DataTable columns={columns} data={response?.data} />
<DataTable
columns={columns}
data={response?.data}
pageCount={noOfPages}
onPaginationChange={onPaginationChange}
pagination={pagination}
/>
</div>
</div>
)
Expand Down
13 changes: 12 additions & 1 deletion frontend/src/components/data-table/data-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

import {
ColumnDef,
PaginationState,
SortingState,
Updater,
flexRender,
getCoreRowModel,
getFilteredRowModel,
Expand All @@ -25,11 +27,17 @@ import { useMemo, useState } from "react"
interface DataTableProps<TData, TValue> {
columns: ColumnDef<TData, TValue>[]
data?: TData[] | void
onPaginationChange?: (pagination: Updater<PaginationState>) => void
pagination?: PaginationState
pageCount?: number
}

export function DataTable<TData, TValue>({
columns,
data,
onPaginationChange,
pagination,
pageCount,
}: DataTableProps<TData, TValue>) {
const [sorting, setSorting] = useState<SortingState>([])

Expand All @@ -47,8 +55,11 @@ export function DataTable<TData, TValue>({
getSortedRowModel: getSortedRowModel(),
state: {
sorting,
pagination,
},
getPaginationRowModel: getPaginationRowModel(),
manualPagination: true,
pageCount: pageCount,
onPaginationChange,
})

return (
Expand Down
12 changes: 9 additions & 3 deletions frontend/src/components/layouts/data-table-pagination.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@ import {
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
import { ChevronRight, ChevronsLeft, ChevronsRight } from "lucide-react"
import {
ChevronLeft,
ChevronRight,
ChevronsLeft,
ChevronsRight,
} from "lucide-react"

interface DataTablePaginationProps<TData> {
table: Table<TData>
Expand All @@ -33,7 +38,8 @@ export function DataTablePagination<TData>({
<SelectValue placeholder={table.getState().pagination.pageSize} />
</SelectTrigger>
<SelectContent>
{[10, 20, 30, 40, 50].map((pageSize) => (
{/* TODO: remove 1 per page option once testing fully done */}
{[1, 10, 20, 30, 40, 50].map((pageSize) => (
<SelectItem key={pageSize} value={`${pageSize}`}>
{pageSize}
</SelectItem>
Expand Down Expand Up @@ -62,7 +68,7 @@ export function DataTablePagination<TData>({
disabled={!table.getCanPreviousPage()}
>
<span className="sr-only">Go to previous page</span>
<ChevronsLeft className="h-4 w-4" />
<ChevronLeft className="h-4 w-4" />
</Button>
<Button
variant="outline"
Expand Down
17 changes: 17 additions & 0 deletions frontend/src/hooks/use-pagination.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { PaginationState } from "@tanstack/react-table"
import { useState } from "react"

export function usePagination() {
const [pagination, setPagination] = useState<PaginationState>({
pageSize: 10,
pageIndex: 0,
})
const { pageSize, pageIndex } = pagination

return {
limit: pageSize,
onPaginationChange: setPagination,
pagination,
skip: pageSize * pageIndex,
}
}
14 changes: 12 additions & 2 deletions frontend/src/lib/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,24 @@ export async function createServiceRequest(
})
}

export async function getAllServiceRequest(organizationId: number): Promise<{
export async function getAllServiceRequest(
organizationId: number,
page?: number,
pageSize?: number
): Promise<{
data: ServiceRequest[]
metadata: {
total_count: number
}
}> {
return apiClient
.get("/service_request", { params: { org_id: organizationId } })
.get("/service_request", {
params: {
org_id: organizationId,
page: page ?? 1,
page_size: pageSize ?? 10,
},
})
.then((res) => res.data)
}

Expand Down

0 comments on commit ae6d26c

Please sign in to comment.