98 lines
2.4 KiB
TypeScript
98 lines
2.4 KiB
TypeScript
/**
|
|
* Returns the first item in an array or null if empty.
|
|
*/
|
|
export function takeFirstOrNull<TData>(data: TData[]): TData | null {
|
|
return data[0] ?? null;
|
|
}
|
|
|
|
/**
|
|
* Returns the first item in an array or throws an error if empty.
|
|
*/
|
|
export function takeFirstOrThrow<TData>(
|
|
data: TData[],
|
|
errorMessage?: string
|
|
): TData {
|
|
const first = takeFirstOrNull(data);
|
|
|
|
if (!first) {
|
|
throw new Error(errorMessage ?? 'Item not found');
|
|
}
|
|
|
|
return first;
|
|
}
|
|
|
|
/**
|
|
* Checks if a value is considered empty (null, empty string, empty array, or empty object).
|
|
*/
|
|
export function isEmpty(value: unknown): boolean {
|
|
if (value === null || value === undefined) return true;
|
|
if (typeof value === 'string' && value.trim() === '') return true;
|
|
if (Array.isArray(value) && value.length === 0) return true;
|
|
if (
|
|
typeof value === 'object' &&
|
|
value !== null &&
|
|
Object.keys(value).length === 0
|
|
)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
export type Prettify<T> = {
|
|
[K in keyof T]: T[K];
|
|
} & {};
|
|
|
|
export type EmptyProps<T extends React.ElementType> = Omit<
|
|
React.ComponentProps<T>,
|
|
keyof React.ComponentProps<T>
|
|
>;
|
|
|
|
export interface SearchParams {
|
|
[key: string]: string | string[] | undefined;
|
|
}
|
|
|
|
/**
|
|
* Utility untuk membangun opsi query Prisma (where, orderBy) dari search params.
|
|
* @param params Search params (biasanya dari URL/query string)
|
|
* @param fieldMap Mapping field untuk pencarian (misal: { name: 'contains', email: 'equals' })
|
|
* @returns Objek { where, orderBy } untuk Prisma
|
|
*/
|
|
export function buildPrismaQueryOptions<T extends Record<string, any>>(
|
|
params: Record<string, any>,
|
|
fieldMap: Record<
|
|
string,
|
|
| 'equals'
|
|
| 'contains'
|
|
| 'startsWith'
|
|
| 'endsWith'
|
|
| 'in'
|
|
| 'gte'
|
|
| 'lte'
|
|
| 'gt'
|
|
| 'lt'
|
|
>,
|
|
defaultOrderBy?: { [key: string]: 'asc' | 'desc' }
|
|
) {
|
|
const where: Record<string, any> = {};
|
|
let orderBy: Record<string, 'asc' | 'desc'> = defaultOrderBy || {};
|
|
|
|
Object.entries(fieldMap).forEach(([paramKey, operator]) => {
|
|
const value = params[paramKey];
|
|
if (value !== undefined && value !== null && value !== '') {
|
|
if (operator === 'in' && Array.isArray(value)) {
|
|
where[paramKey] = { in: value };
|
|
} else {
|
|
where[paramKey] = { [operator]: value };
|
|
}
|
|
}
|
|
});
|
|
|
|
// Support orderBy param (misal: ?orderBy=createdAt&order=desc)
|
|
if (params.orderBy) {
|
|
orderBy = {
|
|
[params.orderBy]: params.order === 'desc' ? 'desc' : 'asc'
|
|
};
|
|
}
|
|
|
|
return { where, orderBy };
|
|
}
|