Pilot 001: Operativ checklista — inte dokument, arbetsverktyg
- Fältfas: område, varför, infrastruktur, förväntade objekt, tid, problem - Teknisk fas: session, mission, artifacts, upload, metadata, explorer, viewer - Beslutsfas: rätt observation, evidens, beslut, varför inte - Utvärdering: tid, osäkerhet, automation, värde, nästa steg - Golden Mission-knapp för att markera #0001 Mål: Kan vi gå från verklighet till verifierat Decision Case utan manuella genvägar? Stoppregel: Ingen ny arkitektur förrän Pilot 001 genomfört. Next: Starta Pilot 001 — film, upload, verifiera
This commit is contained in:
@@ -4,6 +4,7 @@ import ArtifactViewer from './pages/ArtifactViewer';
|
||||
import MissionUpload from './pages/MissionUpload';
|
||||
import FieldConsole from './pages/FieldConsole';
|
||||
import HealthDashboard from './pages/HealthDashboard';
|
||||
import PilotChecklist from './pages/PilotChecklist';
|
||||
|
||||
function App() {
|
||||
return (
|
||||
@@ -15,6 +16,7 @@ function App() {
|
||||
<Link to="/upload">Mission Upload</Link>
|
||||
<Link to="/console">Field Console</Link>
|
||||
<Link to="/health">Health</Link>
|
||||
<Link to="/pilot">Pilot 001</Link>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
@@ -24,6 +26,7 @@ function App() {
|
||||
<Route path="/artifact/:id" element={<ArtifactViewer />} />
|
||||
<Route path="/console" element={<FieldConsole />} />
|
||||
<Route path="/health" element={<HealthDashboard />} />
|
||||
<Route path="/pilot" element={<PilotChecklist />} />
|
||||
</Routes>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,204 @@
|
||||
/**
|
||||
* Pilot Checklist
|
||||
*
|
||||
* Operativ checklista för fältarbete.
|
||||
* Inte ett dokument — ett arbetsverktyg.
|
||||
*
|
||||
* Pilot 001 Mål:
|
||||
* Kan vi gå från verklighet till verifierat Decision Case utan manuella genvägar?
|
||||
*/
|
||||
|
||||
import { useState } from 'react';
|
||||
|
||||
interface ChecklistItem {
|
||||
id: string;
|
||||
label: string;
|
||||
done: boolean;
|
||||
notes: string;
|
||||
}
|
||||
|
||||
const INITIAL_FIELD_ITEMS: ChecklistItem[] = [
|
||||
{ id: 'area', label: 'Område besökt', done: false, notes: '' },
|
||||
{ id: 'why', label: 'Varför valdes området?', done: false, notes: '' },
|
||||
{ id: 'infra', label: 'Typ av infrastruktur observerad', done: false, notes: '' },
|
||||
{ id: 'expected', label: 'Förväntade objekt', done: false, notes: '' },
|
||||
{ id: 'duration', label: 'Tid för datainsamling', done: false, notes: '' },
|
||||
{ id: 'bad', label: 'Vad fungerade dåligt?', done: false, notes: '' },
|
||||
{ id: 'missing', label: 'Vad saknades i appen?', done: false, notes: '' },
|
||||
];
|
||||
|
||||
const INITIAL_TECH_ITEMS: ChecklistItem[] = [
|
||||
{ id: 'session', label: 'Field Session skapad', done: false, notes: '' },
|
||||
{ id: 'mission', label: 'Mission skapad', done: false, notes: '' },
|
||||
{ id: 'artifacts', label: 'Artifacts registrerade', done: false, notes: '' },
|
||||
{ id: 'upload', label: 'Upload fungerade', done: false, notes: '' },
|
||||
{ id: 'metadata', label: 'Metadata extraherad', done: false, notes: '' },
|
||||
{ id: 'explorer', label: 'Dataset Explorer visar mission', done: false, notes: '' },
|
||||
{ id: 'viewer', label: 'Artifact Viewer visar detaljer', done: false, notes: '' },
|
||||
];
|
||||
|
||||
const INITIAL_DECISION_ITEMS: ChecklistItem[] = [
|
||||
{ id: 'observation', label: 'Systemet gav rätt observation', done: false, notes: '' },
|
||||
{ id: 'evidence', label: 'Tillräcklig evidens fanns', done: false, notes: '' },
|
||||
{ id: 'decision', label: 'Beslut kunde fattas', done: false, notes: '' },
|
||||
{ id: 'why-not', label: 'Om inte — varför?', done: false, notes: '' },
|
||||
];
|
||||
|
||||
const INITIAL_REVIEW_ITEMS: ChecklistItem[] = [
|
||||
{ id: 'time', label: 'Vad tog längst tid?', done: false, notes: '' },
|
||||
{ id: 'uncertain', label: 'Var blev vi osäkra?', done: false, notes: '' },
|
||||
{ id: 'auto', label: 'Vilket moment borde automatiseras?', done: false, notes: '' },
|
||||
{ id: 'value', label: 'Vilket steg skapade mest värde?', done: false, notes: '' },
|
||||
{ id: 'next', label: 'Vad bygger vi härnäst?', done: false, notes: '' },
|
||||
];
|
||||
|
||||
function PilotChecklist() {
|
||||
const [fieldItems, setFieldItems] = useState(INITIAL_FIELD_ITEMS);
|
||||
const [techItems, setTechItems] = useState(INITIAL_TECH_ITEMS);
|
||||
const [decisionItems, setDecisionItems] = useState(INITIAL_DECISION_ITEMS);
|
||||
const [reviewItems, setReviewItems] = useState(INITIAL_REVIEW_ITEMS);
|
||||
|
||||
const toggleItem = (items: ChecklistItem[], setItems: React.Dispatch<React.SetStateAction<ChecklistItem[]>>, id: string) => {
|
||||
setItems(items.map(item =>
|
||||
item.id === id ? { ...item, done: !item.done } : item
|
||||
));
|
||||
};
|
||||
|
||||
const updateNote = (items: ChecklistItem[], setItems: React.Dispatch<React.SetStateAction<ChecklistItem[]>>, id: string, notes: string) => {
|
||||
setItems(items.map(item =>
|
||||
item.id === id ? { ...item, notes } : item
|
||||
));
|
||||
};
|
||||
|
||||
const progress = (items: ChecklistItem[]) =>
|
||||
Math.round((items.filter(i => i.done).length / items.length) * 100);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2>Pilot 001 Checklist</h2>
|
||||
<p style={{ color: '#666', marginBottom: 20 }}>
|
||||
Mål: Kan vi gå från verklighet till verifierat Decision Case utan manuella genvägar?
|
||||
</p>
|
||||
|
||||
<Section
|
||||
title="Fältfas"
|
||||
progress={progress(fieldItems)}
|
||||
items={fieldItems}
|
||||
onToggle={id => toggleItem(fieldItems, setFieldItems, id)}
|
||||
onNote={(id, notes) => updateNote(fieldItems, setFieldItems, id, notes)}
|
||||
/>
|
||||
|
||||
<Section
|
||||
title="Teknisk fas"
|
||||
progress={progress(techItems)}
|
||||
items={techItems}
|
||||
onToggle={id => toggleItem(techItems, setTechItems, id)}
|
||||
onNote={(id, notes) => updateNote(techItems, setTechItems, id, notes)}
|
||||
/>
|
||||
|
||||
<Section
|
||||
title="Beslutsfas"
|
||||
progress={progress(decisionItems)}
|
||||
items={decisionItems}
|
||||
onToggle={id => toggleItem(decisionItems, setDecisionItems, id)}
|
||||
onNote={(id, notes) => updateNote(decisionItems, setDecisionItems, id, notes)}
|
||||
/>
|
||||
|
||||
<Section
|
||||
title="Utvärdering"
|
||||
progress={progress(reviewItems)}
|
||||
items={reviewItems}
|
||||
onToggle={id => toggleItem(reviewItems, setReviewItems, id)}
|
||||
onNote={(id, notes) => updateNote(reviewItems, setReviewItems, id, notes)}
|
||||
/>
|
||||
|
||||
<div style={{ marginTop: 20, padding: 16, background: '#f0f0f0', borderRadius: 8 }}>
|
||||
<h3>Golden Mission</h3>
|
||||
<p>Markera första riktiga videon som Golden Mission #0001</p>
|
||||
<button style={{
|
||||
padding: '12px 24px',
|
||||
background: '#ffd700',
|
||||
border: 'none',
|
||||
borderRadius: 8,
|
||||
cursor: 'pointer',
|
||||
fontWeight: 'bold',
|
||||
}}>
|
||||
Markera som Golden Mission
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Section({
|
||||
title,
|
||||
progress,
|
||||
items,
|
||||
onToggle,
|
||||
onNote,
|
||||
}: {
|
||||
title: string;
|
||||
progress: number;
|
||||
items: ChecklistItem[];
|
||||
onToggle: (id: string) => void;
|
||||
onNote: (id: string, notes: string) => void;
|
||||
}) {
|
||||
return (
|
||||
<div style={{ marginBottom: 24 }}>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 12 }}>
|
||||
<h3 style={{ margin: 0 }}>{title}</h3>
|
||||
<span style={{
|
||||
padding: '4px 12px',
|
||||
background: progress === 100 ? '#32cd32' : '#1e90ff',
|
||||
color: 'white',
|
||||
borderRadius: 12,
|
||||
fontSize: 12,
|
||||
fontWeight: 'bold',
|
||||
}}>
|
||||
{progress}%
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div style={{ display: 'grid', gap: 8 }}>
|
||||
{items.map(item => (
|
||||
<div key={item.id} style={{
|
||||
border: '1px solid #ccc',
|
||||
padding: 12,
|
||||
borderRadius: 8,
|
||||
display: 'grid',
|
||||
gap: 8,
|
||||
}}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={item.done}
|
||||
onChange={() => onToggle(item.id)}
|
||||
style={{ width: 20, height: 20 }}
|
||||
/>
|
||||
<span style={{
|
||||
textDecoration: item.done ? 'line-through' : 'none',
|
||||
color: item.done ? '#999' : '#333',
|
||||
}}>
|
||||
{item.label}
|
||||
</span>
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Anteckningar..."
|
||||
value={item.notes}
|
||||
onChange={e => onNote(item.id, e.target.value)}
|
||||
style={{
|
||||
padding: 8,
|
||||
border: '1px solid #ddd',
|
||||
borderRadius: 4,
|
||||
fontSize: 14,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default PilotChecklist;
|
||||
Reference in New Issue
Block a user