Every row has a details icon under the Find a Tender link. It opens a dialog with the opportunity's details from the list, and fetches the opportunity with its releases, shown as tabs, newest first. Each tab summarises the release's tender, awards and contracts, links to its Find a Tender notice where it is one, and has the release's JSON, pretty-printed and highlighted, in a section laid out only when it's opened. The JSON is highlighted by splitting it into tokens shown as text, not HTML, as releases come from outside sources. The README's saved preferences now include the filters.
36 lines
1020 B
TypeScript
36 lines
1020 B
TypeScript
import type { JSX } from 'solid-js';
|
|
import { createMemo, For } from 'solid-js';
|
|
|
|
import type { JsonTokenKind } from '@/lib/jsonTokens';
|
|
import { tokeniseJson } from '@/lib/jsonTokens';
|
|
|
|
interface Props {
|
|
/** JSON text, shown as it is: pretty-print it first. */
|
|
json: string;
|
|
}
|
|
|
|
const TOKEN_CLASS: Record<JsonTokenKind, string> = {
|
|
key: 'text-code-key',
|
|
string: 'text-code-string',
|
|
number: 'text-code-number',
|
|
literal: 'text-code-literal',
|
|
plain: '',
|
|
};
|
|
|
|
/**
|
|
* JSON text, syntax highlighted. Each token is a text span, never HTML, as
|
|
* releases come from outside sources.
|
|
*/
|
|
export function JsonView(props: Props): JSX.Element {
|
|
const tokens = createMemo(() => tokeniseJson(props.json));
|
|
return (
|
|
<pre class="max-h-[50vh] overflow-auto rounded-md border border-line-soft bg-paper p-3 font-mono text-[13px] leading-relaxed">
|
|
<code>
|
|
<For each={tokens()}>
|
|
{(token) => <span class={TOKEN_CLASS[token.kind]}>{token.text}</span>}
|
|
</For>
|
|
</code>
|
|
</pre>
|
|
);
|
|
}
|