diff --git a/mast-react-vite/src/App.tsx b/mast-react-vite/src/App.tsx index e30b1a2..7f5d895 100644 --- a/mast-react-vite/src/App.tsx +++ b/mast-react-vite/src/App.tsx @@ -40,15 +40,18 @@ export const columns: ColumnDef[] = [ { id: "select", header: ({ table }) => ( - table.toggleAllPageRowsSelected(!!value)} - aria-label="Select all" - className="translate-y-[2px]" - /> +
+

0

+ table.toggleAllPageRowsSelected(!!value)} + aria-label="Select all" + className="translate-y-[2px]" + /> +
), cell: ({ row, table }) => (
@@ -99,7 +102,7 @@ export const columns: ColumnDef[] = [ ] function App({ ctx }) { - const todos = useQuery(ctx, "SELECT * FROM todos").data; + const todos = useQuery(ctx, "SELECT * FROM todos where completed = 0").data; const [newText, setNewText] = useState(""); const [rowSelection, setRowSelection] = useState({}); @@ -118,15 +121,70 @@ function App({ ctx }) { } const parseTodos = (e) => { - if (e.target.value.trim() !== "") { + // On enter execute the command + if (e.key === "Enter") { + console.log("enter confirmed") try { const parsed = commandParser.parse(e.target.value); + // TODO: + // We need to identify the type of the command first + // Currently assumes that it is done + switch (parsed.type) { + case "done": + if (parsed.filters && parsed.filters.length > 0) { + const idFilters = parsed.filters.filter(f => f.type === "id"); + + // Get the row IDs from the table + const rowIds = idFilters.flatMap(filter => + filter.ids.map(id => todos[id - 1]?.id) + ).filter(Boolean); + + // Execute SQL update for each row + if (rowIds.length > 0) { + rowIds.forEach(id => { + ctx.db.exec( + `UPDATE todos SET completed = 1 WHERE id = ?`, + [id] + ); + }); + } + + setNewText(""); + setRowSelection({}); + } + case "add": + ctx.db.exec(`INSERT INTO todos (id, description, tags, project, completed) + VALUES (lower(hex(randomblob(16))), ?, ?, ?, 0)`, + [ + parsed.description, + parsed.tags, + parsed.project + ]); + setNewText(""); + } + } catch (error) { + // TODO: + // This is actually bad + // We want to throw an error to the user here + console.log("Unable to parse field"); + return; + } + } + // React to key presses for selection + else if (e.target.value.trim() !== "") { + try { + const parsed = commandParser.parse(e.target.value); + // TODO: + // We want to reactively update based on command still + // Right now this assumes the command is filter - console.log(parsed) if (parsed.filters && parsed.filters.length > 0) { const idFilters = parsed.filters.filter(f => f.type === "id"); - console.log(idFilters) + // TODO: + // We should probably have selection state react to changes on NewText + // Rather than manually updating here + // Create new selection state const newSelection = {}; idFilters.forEach(filter => { diff --git a/parser/command_js.peg b/parser/command_js.peg index 59655ff..614a2b4 100644 --- a/parser/command_js.peg +++ b/parser/command_js.peg @@ -4,24 +4,43 @@ const validParts = parts || []; // Extract pure description (without attributes) const description = validParts - .filter(part => typeof part === 'string') + .filter(part => part.type === 'text') + .map(part => part.value) .join(' ') .trim(); // Collect all attributes const attributes = validParts - .filter(part => typeof part === 'object') + .filter(part => part.type !== 'text') .filter(part => part !== null); + + const project = validParts + .filter(part => part.type === 'project') + .map(part => part.value) + .join('') + .trim(); + + const tags = validParts + .filter(part => part.type === 'tag') + .map(part => part.value); + // Store original parts for reconstruction const originalParts = validParts.filter(part => part !== null); - + let validFilters; + if (!!filters) { + validFilters = filters.flat(); + } else { + validFilters = []; + } return { type: type, - filters: filters.flat() || [], description: description || [], attributes: attributes || [], + filters: validFilters, + project: project, + tags: JSON.stringify(tags), parts: parts || [], reconstruct: function() { const filterStr = this.filters.map(f => f.reconstruct()).join(','); @@ -71,7 +90,7 @@ SingleId = id:Integer { Filter = IdFilter / Tag / Project / Priority / Due -IdFilter = first:(IdRange / SingleId) rest:("," (IdRange / SingleId))* trailing:"," ? { +IdFilter = first:(IdRange / SingleId) rest:("," (IdRange / SingleId))* trailing:"," ? { const ids = [first, ...rest.map(r => r[1])].flat(); return { type: 'id', diff --git a/parser/command_parser.js b/parser/command_parser.js index b8e04b3..bb19189 100644 --- a/parser/command_parser.js +++ b/parser/command_parser.js @@ -1219,24 +1219,43 @@ function peg$parse(input, options) { const validParts = parts || []; // Extract pure description (without attributes) const description = validParts - .filter(part => typeof part === 'string') + .filter(part => part.type === 'text') + .map(part => part.value) .join(' ') .trim(); // Collect all attributes const attributes = validParts - .filter(part => typeof part === 'object') + .filter(part => part.type !== 'text') .filter(part => part !== null); + + const project = validParts + .filter(part => part.type === 'project') + .map(part => part.value) + .join('') + .trim(); + + const tags = validParts + .filter(part => part.type === 'tag') + .map(part => part.value); + // Store original parts for reconstruction const originalParts = validParts.filter(part => part !== null); - + let validFilters; + if (!!filters) { + validFilters = filters.flat(); + } else { + validFilters = []; + } return { type: type, - filters: filters.flat() || [], description: description || [], attributes: attributes || [], + filters: validFilters, + project: project, + tags: JSON.stringify(tags), parts: parts || [], reconstruct: function() { const filterStr = this.filters.map(f => f.reconstruct()).join(',');