mirror of
https://github.com/olehomelchenko/astrolabe.git
synced 2026-08-08 02:02:33 +00:00
Add dataset library, extract-to-dataset, and render-time reference resolution
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* Extract-to-Dataset — the modal body (spec §03F).
|
||||
*
|
||||
* Shows a read-only preview of the active snippet draft's inline data and asks
|
||||
* for a dataset name. On confirm it saves the data as a new dataset and rewrites
|
||||
* the draft to reference it by name (logic in ExtractStore), then force-closes
|
||||
* (the commit is the user's confirmation, so no discard prompt). Cancel leaves
|
||||
* the spec unchanged.
|
||||
*/
|
||||
|
||||
import { useShallow } from 'zustand/react/shallow';
|
||||
import { closeModal } from '../modals/ModalCoordinator';
|
||||
import { useExtractStore } from '../stores/ExtractStore';
|
||||
import styles from './ExtractModal.module.css';
|
||||
|
||||
const MAX_PREVIEW = 1500;
|
||||
|
||||
function previewOf(values: unknown): string {
|
||||
let text: string;
|
||||
try {
|
||||
text = typeof values === 'string' ? values : JSON.stringify(values, null, 2);
|
||||
} catch {
|
||||
text = String(values);
|
||||
}
|
||||
return text.length > MAX_PREVIEW ? `${text.slice(0, MAX_PREVIEW)}\n…` : text;
|
||||
}
|
||||
|
||||
export function ExtractModal() {
|
||||
const { name, source, error } = useExtractStore(
|
||||
useShallow((s) => ({ name: s.name, source: s.source, error: s.error })),
|
||||
);
|
||||
const setName = useExtractStore((s) => s.setName);
|
||||
const runConfirm = useExtractStore((s) => s.confirm);
|
||||
|
||||
const handleCreate = () => {
|
||||
if (runConfirm()) void closeModal(true);
|
||||
};
|
||||
|
||||
if (!source) {
|
||||
return <p className={styles.muted}>This snippet has no inline data to extract.</p>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.extract}>
|
||||
<p className={styles.intro}>
|
||||
Save this snippet’s inline data as a reusable dataset. The spec will be rewritten to
|
||||
reference it by name.
|
||||
</p>
|
||||
|
||||
<label className={styles.field}>
|
||||
<span className={styles.label}>Dataset name</span>
|
||||
<input
|
||||
type="text"
|
||||
className={styles.input}
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter') handleCreate();
|
||||
}}
|
||||
placeholder="e.g. Sales 2024"
|
||||
autoFocus
|
||||
/>
|
||||
</label>
|
||||
|
||||
<div className={styles.field}>
|
||||
<span className={styles.label}>Data to extract ({source.format.toUpperCase()})</span>
|
||||
<pre className={styles.preview}>{previewOf(source.values)}</pre>
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
<p className={styles.error} role="alert">
|
||||
{error}
|
||||
</p>
|
||||
)}
|
||||
|
||||
<div className={styles.actions}>
|
||||
<button type="button" className={styles.action} onClick={() => void closeModal()}>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={`${styles.action} ${styles.primary}`}
|
||||
onClick={handleCreate}
|
||||
>
|
||||
Create dataset
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user