Skip to content

Commit

Permalink
Adding pagination for catalog-api and Adding catalogs Page for dashbo…
Browse files Browse the repository at this point in the history
…ard app (#151)
  • Loading branch information
jurabek authored Jul 12, 2024
1 parent dd372ae commit 8ae58a7
Show file tree
Hide file tree
Showing 14 changed files with 194 additions and 74 deletions.
36 changes: 28 additions & 8 deletions src/backend/services/catalog-api/src/handlers/catalog.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use crate::models::catalog::{Catalog, CatalogRequest, CatalogResponse, NewCatalog, UpdateCatalog};
use crate::models::catalog::{Catalog, CatalogItem, CatalogRequest, CatalogsResponse, NewCatalog, UpdateCatalog};

use crate::db::db::establish_connection;
use crate::schema::{self};
use bigdecimal::ToPrimitive;
use diesel::dsl::Limit;
use diesel::prelude::*;
use opentelemetry::global::{self, ObjectSafeSpan};
use opentelemetry::trace::Tracer;
Expand Down Expand Up @@ -72,20 +73,31 @@ pub fn delete(catalog_id: i32) {
}

#[openapi]
#[get("/all?<category_name>", format = "json")]
pub fn get_catalogs(category_name: Option<String>) -> Result<Json<Vec<CatalogResponse>>> {
#[get("/all?<category_name>&<offset>&<limit>", format = "json")]
pub fn get_catalogs(
category_name: Option<String>,
offset: Option<i64>,
limit: Option<i64>,
) -> Result<Json<CatalogsResponse>> {
use schema::catalog::dsl::*;
let connection = &mut establish_connection();

let mut query = catalog.into_boxed();
if let Some(ref cat_name) = category_name {
query = query.filter(category.eq(cat_name));
}
let res = query

let offset = offset.unwrap_or(0);
let limit = limit.unwrap_or(20);
let total = catalog.select(diesel::dsl::count(id)).first(connection).expect("failed to get catalogs count");

let catalog_items: Vec<CatalogItem> = query
.offset(offset)
.limit(limit)
.load::<Catalog>(connection)
.expect("failed to loading catalogs")
.into_iter()
.map(|c| CatalogResponse {
.map(|c| CatalogItem {
id: c.id,
name: c.name,
description: c.description,
Expand All @@ -95,12 +107,20 @@ pub fn get_catalogs(category_name: Option<String>) -> Result<Json<Vec<CatalogRes
category: c.category,
})
.collect();
return Ok(Json(res));

let result = CatalogsResponse {
total,
catalog_items,
offset,
limit,
};

return Ok(Json(result));
}

#[openapi]
#[get("/<catalog_id>", format = "json")]
pub fn get_catalog(catalog_id: i32) -> Result<Json<CatalogResponse>> {
pub fn get_catalog(catalog_id: i32) -> Result<Json<CatalogItem>> {
use schema::catalog::dsl::*;

let connection = &mut establish_connection();
Expand All @@ -109,7 +129,7 @@ pub fn get_catalog(catalog_id: i32) -> Result<Json<CatalogResponse>> {
.first::<Catalog>(connection)
.expect("failed to loading catalogs");

let res = CatalogResponse {
let res = CatalogItem {
id: c.id,
name: c.name,
description: c.description,
Expand Down
11 changes: 10 additions & 1 deletion src/backend/services/catalog-api/src/models/catalog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ pub struct CatalogRequest {

#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(crate = "rocket::serde")]
pub struct CatalogResponse {
pub struct CatalogItem {
pub id: i32,
pub name: String,
pub description: Option<String>,
Expand All @@ -65,3 +65,12 @@ pub struct CatalogResponse {
pub currency: String,
pub category: String,
}

#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(crate = "rocket::serde")]
pub struct CatalogsResponse {
pub catalog_items: Vec<CatalogItem>,
pub total: i64,
pub offset: i64,
pub limit: i64,
}
2 changes: 1 addition & 1 deletion src/backend/services/web-app/src/app/foods/foods.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export const FoodsPage = ({
</div>
<div className="order-last min-h-screen w-full md:order-none">
<div className="flex flex-wrap justify-center">
{foodItems.map((item) => (
{foodItems.catalog_items.map((item) => (
<Item foodItem={item} key={item.id} />
))}
</div>
Expand Down
5 changes: 3 additions & 2 deletions src/backend/services/web-app/src/lib/fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,15 @@ async function fetchItems(apiUrl: string) {
}

const items: FoodItems = FoodItemsScheme.parse(await res.json());
const updatedItems = items.map((item) => {
const catalog_items = items.catalog_items.map((item) => {
return {
...item,
image: process.env.INTERNAL_API_BASE_URL + item.image
};
});

return updatedItems;

return {...items, catalog_items}
}

export async function fetchCategories(): Promise<Categories> {
Expand Down
7 changes: 6 additions & 1 deletion src/backend/services/web-app/src/lib/types/food-item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@ export const FoodItemScheme = z.object({
currency: z.string()
});

export const FoodItemsScheme = z.array(FoodItemScheme);
export const FoodItemsScheme = z.object({
catalog_items: z.array(FoodItemScheme),
total: z.number(),
offset: z.number(),
limit: z.number()
});

export const CategoriesScheme = z.array(
z.object({
Expand Down
12 changes: 12 additions & 0 deletions src/backend/services/web.admin/dashboard-app/next.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,18 @@ const nextConfig = {
protocol: "https",
hostname: "*.public.blob.vercel-storage.com",
},
{
protocol: 'http',
hostname: 'localhost'
},
{
protocol: 'http',
hostname: 'traefik'
},
{
protocol: 'http',
hostname: 'host.docker.internal'
}
],
},
};
Expand Down
19 changes: 14 additions & 5 deletions src/backend/services/web.admin/dashboard-app/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions src/backend/services/web.admin/dashboard-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
},
"dependencies": {
"@heroicons/react": "^2.1.1",
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-dialog": "^1.1.1",
"@radix-ui/react-dropdown-menu": "^2.1.1",
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-slot": "^1.1.0",
"@radix-ui/react-tabs": "^1.1.0",
"@radix-ui/react-tooltip": "^1.1.2",
Expand All @@ -24,10 +24,11 @@
"react": "^18",
"react-dom": "^18",
"tailwind-merge": "^2.2.1",
"tailwindcss-animate": "^1.0.7"
"tailwindcss-animate": "^1.0.7",
"zod": "^3.23.8"
},
"devDependencies": {
"@types/node": "^20",
"@types/node": "^20.14.10",
"@types/react": "^18",
"@types/react-dom": "^18",
"autoprefixer": "^10.0.1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { File, PlusCircle } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { ProductsTable } from './products-table';
import { getProducts } from '@/lib/db';
import { fetchCatalogs } from '@/lib/fetch';


export default async function ProductsPage({
Expand All @@ -12,9 +12,11 @@ export default async function ProductsPage({
}) {
const search = searchParams.q ?? '';
const offset = searchParams.offset ?? 0;
const { products, newOffset, totalProducts } = await getProducts(
const productsPerPage = 10;
const { products, newOffset, totalProducts } = await fetchCatalogs(
search,
Number(offset)
Number(offset),
productsPerPage,
);

return (
Expand Down Expand Up @@ -48,6 +50,7 @@ export default async function ProductsPage({
products={products}
offset={newOffset ?? 0}
totalProducts={totalProducts}
productsPerPage={productsPerPage}
/>
</TabsContent>
</Tabs>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import {
} from '@/components/ui/dropdown-menu';
import { MoreHorizontal } from 'lucide-react';
import { TableCell, TableRow } from '@/components/ui/table';
import { SelectProduct } from '@/lib/db';
import { deleteProduct } from './actions';
import { SelectProduct } from '@/lib/fetch';

export function Product({ product }: { product: SelectProduct }) {
return (
Expand All @@ -32,7 +32,7 @@ export function Product({ product }: { product: SelectProduct }) {
</Badge>
</TableCell>
<TableCell className="hidden md:table-cell">{`$${product.price}`}</TableCell>
<TableCell className="hidden md:table-cell">{product.stock}</TableCell>
<TableCell className="hidden md:table-cell">{product.currency}</TableCell>
<TableCell className="hidden md:table-cell">
{product.availableAt.toLocaleDateString()}
</TableCell>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,34 +19,34 @@ import { Product } from './product';
import { useRouter } from 'next/navigation';
import { ChevronLeft, ChevronRight } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { SelectProduct } from '@/lib/db';
import { SelectProduct } from '@/lib/fetch';

export function ProductsTable({
products,
offset,
totalProducts
totalProducts,
productsPerPage,
}: {
products: SelectProduct[];
offset: number;
totalProducts: number;
productsPerPage: number;
}) {
let router = useRouter();
let productsPerPage = 5;

function prevPage() {
router.back();
}

function nextPage() {
router.push(`/?offset=${offset}`, { scroll: false });
router.push(`/products?offset=${offset}`, { scroll: false });
}

return (
<Card>
<CardHeader>
<CardTitle>Products</CardTitle>
<CardDescription>
Manage your products and view their sales performance.
Manage your products.
</CardDescription>
</CardHeader>
<CardContent>
Expand All @@ -59,9 +59,7 @@ export function ProductsTable({
<TableHead>Name</TableHead>
<TableHead>Status</TableHead>
<TableHead className="hidden md:table-cell">Price</TableHead>
<TableHead className="hidden md:table-cell">
Total Sales
</TableHead>
<TableHead className="hidden md:table-cell">Currency</TableHead>
<TableHead className="hidden md:table-cell">Created at</TableHead>
<TableHead>
<span className="sr-only">Actions</span>
Expand Down
39 changes: 0 additions & 39 deletions src/backend/services/web.admin/dashboard-app/src/lib/db.ts

This file was deleted.

Loading

0 comments on commit 8ae58a7

Please sign in to comment.