mirror of
https://github.com/orangecoding/fredy.git
synced 2026-06-16 12:31:07 +00:00
Redesigning listing table (#248)
* redesigning listing table * getting rid of old listing table view * improving listing grid
This commit is contained in:
committed by
GitHub
parent
398259ff20
commit
3c209a8f97
@@ -104,7 +104,11 @@ export default async function execute(url, waitForSelector, options) {
|
||||
result = pageSource || (await page.content());
|
||||
}
|
||||
} catch (error) {
|
||||
logger.warn('Error executing with puppeteer executor', error);
|
||||
if (error?.message?.includes('Timeout')) {
|
||||
logger.debug('Error executing with puppeteer executor', error);
|
||||
} else {
|
||||
logger.warn('Error executing with puppeteer executor', error);
|
||||
}
|
||||
result = null;
|
||||
} finally {
|
||||
try {
|
||||
|
||||
@@ -40,11 +40,3 @@ export function markRunning(jobId) {
|
||||
export function markFinished(jobId) {
|
||||
running.delete(jobId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve all currently running job IDs.
|
||||
* @returns {string[]}
|
||||
*/
|
||||
export function getRunningJobIds() {
|
||||
return Array.from(running);
|
||||
}
|
||||
|
||||
@@ -163,3 +163,109 @@ export const getJobs = () => {
|
||||
notificationAdapter: fromJson(row.notificationAdapter, []),
|
||||
}));
|
||||
};
|
||||
|
||||
/**
|
||||
* Query jobs with pagination, filtering and sorting.
|
||||
*
|
||||
* @param {Object} params
|
||||
* @param {number} [params.pageSize=50]
|
||||
* @param {number} [params.page=1]
|
||||
* @param {string} [params.freeTextFilter]
|
||||
* @param {object} [params.activityFilter]
|
||||
* @param {string|null} [params.sortField=null]
|
||||
* @param {('asc'|'desc')} [params.sortDir='asc']
|
||||
* @param {string} [params.userId] - Current user id used to scope jobs (ignored for admins).
|
||||
* @param {boolean} [params.isAdmin=false] - When true, returns all jobs.
|
||||
* @returns {{ totalNumber:number, page:number, result:Object[] }}
|
||||
*/
|
||||
export const queryJobs = ({
|
||||
pageSize = 50,
|
||||
page = 1,
|
||||
activityFilter,
|
||||
freeTextFilter,
|
||||
sortField = null,
|
||||
sortDir = 'asc',
|
||||
userId = null,
|
||||
isAdmin = false,
|
||||
} = {}) => {
|
||||
// sanitize inputs
|
||||
const safePageSize = Number.isFinite(pageSize) && pageSize > 0 ? Math.min(500, Math.floor(pageSize)) : 50;
|
||||
const safePage = Number.isFinite(page) && page > 0 ? Math.floor(page) : 1;
|
||||
const offset = (safePage - 1) * safePageSize;
|
||||
|
||||
// build WHERE filter
|
||||
const whereParts = [];
|
||||
const params = { limit: safePageSize, offset };
|
||||
params.userId = userId || '__NO_USER__';
|
||||
|
||||
if (!isAdmin) {
|
||||
whereParts.push(
|
||||
`(j.user_id = @userId OR EXISTS (SELECT 1 FROM json_each(j.shared_with_user) AS sw WHERE sw.value = @userId))`,
|
||||
);
|
||||
}
|
||||
|
||||
if (freeTextFilter && String(freeTextFilter).trim().length > 0) {
|
||||
params.filter = `%${String(freeTextFilter).trim()}%`;
|
||||
whereParts.push(`(j.name LIKE @filter)`);
|
||||
}
|
||||
|
||||
if (activityFilter === true) {
|
||||
whereParts.push('(j.enabled = 1)');
|
||||
} else if (activityFilter === false) {
|
||||
whereParts.push('(j.enabled = 0)');
|
||||
}
|
||||
|
||||
const whereSql = whereParts.length ? `WHERE ${whereParts.join(' AND ')}` : '';
|
||||
|
||||
// whitelist sortable fields
|
||||
const sortable = new Set(['name', 'numberOfFoundListings', 'enabled']);
|
||||
const safeSortField = sortField && sortable.has(sortField) ? sortField : null;
|
||||
const safeSortDir = String(sortDir).toLowerCase() === 'desc' ? 'DESC' : 'ASC';
|
||||
|
||||
let orderSql = 'ORDER BY j.name IS NULL, j.name ASC';
|
||||
if (safeSortField) {
|
||||
if (safeSortField === 'numberOfFoundListings') {
|
||||
orderSql = `ORDER BY numberOfFoundListings ${safeSortDir}`;
|
||||
} else {
|
||||
orderSql = `ORDER BY j.${safeSortField} ${safeSortDir}`;
|
||||
}
|
||||
}
|
||||
|
||||
// count total
|
||||
const countRow = SqliteConnection.query(
|
||||
`SELECT COUNT(1) as cnt
|
||||
FROM jobs j
|
||||
${whereSql}`,
|
||||
params,
|
||||
);
|
||||
const totalNumber = countRow?.[0]?.cnt ?? 0;
|
||||
|
||||
// fetch page
|
||||
const rows = SqliteConnection.query(
|
||||
`SELECT j.id,
|
||||
j.user_id AS userId,
|
||||
j.enabled,
|
||||
j.name,
|
||||
j.blacklist,
|
||||
j.provider,
|
||||
j.shared_with_user,
|
||||
j.notification_adapter AS notificationAdapter,
|
||||
(SELECT COUNT(1) FROM listings l WHERE l.job_id = j.id) AS numberOfFoundListings
|
||||
FROM jobs j
|
||||
${whereSql}
|
||||
${orderSql}
|
||||
LIMIT @limit OFFSET @offset`,
|
||||
params,
|
||||
);
|
||||
|
||||
const result = rows.map((row) => ({
|
||||
...row,
|
||||
enabled: !!row.enabled,
|
||||
blacklist: fromJson(row.blacklist, []),
|
||||
provider: fromJson(row.provider, []),
|
||||
shared_with_user: fromJson(row.shared_with_user, []),
|
||||
notificationAdapter: fromJson(row.notificationAdapter, []),
|
||||
}));
|
||||
|
||||
return { totalNumber, page: safePage, result };
|
||||
};
|
||||
|
||||
@@ -277,9 +277,11 @@ export const queryListings = ({
|
||||
params.filter = `%${String(freeTextFilter).trim()}%`;
|
||||
whereParts.push(`(title LIKE @filter OR address LIKE @filter OR provider LIKE @filter OR link LIKE @filter)`);
|
||||
}
|
||||
// activityFilter: when true -> only active listings (is_active = 1)
|
||||
// activityFilter: when true -> only active listings (is_active = 1), false -> only inactive
|
||||
if (activityFilter === true) {
|
||||
whereParts.push('(is_active = 1)');
|
||||
} else if (activityFilter === false) {
|
||||
whereParts.push('(is_active = 0)');
|
||||
}
|
||||
// Prefer filtering by job id when provided (unambiguous and robust)
|
||||
if (jobIdFilter && String(jobIdFilter).trim().length > 0) {
|
||||
@@ -295,9 +297,11 @@ export const queryListings = ({
|
||||
params.providerName = String(providerFilter).trim();
|
||||
whereParts.push('(provider = @providerName)');
|
||||
}
|
||||
// watchListFilter: when true -> only watched listings
|
||||
// watchListFilter: when true -> only watched listings, false -> only unwatched
|
||||
if (watchListFilter === true) {
|
||||
whereParts.push('(wl.id IS NOT NULL)');
|
||||
} else if (watchListFilter === false) {
|
||||
whereParts.push('(wl.id IS NULL)');
|
||||
}
|
||||
|
||||
const whereSql = whereParts.length ? `WHERE ${whereParts.join(' AND ')}` : '';
|
||||
|
||||
Reference in New Issue
Block a user