import React, { useState } from 'react';
import Icon from '../Icon.jsx';
import { categories } from '../seed.js';
import { baselineInputIssues } from '../access.js';
import { useStudio, uid, timestamp, log } from '../store.jsx';
import { Badge, Button, Empty, Field, Heading, Modal, Note, Search, SourceDialog, Tabs, UploadBox } from '../ui.jsx';
export function CreateBaseline({ close, onCreated }) {
const { state: s, mutate, notify } = useStudio();
const [name, setName] = useState(''), [description, setDescription] = useState(''), [scope, setScope] = useState('');
const [documents, setDocuments] = useState([]), [category, setCategory] = useState(0), [error, setError] = useState('');
const [id] = useState(() => uid('baseline'));
const issues = baselineInputIssues(s, name, documents);
function save(event) {
event.preventDefault(); if (issues.length) { setError(issues.join(' ')); return; }
mutate(d => {
d.baselines.push({ id, ref: name.trim(), team: s.session.team, description, scope, version: '1.0', status: 'Active', sourceIds: documents.map(f => f.id), rules: [], incorporatedCRs: [], createdAt: timestamp() });
d.documents.push(...documents.map(doc => ({ ...doc, status: 'Ready' })));
log(d, `Created System Baseline ${name.trim()} from ${documents.length} document(s)`);
}); onCreated(id); close(); notify('System Baseline created. Initial creation does not require approval.');
}
return <Modal title="Create a System Baseline" close={close} wide>
<p className="modal-intro">Upload at least one document in any category. You do not need all three.</p>
<form onSubmit={save}>
<div className="form-grid"><Field label="System Baseline name"><input required maxLength={60} placeholder={`${s.session.team}-B3`} value={name} onChange={e => setName(e.target.value)}/></Field><Field label="Owning team"><input value={s.session.team} readOnly/></Field></div>
<Field label="Description"><textarea rows={2} value={description} onChange={e => setDescription(e.target.value)} maxLength={600} placeholder="Describe the reusable system knowledge."/></Field>
<Field label="Application / process scope (optional)"><input value={scope} onChange={e => setScope(e.target.value)} maxLength={100} placeholder="e.g. Membership and collateral"/></Field>
<div className="category-tabs document-category-picker" role="tablist" aria-label="Baseline document categories">{categories.map((label, index) => <button type="button" role="tab" aria-selected={category === index} className={category === index ? 'active' : ''} key={label} onClick={() => setCategory(index)}>{label}<span>{documents.filter(d => d.category === index).length}</span></button>)}</div>
<UploadBox scope={{ team: s.session.team, baselineId: id, category }} onStaged={files => setDocuments(d => [...d, ...files])}/>
{documents.map(doc => <div className="selected-file" key={doc.id}><Icon name="file" size={16}/><span>{doc.name}<small>{categories[doc.category]}</small></span><button type="button" className="icon-button" aria-label={`Remove ${doc.name}`} onClick={() => setDocuments(files => files.filter(f => f.id !== doc.id))}><Icon name="close" size={14}/></button></div>)}
<Note>{documents.length ? `${documents.length} document(s) attached. The one-document minimum is met.` : 'Minimum: one Business, Technical, or Functional / Bug Information document.'} Only updates to an existing System Baseline need administrator approval.</Note>
{error && <p className="form-error" role="alert">{error}</p>}
<div className="modal-actions"><Button onClick={close}>Cancel</Button><Button type="submit" variant="primary" disabled={issues.length > 0}>Create System Baseline</Button></div>
</form>
</Modal>;
}
export default function Baselines({ navigate, create = false }) {
const { state: s, mutate, notify } = useStudio();
const [query, setQuery] = useState(''), [selected, setSelected] = useState(''), [showCreate, setShowCreate] = useState(create);
const [source, setSource] = useState(''), [tab, setTab] = useState('Documents'), [category, setCategory] = useState(0);
const list = s.baselines.filter(b => b.team === s.session.team && `${b.ref} ${b.description}`.toLowerCase().includes(query.toLowerCase()));
const baseline = list.find(b => b.id === selected);
const docs = baseline ? s.documents.filter(d => baseline.sourceIds.includes(d.id)) : [];
return <><Heading eyebrow={`${s.session.team} · REUSABLE SYSTEM KNOWLEDGE`} title={baseline?.ref || 'System Baselines'} description={baseline?.description || 'A stable foundation for every change request.'}>
{baseline ? <Button onClick={() => setSelected('')}>Back to System Baselines</Button> : <Button icon="plus" variant="primary" onClick={() => setShowCreate(true)}>Create System Baseline</Button>}
</Heading>
{!baseline ? <><div className="library-toolbar"><Search value={query} onChange={setQuery} placeholder="Search System Baselines…"/><span className="subtle">{list.length} baseline versions</span></div>
<div className="library-grid">{list.map(b => <button className="card library-card" onClick={() => { setSelected(b.id); setTab('Documents'); }} key={b.id}><div className="library-card-top"><span className="soft-icon soft-purple"><Icon name="layers" size={21}/></span><Badge tone="green">{b.status}</Badge></div><h3>{b.ref}</h3><p>{b.description || 'No description added.'}</p><div className="library-meta"><span>Baseline Version {b.version}</span><span>{b.sourceIds.length} sources</span></div><div className="library-footer"><span>{b.scope || 'System knowledge'}</span><Icon name="arrow" size={16}/></div></button>)}</div>
{!list.length && <section className="card"><Empty title="No System Baselines yet" description="Start with one document from any of the three categories." action={<Button onClick={() => setShowCreate(true)}>Create System Baseline</Button>}/></section>}
</> : <><div className="detail-summary"><Badge tone="green">{baseline.status}</Badge><span>Owning team: <strong>{baseline.team}</strong></span><span>Baseline Version: <strong>{baseline.version}</strong></span><span>Scope: <strong>{baseline.scope || 'General'}</strong></span></div>
<Tabs value={tab} onChange={setTab} items={['Documents', 'Knowledge rules', 'Version history']}/>
{tab === 'Documents' ? <div className="detail-grid"><section className="card padded"><div className="section-heading inset"><h2>System Baseline documents</h2><span className="count-pill">{docs.length}</span></div>
<div className="category-tabs document-category-picker" role="tablist" aria-label="System Baseline categories">{categories.map((label, index) => <button role="tab" aria-selected={category === index} className={category === index ? 'active' : ''} key={label} onClick={() => setCategory(index)}>{label}<span>{docs.filter(d => d.category === index).length}</span></button>)}</div>
{docs.filter(d => d.category === category).map(d => <div className="review-source" key={d.id}><button className="source-link" onClick={() => setSource(d.id)}><Icon name="file" size={19}/><span><strong>{d.name}</strong><small>{d.kind} · {d.size} · Revision {d.revision}</small></span></button><Badge>{d.status}</Badge></div>)}
{!docs.some(d => d.category === category) && <Empty title="No document in this category" description="One document in any category is sufficient. All three categories are optional individually."/>}
{baseline.status === 'Draft' && <UploadBox scope={{ team: s.session.team, baselineId: baseline.id, category }} onStaged={files => mutate(d => { d.documents.push(...files.map(f => ({ ...f, status: 'Ready' }))); d.baselines.find(b => b.id === baseline.id).sourceIds.push(...files.map(f => f.id)); })}/>}
</section><section className="card padded"><h2>System Baseline lifecycle</h2><p className="supporting-copy">Create an initial baseline with any one document. Everyone in the team can create workspaces and use the available knowledge.</p><Note>Updating this System Baseline creates a new version and requires Administrator approval. Existing workspaces keep their selected version.</Note>
{baseline.status === 'Draft' && <Button className="full-width margin-top" variant="primary" disabled={!docs.length} onClick={() => { mutate(d => { d.baselines.find(b => b.id === baseline.id).status = 'Active'; }); notify('System Baseline activated. No approval is required for initial creation.'); }}>Activate System Baseline</Button>}
<Button className="full-width margin-top" onClick={() => navigate('workspaces?create')}>Create workspace from baseline</Button><Button className="full-width margin-top" onClick={() => navigate('approvals')}>System Baseline updates</Button>
</section></div> : tab === 'Knowledge rules' ? <section className="card"><div className="section-heading"><h2>Immutable knowledge manifest</h2><span className="count-pill">{baseline.rules.length}</span></div>{baseline.rules.map(r => <div className="rule-row" key={r.id}><span className="code-label">{r.id}</span><div><strong>{r.title}</strong><p>{r.text}</p></div><button className="text-button" onClick={() => setSource(r.source)}>Source <Icon name="external" size={12}/></button></div>)}{!baseline.rules.length && <Empty title="No knowledge rules recorded" description="Add source-linked requirements in a workspace to use this knowledge."/>}</section> : <section className="card"><div className="section-heading"><h2>System Baseline version history</h2></div>{s.baselines.filter(b => b.team === baseline.team).map(b => <button className="history-row" key={b.id} onClick={() => setSelected(b.id)}><span className="soft-icon soft-purple"><Icon name="layers" size={17}/></span><span><strong>{b.ref} · v{b.version}</strong><small>{b.incorporatedCRs.length} incorporated changes · {new Date(b.createdAt).toLocaleDateString('en-IN')}</small></span><Badge>{b.status}</Badge><Icon name="chevron" size={15}/></button>)}</section>}
</>}
{showCreate && <CreateBaseline onCreated={setSelected} close={() => { setShowCreate(false); if (create) navigate('baselines'); }}/>} {source && <SourceDialog sourceId={source} close={() => setSource('')}/>}</>;
}
For further actions, you may consider blocking this person and/or reporting abuse
Top comments (0)