mirror of
https://github.com/orangecoding/fredy.git
synced 2026-06-16 12:31:07 +00:00
storing the date when a status was set
This commit is contained in:
@@ -127,7 +127,7 @@ export function normalizeListListings(queryResult, { page, pageSize }) {
|
||||
md += `| ID | Title | Address | Price | Size | Provider | Active | Status | Created | Job |\n`;
|
||||
md += `|----|-------|---------|-------|------|----------|--------|--------|---------|-----|\n`;
|
||||
for (const l of listings) {
|
||||
md += `| ${cell(l.id)} | ${cell(l.title)} | ${cell(l.address)} | ${cell(l.price)} | ${cell(l.size)} | ${cell(l.provider)} | ${l.is_active ? 'yes' : 'no'} | ${cell(l.status)} | ${formatDate(l.created_at)} | ${cell(l.job_name)} |\n`;
|
||||
md += `| ${cell(l.id)} | ${cell(l.title)} | ${cell(l.address)} | ${cell(l.price)} | ${cell(l.size)} | ${cell(l.provider)} | ${l.is_active ? 'yes' : 'no'} | ${cell(l.status?.status)} | ${formatDate(l.created_at)} | ${cell(l.job_name)} |\n`;
|
||||
}
|
||||
md += `\nUse **get_listing** with an ID for full details (description, link, image).\n`;
|
||||
} else {
|
||||
@@ -156,7 +156,10 @@ export function normalizeGetListing(listing) {
|
||||
md += `- **Link:** ${listing.link || '–'}\n`;
|
||||
md += `- **Image:** ${listing.image_url || '–'}\n`;
|
||||
md += `- **Active:** ${listing.is_active ? 'yes' : 'no'}\n`;
|
||||
md += `- **Status:** ${listing.status || '–'}\n`;
|
||||
md += `- **Status:** ${listing.status?.status || '–'}\n`;
|
||||
if (listing.status?.setAt) {
|
||||
md += `- **Status set at:** ${formatDate(listing.status.setAt)}\n`;
|
||||
}
|
||||
md += `- **Created:** ${formatDate(listing.created_at)}\n`;
|
||||
md += `- **Job:** ${listing.job_name || '–'}\n`;
|
||||
if (listing.latitude != null && listing.longitude != null) {
|
||||
|
||||
@@ -3,10 +3,27 @@
|
||||
* Licensed under Apache-2.0 with Commons Clause and Attribution/Naming Clause
|
||||
*/
|
||||
|
||||
import { nullOrEmpty } from '../../utils.js';
|
||||
import { nullOrEmpty, fromJson } from '../../utils.js';
|
||||
import SqliteConnection from './SqliteConnection.js';
|
||||
import { nanoid } from 'nanoid';
|
||||
|
||||
/**
|
||||
* Parse the JSON `status` column of a listing row in place.
|
||||
*
|
||||
* The DB stores status as a JSON payload `{ status, setAt }` (or NULL).
|
||||
* Consumers expect an object/null, so we normalize before returning.
|
||||
*
|
||||
* @param {Object|null|undefined} row - A raw row from the listings table.
|
||||
* @returns {Object|null|undefined} The same row with `status` parsed.
|
||||
*/
|
||||
const parseListingStatus = (row) => {
|
||||
if (row == null) return row;
|
||||
if (typeof row.status === 'string') {
|
||||
row.status = fromJson(row.status, null);
|
||||
}
|
||||
return row;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return a list of known listing hashes for a given job and provider.
|
||||
* Useful to de-duplicate before inserting new listings.
|
||||
@@ -319,7 +336,9 @@ export const queryListings = ({
|
||||
} else if (watchListFilter === false) {
|
||||
whereParts.push('(wl.id IS NULL)');
|
||||
}
|
||||
// statusFilter: 'applied'|'rejected'|'accepted' -> equality; 'none' -> NULL
|
||||
// statusFilter: 'applied'|'rejected'|'accepted' -> equality on JSON status field; 'none' -> NULL.
|
||||
// The status column is a JSON payload `{ status, setAt }`, so we extract the inner
|
||||
// status string for comparison instead of matching the raw text.
|
||||
if (statusFilter === 'none') {
|
||||
whereParts.push('(l.status IS NULL)');
|
||||
} else if (
|
||||
@@ -327,7 +346,7 @@ export const queryListings = ({
|
||||
['applied', 'rejected', 'accepted'].includes(statusFilter.toLowerCase())
|
||||
) {
|
||||
params.statusValue = statusFilter.toLowerCase();
|
||||
whereParts.push('(l.status = @statusValue)');
|
||||
whereParts.push(`(json_extract(l.status, '$.status') = @statusValue)`);
|
||||
}
|
||||
// Time range filters (unix timestamps in milliseconds)
|
||||
if (Number.isFinite(createdAfter) && createdAfter > 0) {
|
||||
@@ -403,7 +422,7 @@ export const queryListings = ({
|
||||
params,
|
||||
);
|
||||
|
||||
return { totalNumber, page: safePage, result: rows };
|
||||
return { totalNumber, page: safePage, result: rows.map(parseListingStatus) };
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -638,7 +657,7 @@ export const getListingById = (id, userId = null, isAdmin = false) => {
|
||||
if (!isAdmin) {
|
||||
whereScoping = `AND (j.user_id = @userId OR EXISTS (SELECT 1 FROM json_each(j.shared_with_user) AS sw WHERE sw.value = @userId))`;
|
||||
}
|
||||
return (
|
||||
return parseListingStatus(
|
||||
SqliteConnection.query(
|
||||
`SELECT l.*, j.name AS job_name, CASE WHEN wl.id IS NOT NULL THEN 1 ELSE 0 END AS isWatched
|
||||
FROM listings l
|
||||
@@ -646,7 +665,7 @@ export const getListingById = (id, userId = null, isAdmin = false) => {
|
||||
LEFT JOIN watch_list wl ON wl.listing_id = l.id AND wl.user_id = @userId
|
||||
WHERE l.id = @id AND l.manually_deleted = 0 ${whereScoping}`,
|
||||
params,
|
||||
)[0] || null
|
||||
)[0] || null,
|
||||
);
|
||||
};
|
||||
|
||||
@@ -674,6 +693,10 @@ export const setListingNotes = (id, notes) => {
|
||||
/**
|
||||
* Set or clear the status of a single listing.
|
||||
*
|
||||
* The status column stores a JSON payload `{ status, setAt }` so consumers
|
||||
* can show both the user's decision and when it was made. Passing `null`
|
||||
* clears the column.
|
||||
*
|
||||
* @param {string} id - The listing ID.
|
||||
* @param {('applied'|'rejected'|'accepted'|null)} status - New status, or null to clear.
|
||||
* @returns {number} Number of rows affected (0 if listing not found).
|
||||
@@ -685,9 +708,10 @@ export const setListingStatus = (id, status) => {
|
||||
if (normalized != null && !allowed.includes(normalized)) {
|
||||
throw new Error(`Invalid listing status: ${status}`);
|
||||
}
|
||||
const payload = normalized == null ? null : JSON.stringify({ status: normalized, setAt: Date.now() });
|
||||
const res = SqliteConnection.execute(`UPDATE listings SET status = @status WHERE id = @id`, {
|
||||
id,
|
||||
status: normalized,
|
||||
status: payload,
|
||||
});
|
||||
return res?.changes ?? 0;
|
||||
};
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
export function up(db) {
|
||||
db.exec(`
|
||||
ALTER TABLE listings ADD COLUMN status TEXT;
|
||||
CREATE INDEX IF NOT EXISTS idx_listings_status ON listings (status);
|
||||
ALTER TABLE listings ADD COLUMN status JSON;
|
||||
CREATE INDEX IF NOT EXISTS idx_listings_status ON listings (json_extract(status, '$.status'));
|
||||
`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user