soja-cli 1.5.1 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/application/finance.js +1 -0
- package/dist/application/intake.js +1 -0
- package/dist/application/services/task-service.js +26 -2
- package/dist/application/services/workspace-service.js +5 -2
- package/dist/application/timeline.js +2 -0
- package/dist/cli/commands/workspace.js +12 -1
- package/dist/cli/output.js +2 -1
- package/dist/data/local/tasks.js +5 -1
- package/dist/data/local/workspaces.js +6 -3
- package/dist/data/sync/engine.js +1 -1
- package/dist/data/sync/finance.js +15 -0
- package/dist/data/sync/index.js +4 -0
- package/dist/data/sync/intake.js +27 -0
- package/dist/data/sync/services.js +7 -2
- package/dist/data/sync/store.js +3 -3
- package/dist/database/migrations/0007_finances.sql +5 -0
- package/dist/database/migrations/0008_finances_designates.sql +44 -0
- package/dist/database/migrations/0009_external_approval.sql +1 -0
- package/dist/database/migrations/meta/0008_snapshot.json +1221 -0
- package/dist/database/migrations/meta/0009_snapshot.json +1228 -0
- package/dist/database/migrations/meta/_journal.json +21 -0
- package/dist/database/schema.js +7 -0
- package/dist/ui/Shell.js +7 -0
- package/dist/ui/chrome/Sidebar.js +1 -1
- package/dist/ui/components/TaskTable.js +1 -1
- package/dist/ui/hooks/use-commands.js +1 -0
- package/dist/ui/hooks/use-task-actions.js +36 -0
- package/dist/ui/screens/FinanceScreen.js +88 -0
- package/dist/ui/screens/HelpScreen.js +9 -0
- package/dist/ui/screens/ProjectsScreen.js +185 -1
- package/dist/ui/screens/TaskScreen.js +9 -1
- package/dist/ui/screens/WorkspacesScreen.js +29 -1
- package/package.json +1 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Box, Text } from 'ink';
|
|
3
|
+
import { useState } from 'react';
|
|
3
4
|
import { useAppState } from '../app-state.js';
|
|
4
5
|
import { useFlows } from '../hooks/use-flows.js';
|
|
5
6
|
import { useLayout } from '../hooks/use-layout.js';
|
|
@@ -14,6 +15,8 @@ import { Panel } from '../kit/Panel.js';
|
|
|
14
15
|
import { ScreenFrame } from './ScreenFrame.js';
|
|
15
16
|
export function WorkspacesScreen({ active }) {
|
|
16
17
|
const { services, session } = useAppState();
|
|
18
|
+
const { invalidateQueries, run } = useAppState();
|
|
19
|
+
const [membersFocus, setMembersFocus] = useState(false);
|
|
17
20
|
const { height, width } = useLayout();
|
|
18
21
|
const data = useQuery(async () => {
|
|
19
22
|
const [workspaces, members] = await Promise.all([
|
|
@@ -26,8 +29,31 @@ export function WorkspacesScreen({ active }) {
|
|
|
26
29
|
const members = data.data?.members ?? [];
|
|
27
30
|
const listRows = Math.max(3, Math.min(workspaces.length, height - 6 - Math.min(members.length, 8)));
|
|
28
31
|
const list = useList(workspaces.length, listRows, true, Math.max(0, workspaces.findIndex((w) => w.id === session.workspace.id)));
|
|
32
|
+
const memberList = useList(Math.min(members.length, 10), Math.max(1, Math.min(members.length, 8)), membersFocus);
|
|
33
|
+
const isOwner = workspaces.find((workspace) => workspace.id === session.workspace.id)?.role === 'owner';
|
|
29
34
|
const flows = useFlows();
|
|
30
35
|
useKeys(Layer.screen, (input, key) => {
|
|
36
|
+
if (input === '\t') {
|
|
37
|
+
setMembersFocus((focused) => !focused);
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
if (membersFocus) {
|
|
41
|
+
if (memberList.handleKey(input, key))
|
|
42
|
+
return true;
|
|
43
|
+
if (key.escape) {
|
|
44
|
+
setMembersFocus(false);
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
const member = members[memberList.index];
|
|
48
|
+
if (input === 'd' && isOwner && member) {
|
|
49
|
+
void run(async () => {
|
|
50
|
+
await services.workspaces.setDesignated(session, member.id, !member.designated);
|
|
51
|
+
invalidateQueries([`workspaces:${session.workspace.id}`]);
|
|
52
|
+
}, `${member.designated ? 'Removed designate access from' : 'Designated'} @${member.username}`);
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
31
57
|
if (list.handleKey(input, key))
|
|
32
58
|
return true;
|
|
33
59
|
if (key.ctrl || key.meta)
|
|
@@ -49,6 +75,8 @@ export function WorkspacesScreen({ active }) {
|
|
|
49
75
|
['enter', 'switch'],
|
|
50
76
|
['n', 'new workspace'],
|
|
51
77
|
['a', 'add developer'],
|
|
78
|
+
['tab', 'members'],
|
|
79
|
+
...(isOwner ? [['d', 'toggle designate']] : []),
|
|
52
80
|
['esc', 'back'],
|
|
53
81
|
], children: _jsxs(Box, { flexDirection: wide ? 'row' : 'column', gap: 1, children: [_jsx(Panel, { title: "Your workspaces", flexGrow: 1, children: workspaces.slice(list.offset, list.offset + listRows).map((workspace, position) => {
|
|
54
82
|
const index = list.offset + position;
|
|
@@ -56,7 +84,7 @@ export function WorkspacesScreen({ active }) {
|
|
|
56
84
|
const current = workspace.id === session.workspace.id;
|
|
57
85
|
const bg = selected ? { backgroundColor: palette.selection } : {};
|
|
58
86
|
return (_jsx(Clickable, { active: active, onClick: () => (selected ? void flows.switchWorkspace(workspace.id) : list.select(index)), children: _jsxs(Box, { gap: 1, flexGrow: 1, ...bg, children: [_jsx(Text, { color: palette.accent, children: selected ? symbols.pointer : ' ' }), _jsx(Text, { color: palette.text, bold: selected, wrap: "truncate-end", children: workspace.name }), _jsx(Text, { color: palette.muted, wrap: "truncate-end", children: `${workspace.slug} ${symbols.dot} ${workspace.role}` }), current ? _jsx(Badge, { tone: "success", children: "current" }) : null] }) }, workspace.id));
|
|
59
|
-
}) }), _jsxs(Panel, { title: `Developers in ${session.workspace.name}`, aside: `${members.length}`, width: wide ? Math.floor(width * 0.45) : undefined, flexShrink: 0, children: [members.slice(0, 10).map((member) => (_jsxs(Box, { gap: 1, children: [_jsx(Text, { backgroundColor: palette.neutralSoft, color: palette.accent, bold: true, children: ` ${initials(member.displayName)} ` }), _jsx(Text, { color: palette.text, children: `@${member.username}` }), _jsx(Text, { color: palette.muted, wrap: "truncate-end", children: member.displayName }), member.role === 'owner' ? _jsx(Badge, { tone: "info", children: "owner" }) : null] }, member.id))), members.length > 10 ? _jsx(Text, { color: palette.faint, children: `${symbols.ellipsis} and ${members.length - 10} more` }) : null, _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: palette.faint, children:
|
|
87
|
+
}) }), _jsxs(Panel, { title: `Developers in ${session.workspace.name}`, aside: `${members.length}`, width: wide ? Math.floor(width * 0.45) : undefined, flexShrink: 0, children: [members.slice(0, 10).map((member, index) => (_jsx(Clickable, { active: active, onClick: () => { memberList.select(index); setMembersFocus(true); }, children: _jsxs(Box, { gap: 1, flexGrow: 1, ...(membersFocus && index === memberList.index ? { backgroundColor: palette.selection } : {}), children: [_jsx(Text, { backgroundColor: palette.neutralSoft, color: palette.accent, bold: true, children: ` ${initials(member.displayName)} ` }), _jsx(Text, { color: palette.text, children: `@${member.username}` }), _jsx(Text, { color: palette.muted, wrap: "truncate-end", children: member.displayName }), member.role === 'owner' ? _jsx(Badge, { tone: "info", children: "owner" }) : null, member.designated && member.role !== 'owner' ? _jsx(Badge, { tone: "success", children: "designate" }) : null] }) }, member.id))), members.length > 10 ? _jsx(Text, { color: palette.faint, children: `${symbols.ellipsis} and ${members.length - 10} more` }) : null, _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: palette.faint, children: isOwner ? 'a add developer · tab focus members · d toggle designate' : 'a adds a developer' }) })] })] }) }));
|
|
60
88
|
}
|
|
61
89
|
function initials(name) {
|
|
62
90
|
const parts = name.trim().split(/\s+/);
|