Files
tm_panel/docs/client/bp-panel-list-sorting.md
AmirReza Jamali eb41f9b7b2 feat(docs): migrate and reorganize frontend guides for BP panel and file uploads
- Deleted outdated documentation files for BP panel list sorting and Filestore frontend guide.
- Created new documentation files for BP panel list sorting and Filestore frontend guide in the client directory.
- Updated the structure and content to enhance clarity and accessibility for frontend integration with file upload and profile image APIs.
- Ensured consistency in documentation style and improved examples for better developer experience.
2026-05-31 15:18:37 +03:30

7.5 KiB

BP Panel — List sorting (frontend)

Guide for Business Partner (BP) list screens under /api/v1. Use these query parameters to control item order on paginated GET endpoints.

This does not apply to Kanban board card order (see board-api.md — cards use order on move/reorder APIs).


Quick reference

Parameter Type Default Allowed values
sort_by string created_at MongoDB field name (see per-endpoint tables)
sort_order string desc asc or desc

Example (newest tenders first — also the default if omitted):

GET /api/v1/tenders?limit=20&sort_by=created_at&sort_order=desc
Authorization: Bearer <customer_access_token>

Example (soonest deadline first):

GET /api/v1/tenders?limit=20&sort_by=tender_deadline&sort_order=asc

Authentication

All BP list endpoints below require a customer JWT from:

POST /api/v1/profile/login
Authorization: Bearer <access_token>

Base URL (local): http://localhost:8081/api/v1


How sorting works

  1. sort_by must match the BSON field name stored in MongoDB (snake_case), not the JSON display name in the UI.
  2. sort_order
    • desc — highest / latest values first (default).
    • asc — lowest / earliest values first.
  3. If you omit both parameters, the API uses sort_by=created_at and sort_order=desc.
  4. Lists use keyset (cursor) pagination. When requesting page 2+ with cursor, you must keep the same sort_by, sort_order, limit, and filters as the request that produced meta.next_cursor. Changing sort mid-pagination returns 400 (cursor does not match requested sort).
  5. Do not send cursor and offset together — the API returns 400.

Pagination parameters (used with sorting)

Parameter Default Max Notes
limit 10 100 Page size
offset 0 First page only; prefer cursor for deep pages
cursor Copy from meta.next_cursor on the previous response

Response metadata

Sorted list responses include meta on the envelope:

{
  "success": true,
  "message": "Tenders retrieved successfully",
  "data": { "tenders": [] },
  "meta": {
    "total": 240,
    "limit": 20,
    "offset": 0,
    "page": 1,
    "pages": 12,
    "has_more": true,
    "next_cursor": "eyJ2IjoxLCJzb3J0X2ZpZWxkIjoiY3JlYXRlZF9hdCIs..."
  }
}

Endpoints that support sort_by / sort_order

Tenders — main panel list

Method / path GET /api/v1/tenders
Also GET /api/v1/tenders/recommend (same sorting rules)
Default sort created_at desc

Recommended sort_by values (field exists on tender documents):

sort_by Use case
created_at Newest ingested / default
updated_at Recently changed tenders
publication_date Publication date on TED notice
tender_deadline Submission / tender deadline
submission_deadline Alternative deadline field when set
estimated_value Value high → low or low → high
title Alphabetical (locale-dependent)
status Group by lifecycle status
country_code Group by country

The backend does not whitelist tender sort_by values. Use only documented fields; unknown fields may sort incorrectly or behave unpredictably.

Example with filters + sort:

GET /api/v1/tenders?limit=20&country_codes=DE,FR&sort_by=publication_date&sort_order=desc&deadline_from=1717200000

TypeScript helper:

type SortOrder = "asc" | "desc";

interface ListSortParams {
  sort_by?: string;
  sort_order?: SortOrder;
  limit?: number;
  offset?: number;
  cursor?: string;
}

function buildTenderListQuery(
  filters: Record<string, string | number | string[] | undefined>,
  sort: ListSortParams = { sort_by: "created_at", sort_order: "desc", limit: 20 }
): string {
  const params = new URLSearchParams();
  for (const [key, value] of Object.entries(filters)) {
    if (value === undefined) continue;
    if (Array.isArray(value)) value.forEach((v) => params.append(key, v));
    else params.set(key, String(value));
  }
  params.set("sort_by", sort.sort_by ?? "created_at");
  params.set("sort_order", sort.sort_order ?? "desc");
  if (sort.limit) params.set("limit", String(sort.limit));
  if (sort.cursor) params.set("cursor", sort.cursor);
  else if (sort.offset) params.set("offset", String(sort.offset));
  return params.toString();
}

Notifications

Method / path GET /api/v1/notifications
Default sort created_at desc

Recommended sort_by values:

sort_by Use case
created_at Newest notifications (default)
updated_at Last updated
seen_at Seen / unread ordering
priority Important first (with sort_order=desc)
schedule_at Scheduled notifications

Feedback (company list)

Method / path GET /api/v1/feedback
Default sort created_at desc
Scope Company-scoped from the logged-in customer

Recommended sort_by values:

sort_by Use case
created_at Newest feedback (default)
updated_at Recently changed
feedback_type Group likes / dislikes

Endpoints without sort_by / sort_order (BP)

Endpoint Notes
GET /api/v1/tender-approvals Uses limit / offset only; no sort_by on the public handler today
GET /api/v1/companies Single “my company” resource, not a sortable list
Kanban (/admin/v1/kanban/...) Admin-only; card order is order + move/reorder APIs

If you need sorted tender approvals in BP, coordinate with backend to add sort_by / sort_order to PublicGetTenderApprovals (admin list already supports them).


UI patterns

Column header → query params

UI label sort_by Typical sort_order
Date added created_at desc
Deadline tender_deadline asc
Value estimated_value desc
Published publication_date desc

Toggle the same column: flip ascdesc. Changing column: set new sort_by and reset to page 1 (offset=0, drop cursor).

Infinite scroll / “Load more”

  1. First request: sort_by, sort_order, limit, filters — no cursor.
  2. Next page: repeat the same sort and filters; pass cursor=meta.next_cursor.
  3. Stop when meta.has_more === false.

Errors

Situation HTTP error.code (typical)
Invalid sort_order (not asc/desc) 400 BAD_REQUEST
cursor + offset together 400 BAD_REQUEST
cursor with different sort_by / sort_order 400 BAD_REQUEST
limit < 1 or > 100 400 BAD_REQUEST

Admin panel (reference)

Admin list routes under /admin/v1/* use the same sort_by / sort_order conventions where response.NewPagination is used. Some admin forms restrict values (e.g. tender approvals: created_at, updated_at, status, submission_mode). BP tender lists are less restricted than admin tender-approval lists.


Source: pkg/response/pagination.go, pkg/mongo/list_pagination.go, internal/tender, internal/notification, internal/feedback · Last updated: 2026-05-31