Skip to content

Commit

Permalink
feat(web): admin orders page
Browse files Browse the repository at this point in the history
  • Loading branch information
mrevanzak committed Dec 22, 2023
1 parent af09a4f commit bc86b4c
Show file tree
Hide file tree
Showing 8 changed files with 150 additions and 75 deletions.
63 changes: 6 additions & 57 deletions apps/nextjs/src/app/(private)/orders/page.tsx
Original file line number Diff line number Diff line change
@@ -1,64 +1,13 @@
import { Button } from "@/components/ui/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import OrderTable from "@/components/OrderTable";
import { server } from "@/lib/server";

export default async function OrdersPage() {
const data = await server.admin.getOrders.query();

export default function OrdersPage() {
return (
<main className="flex flex-1 flex-col gap-4 p-4 md:gap-8 md:p-6">
<div className="rounded-lg border p-2 shadow-sm">
<Table>
<TableHeader>
<TableRow>
<TableHead className="w-[100px]">Order</TableHead>
<TableHead className="min-w-[150px]">Customer</TableHead>
<TableHead className="hidden md:table-cell">Channel</TableHead>
<TableHead className="hidden md:table-cell">Date</TableHead>
<TableHead className="text-right">Total</TableHead>
<TableHead className="hidden sm:table-cell">Status</TableHead>
<TableHead className="text-right">Actions</TableHead>
</TableRow>
</TableHeader>
<TableBody>
<TableRow>
<TableCell className="font-medium">#3210</TableCell>
<TableCell>Olivia Martin</TableCell>
<TableCell className="hidden md:table-cell">
Online Store
</TableCell>
<TableCell className="hidden md:table-cell">
February 20, 2022
</TableCell>
<TableCell className="text-right">$42.25</TableCell>
<TableCell className="hidden sm:table-cell">Shipped</TableCell>
<TableCell className="text-right">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button size="icon" variant="ghost">
{/* <MoreHorizontalIcon className="h-4 w-4" /> */}
<span className="sr-only">Actions</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem>View order</DropdownMenuItem>
<DropdownMenuItem>Customer details</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</TableCell>
</TableRow>
</TableBody>
</Table>
<OrderTable initialData={data} />
</div>
</main>
);
Expand Down
1 change: 1 addition & 0 deletions apps/nextjs/src/app/providers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export function TRPCReactProvider(props: {
defaultOptions: {
queries: {
staleTime: 5 * 1000,
refetchOnMount: false,
},
},
}),
Expand Down
94 changes: 94 additions & 0 deletions apps/nextjs/src/components/OrderTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
"use client";

import { Button } from "@/components/ui/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { api } from "@/lib/api";
import type { server } from "@/lib/server";
import { cn, rupiahFormatter } from "@/lib/utils";
import { IoEllipsisVerticalSharp } from "react-icons/io5";

export default function OrderTable({
initialData,
}: {
initialData: Awaited<ReturnType<typeof server.admin.getOrders.query>>;
}) {
const { data } = api.admin.getOrders.useQuery(undefined, {
initialData,
});
return (
<Table>
<TableHeader>
<TableRow>
<TableHead className="w-[100px]">Order</TableHead>
<TableHead className="min-w-[150px]">Customer</TableHead>
<TableHead className="hidden md:table-cell">Date</TableHead>
<TableHead className="hidden sm:table-cell">Product</TableHead>
<TableHead className="hidden text-right sm:table-cell">
Total
</TableHead>
<TableHead className="w-12">Status</TableHead>
<TableHead className="text-right">Actions</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{data.map((order) => (
<TableRow key={order.id}>
<TableCell className="line-clamp-1 font-medium">
{order.id}
</TableCell>
<TableCell>{order.buyer.name}</TableCell>
<TableCell className="hidden md:table-cell">
{new Date(order.createdAt).toLocaleDateString()}
</TableCell>
<TableCell className="hidden sm:table-cell">
{order.products.name}
</TableCell>
<TableCell className="hidden text-right sm:table-cell">
{rupiahFormatter(order.totalPrice)}
</TableCell>
<TableCell className="whitespace-nowrap uppercase">
<span
className={cn(
"mr-2 inline-block h-2 w-2 rounded-full",
order.status === "done"
? "bg-green-500"
: order.status === "cancelled"
? "bg-red-500"
: "bg-yellow-500",
)}
/>
{order.status}
</TableCell>
<TableCell className="text-right">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button size="icon" variant="ghost">
<IoEllipsisVerticalSharp />
<span className="sr-only">Actions</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem>View order</DropdownMenuItem>
<DropdownMenuItem>Customer details</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
);
}
2 changes: 1 addition & 1 deletion apps/nextjs/src/lib/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const createContext = cache(async () => {
});
});

export const api = createTRPCClient<AppRouter>({
export const server = createTRPCClient<AppRouter>({
transformer: SuperJSON,
links: [
loggerLink({
Expand Down
18 changes: 14 additions & 4 deletions apps/nextjs/src/lib/utils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
import { clsx } from "clsx"
import type {ClassValue} from "clsx";
import { twMerge } from "tailwind-merge"
import { clsx } from "clsx";
import type { ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";

export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
return twMerge(clsx(inputs));
}

export function rupiahFormatter(value?: number): string {
if (!value) return "";

return new Intl.NumberFormat("id-ID", {
style: "currency",
currency: "IDR",
minimumFractionDigits: 0,
}).format(value);
}
4 changes: 2 additions & 2 deletions packages/api/src/root.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { authRouter } from "./router/auth";
import { adminRouter } from "./router/admin";
import { categoryRouter } from "./router/category";
import { orderRouter } from "./router/orders";
import { productRouter } from "./router/product";
import { userRouter } from "./router/user";
import { createTRPCRouter } from "./trpc";

export const appRouter = createTRPCRouter({
auth: authRouter,
admin: adminRouter,
product: productRouter,
category: categoryRouter,
user: userRouter,
Expand Down
32 changes: 32 additions & 0 deletions packages/api/src/router/admin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { alias } from "drizzle-orm/mysql-core";

import { eq, sql } from "@vivat/db";
import { addresses } from "@vivat/db/schema/addresses";
import { orders } from "@vivat/db/schema/orders";
import { products } from "@vivat/db/schema/products";
import { users } from "@vivat/db/schema/users";

import { adminProcedure, createTRPCRouter } from "../trpc";

export const adminRouter = createTRPCRouter({
getOrders: adminProcedure.query(async ({ ctx }) => {
const seller = alias(users, "seller");

return await ctx.db
.select({
createdAt: orders.createdAt,
id: orders.id,
status: orders.status,
products,
buyer: users,
seller,
totalPrice: orders.totalPrice,
})
.from(orders)
.innerJoin(addresses, eq(orders.addressId, addresses.id))
.innerJoin(users, eq(addresses.userId, users.id))
.innerJoin(products, eq(orders.productId, products.id))
.innerJoin(seller, eq(products.sellerId, seller.id))
.orderBy(sql`(CASE ${orders.status} WHEN 'payment' THEN 0 ELSE 1 END), ${orders.createdAt} DESC`)
}),
});
11 changes: 0 additions & 11 deletions packages/api/src/router/auth.ts

This file was deleted.

0 comments on commit bc86b4c

Please sign in to comment.