User ConsoleNew Table

Adding Table Views

ShipAny has a built-in table view component that makes it easy to render a table view for displaying various types of data.

For example, user lists / order lists / point records, etc.

Creating a New Table View

The following steps demonstrate how to quickly render a table view in the user center to display a list of user payment orders.

  1. First, implement a method in the model layer to get the user’s order list from the database
models/order.ts
export async function getOrdersByUserUuid(
  user_uuid: string
): Promise<Order[] | undefined> {
  const now = new Date().toISOString();
  const supabase = getSupabaseClient();
  const { data, error } = await supabase
    .from("orders")
    .select("*")
    .eq("user_uuid", user_uuid)
    .eq("status", "paid")
    .order("created_at", { ascending: false });
 
  if (error) {
    return undefined;
  }
 
  return data;
}
  1. Create a new page in the user center module to get the logged-in user information

The getUserUuid method can get the current logged-in user’s uuid from the session.

If the user is not logged in, they will be redirected to the login page.

app/[locale]/(default)/(console)/my-orders/page.tsx
import { getUserUuid } from "@/services/user";
import { redirect } from "next/navigation";
 
export default async function MyOrdersPage() {
  const user_uuid = await getUserUuid();
 
  const callbackUrl = `${process.env.NEXT_PUBLIC_WEB_URL}/my-orders`;
  if (!user_uuid) {
    redirect(`/auth/signin?callbackUrl=${encodeURIComponent(callbackUrl)}`);
  }
 
  return <div>My Orders With User UUID: {user_uuid}</div>;
}

Preview the new page to see the logged-in user’s uuid.

user-console-new-page-show

  1. Define the table view headers

In the page file:

Define the headers to display columns: TableColumn[],

Import the table view component: import TableSlot from "@/components/console/slots/table";

Display the headers: return <TableSlot title={t("my_orders.title")} columns={columns} data={[]} />;

Complete code:

app/[locale]/(default)/(console)/my-orders/page.tsx
import { TableColumn } from "@/types/blocks/table";
import TableSlot from "@/components/console/slots/table";
import { getTranslations } from "next-intl/server";
import { getUserUuid } from "@/services/user";
import moment from "moment";
import { redirect } from "next/navigation";
 
export default async function MyOrdersPage() {
  const t = await getTranslations();
 
  const user_uuid = await getUserUuid();
 
  const callbackUrl = `${process.env.NEXT_PUBLIC_WEB_URL}/my-orders`;
  if (!user_uuid) {
    redirect(`/auth/signin?callbackUrl=${encodeURIComponent(callbackUrl)}`);
  }
 
  const columns: TableColumn[] = [
    { name: "order_no", title: t("my_orders.table.order_no") },
    { name: "paid_email", title: t("my_orders.table.email") },
    { name: "product_name", title: t("my_orders.table.product_name") },
    {
      name: "amount",
      title: t("my_orders.table.amount"),
      callback: (item: any) =>
        `${item.currency.toUpperCase() === "CNY" ? "¥" : "$"} ${
          item.amount / 100
        }`,
    },
    {
      name: "paid_at",
      title: t("my_orders.table.paid_at"),
      callback: (item: any) =>
        moment(item.paid_at).format("YYYY-MM-DD HH:mm:ss"),
    },
  ];
 
  return <TableSlot title={t("my_orders.title")} columns={columns} data={[]} />;
}

Preview effect:

user-console-table-column

  1. Display data in the table view

Call the data reading function implemented in step 1 and pass the retrieved data to the table view component for display.

Complete code:

app/[locale]/(default)/(console)/my-orders/page.tsx
import { getOrdersByPaidEmail, getOrdersByUserUuid } from "@/models/order";
import { getUserEmail, getUserUuid } from "@/services/user";
 
import { TableColumn } from "@/types/blocks/table";
import TableSlot from "@/components/console/slots/table";
import { Table as TableSlotType } from "@/types/slots/table";
import { getTranslations } from "next-intl/server";
import moment from "moment";
import { redirect } from "next/navigation";
 
export default async function () {
  const t = await getTranslations();
 
  const user_uuid = await getUserUuid();
  const user_email = await getUserEmail();
 
  const callbackUrl = `${process.env.NEXT_PUBLIC_WEB_URL}/my-orders`;
  if (!user_uuid) {
    redirect(`/auth/signin?callbackUrl=${encodeURIComponent(callbackUrl)}`);
  }
 
  let orders = await getOrdersByUserUuid(user_uuid);
  if (!orders || orders.length === 0) {
    orders = await getOrdersByPaidEmail(user_email);
  }
 
  const columns: TableColumn[] = [
    { name: "order_no", title: t("my_orders.table.order_no") },
    { name: "paid_email", title: t("my_orders.table.email") },
    { name: "product_name", title: t("my_orders.table.product_name") },
    {
      name: "amount",
      title: t("my_orders.table.amount"),
      callback: (item: any) =>
        `${item.currency.toUpperCase() === "CNY" ? "¥" : "$"} ${
          item.amount / 100
        }`,
    },
    {
      name: "paid_at",
      title: t("my_orders.table.paid_at"),
      callback: (item: any) =>
        moment(item.paid_at).format("YYYY-MM-DD HH:mm:ss"),
    },
  ];
 
  const table: TableSlotType = {
    title: t("my_orders.title"),
    description: t("my_orders.description"),
    toolbar: {
      items: [
        {
          title: t("my_orders.read_docs"),
          icon: "RiBookLine",
          url: "https://docs.shipany.ai",
          target: "_blank",
          variant: "outline",
        },
        {
          title: t("my_orders.join_discord"),
          icon: "RiDiscordFill",
          url: "https://discord.gg/HQNnrzjZQS",
          target: "_blank",
        },
      ],
    },
    columns: columns,
    data: orders,
    empty_message: t("my_orders.no_orders"),
  };
 
  return <TableSlot {...table} />;
}

Preview effect:

user-orders