Improve admin form workspace UX
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
import { db } from "@/db";
|
||||
import { submissions } from "@/db/schema";
|
||||
import {
|
||||
and,
|
||||
asc,
|
||||
count,
|
||||
desc,
|
||||
eq,
|
||||
gte,
|
||||
ilike,
|
||||
lt,
|
||||
or,
|
||||
sql,
|
||||
type SQL,
|
||||
} from "drizzle-orm";
|
||||
|
||||
export const RESULTS_PAGE_SIZE = 25;
|
||||
|
||||
export type ResultFilters = {
|
||||
q: string;
|
||||
page: number;
|
||||
sort: "submittedAt" | "respondent";
|
||||
direction: "asc" | "desc";
|
||||
edited: "all" | "edited" | "unedited";
|
||||
dateFrom: string;
|
||||
dateTo: string;
|
||||
};
|
||||
|
||||
export function parseResultFilters(
|
||||
params: Record<string, string | string[] | undefined>
|
||||
): ResultFilters {
|
||||
const value = (key: string) => {
|
||||
const entry = params[key];
|
||||
return Array.isArray(entry) ? entry[0] ?? "" : entry ?? "";
|
||||
};
|
||||
const page = Number.parseInt(value("page"), 10);
|
||||
const sort = value("sort");
|
||||
const direction = value("direction");
|
||||
const edited = value("edited");
|
||||
|
||||
return {
|
||||
q: value("q").trim().slice(0, 100),
|
||||
page: Number.isFinite(page) && page > 0 ? page : 1,
|
||||
sort: sort === "respondent" ? "respondent" : "submittedAt",
|
||||
direction: direction === "asc" ? "asc" : "desc",
|
||||
edited:
|
||||
edited === "edited" || edited === "unedited" ? edited : "all",
|
||||
dateFrom: /^\d{4}-\d{2}-\d{2}$/.test(value("dateFrom"))
|
||||
? value("dateFrom")
|
||||
: "",
|
||||
dateTo: /^\d{4}-\d{2}-\d{2}$/.test(value("dateTo"))
|
||||
? value("dateTo")
|
||||
: "",
|
||||
};
|
||||
}
|
||||
|
||||
function bangkokBoundary(date: string, end: boolean) {
|
||||
if (!date) return null;
|
||||
const value = new Date(`${date}T00:00:00+07:00`);
|
||||
if (end) value.setUTCDate(value.getUTCDate() + 1);
|
||||
return value;
|
||||
}
|
||||
|
||||
function buildWhere(formId: string, filters: ResultFilters) {
|
||||
const conditions: SQL[] = [eq(submissions.formId, formId)];
|
||||
if (filters.q) {
|
||||
const query = `%${filters.q}%`;
|
||||
conditions.push(
|
||||
or(
|
||||
ilike(submissions.userName, query),
|
||||
ilike(submissions.userDiscordId, query)
|
||||
)!
|
||||
);
|
||||
}
|
||||
if (filters.edited === "edited") {
|
||||
conditions.push(sql`jsonb_array_length(coalesce(${submissions.editHistory}, '[]'::jsonb)) > 0`);
|
||||
}
|
||||
if (filters.edited === "unedited") {
|
||||
conditions.push(sql`jsonb_array_length(coalesce(${submissions.editHistory}, '[]'::jsonb)) = 0`);
|
||||
}
|
||||
const from = bangkokBoundary(filters.dateFrom, false);
|
||||
const to = bangkokBoundary(filters.dateTo, true);
|
||||
if (from) conditions.push(gte(submissions.submittedAt, from));
|
||||
if (to) conditions.push(lt(submissions.submittedAt, to));
|
||||
return and(...conditions)!;
|
||||
}
|
||||
|
||||
export async function getFilteredSubmissions(
|
||||
formId: string,
|
||||
filters: ResultFilters,
|
||||
options: { paginate?: boolean } = {}
|
||||
) {
|
||||
const where = buildWhere(formId, filters);
|
||||
const orderDirection = filters.direction === "asc" ? asc : desc;
|
||||
const order =
|
||||
filters.sort === "respondent"
|
||||
? orderDirection(submissions.userName)
|
||||
: orderDirection(submissions.submittedAt);
|
||||
const [{ total }] = await db
|
||||
.select({ total: count() })
|
||||
.from(submissions)
|
||||
.where(where);
|
||||
|
||||
const baseQuery = db
|
||||
.select()
|
||||
.from(submissions)
|
||||
.where(where)
|
||||
.orderBy(order, orderDirection(submissions.id));
|
||||
const rows =
|
||||
options.paginate === false
|
||||
? await baseQuery
|
||||
: await baseQuery
|
||||
.limit(RESULTS_PAGE_SIZE)
|
||||
.offset((filters.page - 1) * RESULTS_PAGE_SIZE);
|
||||
|
||||
return { rows, total };
|
||||
}
|
||||
|
||||
export function resultFiltersToSearchParams(
|
||||
filters: ResultFilters,
|
||||
overrides: Partial<ResultFilters> = {}
|
||||
) {
|
||||
const next = { ...filters, ...overrides };
|
||||
const params = new URLSearchParams();
|
||||
if (next.q) params.set("q", next.q);
|
||||
if (next.page > 1) params.set("page", String(next.page));
|
||||
if (next.sort !== "submittedAt") params.set("sort", next.sort);
|
||||
if (next.direction !== "desc") params.set("direction", next.direction);
|
||||
if (next.edited !== "all") params.set("edited", next.edited);
|
||||
if (next.dateFrom) params.set("dateFrom", next.dateFrom);
|
||||
if (next.dateTo) params.set("dateTo", next.dateTo);
|
||||
return params.toString();
|
||||
}
|
||||
Reference in New Issue
Block a user