// Board-level dialogs: activity log, CSV import, column settings.
import { html, useState, useEffect } from '../vendor/preact-htm.mjs';
import { api } from './api.js';
import { state, useStore, loadBoard, navigate, boardPath, toast } from './store.js';
import { Icon, Avatar, ModalHeader, closeModal, timeAgo, Spinner, LABEL_COLORS, textOn } from './ui.js';
import { COLUMN_TYPES } from './cells.js';
import { activityText } from './item.js';
import * as A from './actions.js';
export function ActivityModal({ boardId }) {
const [rows, setRows] = useState(null);
const [who, setWho] = useState('');
useEffect(() => { api.get(`/api/boards/${boardId}/activity`).then(setRows); }, [boardId]);
const shown = (rows || []).filter((a) => !who || a.user_id === who);
const people = [...new Set((rows || []).map((a) => a.user_id).filter(Boolean))];
return html`
<${ModalHeader} title="Activity log" sub=${state.boardData[boardId]?.board.name} />
setWho('')}>Everyone
${people.map((u) => html` setWho(u)}><${Avatar} user=${state.usersById[u]} size=${20} /> ${state.usersById[u]?.name} `)}
`;
}
export function ImportCsvModal({ boardId }) {
const [busy, setBusy] = useState(false);
const pick = async (file) => {
if (!file) return;
setBusy(true);
try {
const text = await file.text();
const r = await api.post(`/api/boards/${boardId}/import.csv`, text, { contentType: 'text/csv' });
toast(`Imported ${r.created} items`); closeModal(); loadBoard(boardId);
} catch (e) { toast(e.message, 'error'); }
setBusy(false);
};
return html``;
}
export function ColumnSettingsModal({ boardId, colId }) {
const s = useStore();
const col = s.boardData[boardId]?.columns.find((c) => c.id === colId);
const [title, setTitle] = useState(col?.title || '');
const [type, setType] = useState(col?.type);
const [settings, setSettings] = useState(() => JSON.parse(JSON.stringify(col?.settings || {})));
if (!col) return null;
const labels = settings.labels || [];
const setLabels = (l) => setSettings({ ...settings, labels: l });
const save = async () => {
const patch = {};
if (title.trim() && title !== col.title) patch.title = title.trim();
if (type !== col.type) patch.type = type;
else patch.settings = settings;
await A.patchColumn(boardId, colId, patch);
if (patch.type) await loadBoard(boardId);
closeModal();
};
return html`<${ModalHeader} title="Column settings" />
Title setTitle(e.target.value)} />
Type
setType(e.target.value)}>
${Object.entries(COLUMN_TYPES).filter(([k]) => k !== 'priority').map(([k, t]) => html`${t.label} `)}
${type !== col.type ? html`
Changing the type keeps values that convert to text cleanly and clears the rest.
` : null}
${type === col.type && (col.type === 'status' || col.type === 'dropdown') ? html`
Labels
${labels.map((l, i) => html`
setLabels(labels.map((x, j) => (j === i ? { ...x, color: e.target.value } : x)))} />
setLabels(labels.map((x, j) => (j === i ? { ...x, name: e.target.value } : x)))} />
${col.type === 'status' ? html` setLabels(labels.map((x, j) => (j === i ? { ...x, done: e.target.checked } : x)))} /> Done ` : null}
{ const n = [...labels]; [n[i - 1], n[i]] = [n[i], n[i - 1]]; setLabels(n); }}><${Icon} name="chevUp" />
setLabels(labels.filter((_, j) => j !== i))}><${Icon} name="trash" />
`)}
setLabels([...labels, { id: Math.random().toString(36).slice(2, 9), name: 'New label', color: LABEL_COLORS[labels.length % LABEL_COLORS.length] }])}><${Icon} name="plus" /> Add label
${labels.map((l) => html`${l.name} `)}
` : null}
${type === col.type && col.type === 'number' ? html`
Unit
setSettings({ ...settings, unit: e.target.value })} />` : null}
${type === col.type && col.type === 'rating' ? html`
Maximum
setSettings({ ...settings, max: Number(e.target.value) })}>${[3, 4, 5, 6, 7, 8, 9, 10].map((n) => html`${n} `)} ` : null}
Cancel Save
`;
}