cohvu 2.20.0 → 2.21.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/__tests__/instruction-file.unit.test.d.ts +1 -0
- package/dist/__tests__/instruction-file.unit.test.js +296 -0
- package/dist/__tests__/instruction-file.unit.test.js.map +1 -0
- package/dist/api.d.ts +17 -9
- package/dist/api.js +18 -9
- package/dist/api.js.map +1 -1
- package/dist/index.js +18 -6
- package/dist/index.js.map +1 -1
- package/dist/instructions.d.ts +29 -1
- package/dist/instructions.js +88 -1
- package/dist/instructions.js.map +1 -1
- package/dist/platforms.js +1 -1
- package/dist/platforms.js.map +1 -1
- package/dist/proxy.js +17 -3
- package/dist/proxy.js.map +1 -1
- package/dist/setup.js +74 -36
- package/dist/setup.js.map +1 -1
- package/dist/teardown.js +38 -35
- package/dist/teardown.js.map +1 -1
- package/dist/tui/App.js +186 -79
- package/dist/tui/App.js.map +1 -1
- package/dist/tui/components/Banner.js +4 -13
- package/dist/tui/components/Banner.js.map +1 -1
- package/dist/tui/components/Footer.js +9 -9
- package/dist/tui/components/Footer.js.map +1 -1
- package/dist/tui/components/Header.js +8 -3
- package/dist/tui/components/Header.js.map +1 -1
- package/dist/tui/components/Modal.js +16 -8
- package/dist/tui/components/Modal.js.map +1 -1
- package/dist/tui/state.d.ts +54 -36
- package/dist/tui/state.js +69 -64
- package/dist/tui/state.js.map +1 -1
- package/dist/tui/tabs/KnowledgeTab.js +28 -28
- package/dist/tui/tabs/KnowledgeTab.js.map +1 -1
- package/dist/tui/tabs/TeamTab.js +1 -1
- package/dist/tui/tabs/YouTab.js +16 -2
- package/dist/tui/tabs/YouTab.js.map +1 -1
- package/package.json +2 -2
package/dist/teardown.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"teardown.js","sourceRoot":"","sources":["../src/teardown.ts"],"names":[],"mappings":"AAAA,0CAA0C;AAC1C,oEAAoE;AACpE,mEAAmE;AAEnE,OAAO,EACL,UAAU,EACV,YAAY,EACZ,aAAa,EACb,UAAU,GAEX,MAAM,IAAI,CAAC;AACZ,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAC7B,OAAO,EAAE,IAAI,EAAW,MAAM,MAAM,CAAC;AACrC,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"teardown.js","sourceRoot":"","sources":["../src/teardown.ts"],"names":[],"mappings":"AAAA,0CAA0C;AAC1C,oEAAoE;AACpE,mEAAmE;AAEnE,OAAO,EACL,UAAU,EACV,YAAY,EACZ,aAAa,EACb,UAAU,GAEX,MAAM,IAAI,CAAC;AACZ,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAC7B,OAAO,EAAE,IAAI,EAAW,MAAM,MAAM,CAAC;AACrC,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,0BAA0B,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACrC,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAgBtC,8EAA8E;AAC9E,sBAAsB;AACtB,8EAA8E;AAE9E,SAAS,kBAAkB,CAAC,QAAgB,EAAE,OAAe;IAC3D,IAAI,CAAC;QACH,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;YAAE,OAAO,SAAS,CAAC;QAE5C,MAAM,GAAG,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;QACnD,IAAI,CAAC,GAAG;YAAE,OAAO,SAAS,CAAC;QAE3B,IAAI,MAA+B,CAAC;QACpC,IAAI,CAAC;YACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAA4B,CAAC;QACtD,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,MAAM,EAAE,sBAAsB,EAAE,CAAC;QAC5C,CAAC;QAED,oCAAoC;QACpC,IAAI,UAAU,GAAG,KAAK,CAAC;QACvB,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAwC,CAAC;QACvE,IAAI,OAAO,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;YAC7B,OAAO,OAAO,CAAC,KAAK,CAAC;YACrB,MAAM,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;YAC1B,UAAU,GAAG,IAAI,CAAC;QACpB,CAAC;QAED,2DAA2D;QAC3D,gEAAgE;QAChE,gEAAgE;QAChE,kEAAkE;QAClE,oEAAoE;QACpE,MAAM,QAAQ,GAAG,MAAM,CAAC,QAA+C,CAAC;QACxE,IAAI,QAAQ,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;YAC7C,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC5C,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAwC,CAAC;gBACtE,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;oBAAE,SAAS;gBAChD,MAAM,WAAW,GAAG,IAAI,CAAC,UAAiD,CAAC;gBAC3E,IAAI,WAAW,IAAI,WAAW,CAAC,KAAK,EAAE,CAAC;oBACrC,OAAO,WAAW,CAAC,KAAK,CAAC;oBACzB,IAAI,CAAC,UAAU,GAAG,WAAW,CAAC;oBAC9B,UAAU,GAAG,IAAI,CAAC;gBACpB,CAAC;YACH,CAAC;QACH,CAAC;QAED,IAAI,CAAC,UAAU;YAAE,OAAO,SAAS,CAAC;QAElC,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;QAChE,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,EAAE,MAAM,EAAE,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;IACrF,CAAC;AACH,CAAC;AAED,SAAS,kBAAkB,CAAC,QAAgB;IAC1C,IAAI,CAAC;QACH,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;YAAE,OAAO,SAAS,CAAC;QAE5C,MAAM,OAAO,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAChD,qEAAqE;QACrE,oEAAoE;QACpE,oEAAoE;QACpE,kEAAkE;QAClE,MAAM,QAAQ,GAAG,mCAAmC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACnE,IAAI,CAAC,QAAQ;YAAE,OAAO,SAAS,CAAC;QAEhC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAClC,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,IAAI,QAAQ,GAAG,KAAK,CAAC;QAErB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,gEAAgE;YAChE,IAAI,wCAAwC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC;gBAC/D,QAAQ,GAAG,IAAI,CAAC;gBAChB,SAAS;YACX,CAAC;YACD,wDAAwD;YACxD,IAAI,QAAQ,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,oCAAoC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC;gBACvG,QAAQ,GAAG,KAAK,CAAC;YACnB,CAAC;YACD,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACd,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACpB,CAAC;QACH,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC;QAC3E,aAAa,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QACjC,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,EAAE,MAAM,EAAE,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;IACrF,CAAC;AACH,CAAC;AAED,8EAA8E;AAC9E,uBAAuB;AACvB,8EAA8E;AAE9E,SAAS,aAAa,CAAC,QAAgB;IACrC,IAAI,CAAC;QACH,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;YAAE,OAAO,SAAS,CAAC;QAC5C,UAAU,CAAC,QAAQ,CAAC,CAAC;QACrB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,EAAE,MAAM,EAAE,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;IACrF,CAAC;AACH,CAAC;AAED,SAAS,kBAAkB,CAAC,QAAgB,EAAE,MAA0B;IACtE,IAAI,MAAM,KAAK,KAAK;QAAE,OAAO,aAAa,CAAC,QAAQ,CAAC,CAAC;IACrD,OAAO,0BAA0B,CAAC,QAAQ,CAAC,CAAC;AAC9C,CAAC;AAED,8EAA8E;AAC9E,sBAAsB;AACtB,8EAA8E;AAE9E,SAAS,iBAAiB,CAAC,QAAgB;IACzC,IAAI,CAAC;QACH,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;YAAE,OAAO,SAAS,CAAC;QAE5C,MAAM,GAAG,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;QACnD,IAAI,CAAC,GAAG;YAAE,OAAO,SAAS,CAAC;QAE3B,IAAI,MAA+B,CAAC;QACpC,IAAI,CAAC;YACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAA4B,CAAC;QACtD,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,MAAM,EAAE,sBAAsB,EAAE,CAAC;QAC5C,CAAC;QAED,MAAM,WAAW,GAAG,MAAM,CAAC,WAAkD,CAAC;QAC9E,IAAI,CAAC,WAAW;YAAE,OAAO,SAAS,CAAC;QAEnC,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,WAAW,CAAC,KAAK,CAAa,CAAC,CAAC,CAAC,EAAE,CAAC;QACzF,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC,CAAC;QAC1E,IAAI,QAAQ,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM;YAAE,OAAO,SAAS,CAAC;QAEvD,WAAW,CAAC,KAAK,GAAG,QAAQ,CAAC;QAC7B,MAAM,CAAC,WAAW,GAAG,WAAW,CAAC;QAEjC,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;QAChE,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,EAAE,MAAM,EAAE,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;IACrF,CAAC;AACH,CAAC;AAED,8EAA8E;AAC9E,cAAc;AACd,8EAA8E;AAE9E,SAAS,UAAU,CAAC,IAAY;IAC9B,IAAI,CAAC;QACH,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;YAAE,OAAO,KAAK,CAAC;QACpC,UAAU,CAAC,IAAI,CAAC,CAAC;QACjB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,8EAA8E;AAC9E,eAAe;AACf,8EAA8E;AAE9E,SAAS,OAAO,CAAC,YAAoB;IACnC,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,YAAY,CAAC,CAAC;AACvC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW;IAC/B,MAAM,QAAQ,GAAG,eAAe,EAAE,CAAC;IACnC,MAAM,OAAO,GAAqB,EAAE,CAAC;IAErC,KAAK,MAAM,EAAE,GAAG,EAAE,IAAI,QAAQ,EAAE,CAAC;QAC/B,MAAM,MAAM,GAAmB;YAC7B,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,GAAG,EAAE,IAAI;YACT,YAAY,EAAE,IAAI;YAClB,WAAW,EAAE,IAAI;SAClB,CAAC;QAEF,IAAI,GAAG,CAAC,GAAG,EAAE,CAAC;YACZ,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YACvC,IAAI,GAAG,CAAC,GAAG,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;gBAC9B,MAAM,CAAC,GAAG,GAAG,kBAAkB,CAAC,QAAQ,CAAC,CAAC;YAC5C,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,GAAG,GAAG,kBAAkB,CAAC,QAAQ,EAAE,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YAC7D,CAAC;QACH,CAAC;QAED,IAAI,GAAG,CAAC,YAAY,EAAE,CAAC;YACrB,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;YAChD,MAAM,CAAC,YAAY,GAAG,kBAAkB,CAAC,QAAQ,EAAE,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;QAC9E,CAAC;QAED,IAAI,GAAG,CAAC,WAAW,EAAE,CAAC;YACpB,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;YAC/C,MAAM,CAAC,WAAW,GAAG,iBAAiB,CAAC,QAAQ,CAAC,CAAC;QACnD,CAAC;QAED,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACvB,CAAC;IAED,uEAAuE;IACvE,yEAAyE;IACzE,uEAAuE;IACvE,wEAAwE;IACxE,gDAAgD;IAChD,MAAM,uBAAuB,EAAE,CAAC;IAEhC,qBAAqB;IACrB,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;IACvB,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC;IAChD,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC;IAE3C,OAAO;QACL,SAAS,EAAE,OAAO;QAClB,WAAW,EAAE,IAAI;KAClB,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,uBAAuB;IACpC,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC;QAAE,OAAO;IAClD,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACnC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,SAAS,EAAE,CAAC;QAC/B,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,eAAe,EAAE,CAAC;QAC5C,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,MAAM,CAAC,CAAC;QACvD,IAAI,IAAI,EAAE,CAAC;YACT,MAAM,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACrC,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,iEAAiE;QACjE,qEAAqE;IACvE,CAAC;AACH,CAAC"}
|
package/dist/tui/App.js
CHANGED
|
@@ -92,24 +92,24 @@ export default function App() {
|
|
|
92
92
|
// Guard: ignore events if we've since switched to a different project
|
|
93
93
|
if (stateRef.current.activeProjectId !== projectId)
|
|
94
94
|
return;
|
|
95
|
-
if (eventType === '
|
|
95
|
+
if (eventType === 'contribution') {
|
|
96
96
|
const event = data;
|
|
97
97
|
if (event.operation === 'create') {
|
|
98
|
-
dispatch({ type: '
|
|
99
|
-
dispatch({ type: 'SET_LIVE_DOT',
|
|
98
|
+
dispatch({ type: 'ADD_CONTRIBUTION', contribution: { id: event.id, body: event.body, updated_at: event.updated_at, contributed_by: event.contributed_by, contribution_type: event.contribution_type } });
|
|
99
|
+
dispatch({ type: 'SET_LIVE_DOT', contributionId: event.id });
|
|
100
100
|
if (liveDotTimerRef.current)
|
|
101
101
|
clearTimeout(liveDotTimerRef.current);
|
|
102
102
|
liveDotTimerRef.current = setTimeout(() => dispatch({ type: 'CLEAR_LIVE_DOT' }), 10000);
|
|
103
103
|
}
|
|
104
104
|
else if (event.operation === 'update') {
|
|
105
|
-
dispatch({ type: '
|
|
106
|
-
dispatch({ type: 'SET_LIVE_DOT',
|
|
105
|
+
dispatch({ type: 'UPDATE_CONTRIBUTION', contribution: { id: event.id, body: event.body, updated_at: event.updated_at } });
|
|
106
|
+
dispatch({ type: 'SET_LIVE_DOT', contributionId: event.id });
|
|
107
107
|
if (liveDotTimerRef.current)
|
|
108
108
|
clearTimeout(liveDotTimerRef.current);
|
|
109
109
|
liveDotTimerRef.current = setTimeout(() => dispatch({ type: 'CLEAR_LIVE_DOT' }), 10000);
|
|
110
110
|
}
|
|
111
111
|
else if (event.operation === 'delete') {
|
|
112
|
-
dispatch({ type: '
|
|
112
|
+
dispatch({ type: 'REMOVE_CONTRIBUTION', id: event.id });
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
115
|
else if (eventType === 'role_change') {
|
|
@@ -148,11 +148,11 @@ export default function App() {
|
|
|
148
148
|
},
|
|
149
149
|
onConnected: () => {
|
|
150
150
|
dispatch({ type: 'SET_SSE_CONNECTED', connected: true });
|
|
151
|
-
// Refresh
|
|
151
|
+
// Refresh contributions on reconnection — guard against stale project
|
|
152
152
|
if (stateRef.current.activeProjectId === projectId) {
|
|
153
|
-
api.
|
|
153
|
+
api.listContributions(projectId, { limit: PAGE_SIZE, offset: 0 }).then(result => {
|
|
154
154
|
if (result && stateRef.current.activeProjectId === projectId) {
|
|
155
|
-
dispatch({ type: '
|
|
155
|
+
dispatch({ type: 'SET_CONTRIBUTIONS', contributions: result.contributions, total: result.total });
|
|
156
156
|
}
|
|
157
157
|
}).catch(() => { });
|
|
158
158
|
}
|
|
@@ -176,11 +176,11 @@ export default function App() {
|
|
|
176
176
|
switch (s.tab) {
|
|
177
177
|
case 'knowledge': {
|
|
178
178
|
dispatch({ type: 'SET_LOADING', loading: true });
|
|
179
|
-
const result = await api.
|
|
179
|
+
const result = await api.listContributions(projectId, { limit: PAGE_SIZE, offset: 0 });
|
|
180
180
|
// Guard: project may have changed during async load
|
|
181
181
|
if (stateRef.current.activeProjectId !== projectId)
|
|
182
182
|
break;
|
|
183
|
-
dispatch({ type: '
|
|
183
|
+
dispatch({ type: 'SET_CONTRIBUTIONS', contributions: result.contributions, total: result.total });
|
|
184
184
|
break;
|
|
185
185
|
}
|
|
186
186
|
case 'team': {
|
|
@@ -242,12 +242,12 @@ export default function App() {
|
|
|
242
242
|
const loadMoreMemories = useCallback(async () => {
|
|
243
243
|
const s = stateRef.current;
|
|
244
244
|
const projectId = s.activeProjectId;
|
|
245
|
-
if (!projectId || !s.
|
|
245
|
+
if (!projectId || !s.contributionHasMore)
|
|
246
246
|
return;
|
|
247
247
|
dispatch({ type: 'SET_LOADING', loading: true });
|
|
248
248
|
try {
|
|
249
|
-
const result = await api.
|
|
250
|
-
dispatch({ type: '
|
|
249
|
+
const result = await api.listContributions(projectId, { limit: PAGE_SIZE, offset: s.contributions.length });
|
|
250
|
+
dispatch({ type: 'SET_CONTRIBUTIONS', contributions: result.contributions, total: result.total, append: true });
|
|
251
251
|
}
|
|
252
252
|
catch {
|
|
253
253
|
showToast('Failed to load more', 'error');
|
|
@@ -265,9 +265,9 @@ export default function App() {
|
|
|
265
265
|
if (ps.tab !== 'knowledge' || ps.activeProjectId !== projectId)
|
|
266
266
|
return;
|
|
267
267
|
try {
|
|
268
|
-
const result = await api.
|
|
269
|
-
if (result && stateRef.current.activeProjectId === projectId && result.total !== stateRef.current.
|
|
270
|
-
dispatch({ type: '
|
|
268
|
+
const result = await api.listContributions(projectId, { limit: PAGE_SIZE, offset: 0 });
|
|
269
|
+
if (result && stateRef.current.activeProjectId === projectId && result.total !== stateRef.current.contributionTotal) {
|
|
270
|
+
dispatch({ type: 'SET_CONTRIBUTIONS', contributions: result.contributions, total: result.total });
|
|
271
271
|
}
|
|
272
272
|
}
|
|
273
273
|
catch { }
|
|
@@ -298,8 +298,8 @@ export default function App() {
|
|
|
298
298
|
return;
|
|
299
299
|
dispatch({ type: 'SET_SEARCHING', searching: true });
|
|
300
300
|
try {
|
|
301
|
-
const result = await api.
|
|
302
|
-
dispatch({ type: 'SET_SEARCH_RESULTS', results: result.
|
|
301
|
+
const result = await api.searchContributions(projectId, s.searchQuery);
|
|
302
|
+
dispatch({ type: 'SET_SEARCH_RESULTS', results: result.contributions });
|
|
303
303
|
}
|
|
304
304
|
catch {
|
|
305
305
|
showToast('Search failed', 'error');
|
|
@@ -323,6 +323,18 @@ export default function App() {
|
|
|
323
323
|
if (cancelled)
|
|
324
324
|
return;
|
|
325
325
|
dispatch({ type: 'SET_USER_DATA', me });
|
|
326
|
+
// Locked-mode short-circuit. If the account is pending deletion
|
|
327
|
+
// (user hit `d` earlier, possibly from another machine or before
|
|
328
|
+
// reinstalling), skip all the data-loading fan-out — the server
|
|
329
|
+
// would reject those calls anyway. Force the You tab so the
|
|
330
|
+
// countdown + cancel flow is immediately visible; everything else
|
|
331
|
+
// stays empty until the user either presses `c` or the purge runs.
|
|
332
|
+
if (me.user.deletion_scheduled_at) {
|
|
333
|
+
dispatch({ type: 'SWITCH_TAB', tab: 'you' });
|
|
334
|
+
const platforms = detectPlatformStatuses();
|
|
335
|
+
dispatch({ type: 'SET_PLATFORMS', platforms });
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
326
338
|
// Detect platforms without re-running setup (setup already ran in enterDashboard)
|
|
327
339
|
const platforms = detectPlatformStatuses();
|
|
328
340
|
dispatch({ type: 'SET_PLATFORMS', platforms });
|
|
@@ -336,15 +348,21 @@ export default function App() {
|
|
|
336
348
|
const activeTeamId = isTeamProject && activeProject.owner.kind === 'team' ? activeProject.owner.teamId : null;
|
|
337
349
|
const activeTeam = activeTeamId ? me.teams.find(t => t.team_id === activeTeamId) : null;
|
|
338
350
|
if (projectId) {
|
|
339
|
-
|
|
351
|
+
// Initial-load failures are usually transient (backend warming up,
|
|
352
|
+
// SSE not yet connected, auxiliary endpoints slow). The UI renders
|
|
353
|
+
// empty states gracefully for any piece that's null, and the SSE
|
|
354
|
+
// onConnected handler re-fetches contributions once the stream is
|
|
355
|
+
// live. No toast needed here — showing "Some data failed to load"
|
|
356
|
+
// on first launch creates anxiety for a condition that resolves
|
|
357
|
+
// itself within seconds.
|
|
340
358
|
const [memResult, members, billing, inviteLinks, notifications] = await Promise.all([
|
|
341
|
-
api.
|
|
359
|
+
api.listContributions(projectId, { limit: PAGE_SIZE, offset: 0 }).catch(() => null),
|
|
342
360
|
isTeamProject && activeTeam
|
|
343
|
-
? api.listTeamMembers(activeTeam.team_id).catch(() =>
|
|
361
|
+
? api.listTeamMembers(activeTeam.team_id).catch(() => [])
|
|
344
362
|
: Promise.resolve([]),
|
|
345
363
|
isTeamProject && activeTeam
|
|
346
|
-
? api.getTeamBilling(activeTeam.team_id).catch(() =>
|
|
347
|
-
: api.getIndividualBilling().catch(() =>
|
|
364
|
+
? api.getTeamBilling(activeTeam.team_id).catch(() => null)
|
|
365
|
+
: api.getIndividualBilling().catch(() => null),
|
|
348
366
|
isTeamProject && activeTeam
|
|
349
367
|
? api.listTeamInviteLinks(activeTeam.team_id).catch(() => [])
|
|
350
368
|
: Promise.resolve([]),
|
|
@@ -352,10 +370,8 @@ export default function App() {
|
|
|
352
370
|
]);
|
|
353
371
|
if (cancelled)
|
|
354
372
|
return;
|
|
355
|
-
if (loadError)
|
|
356
|
-
showToast('Some data failed to load', 'error');
|
|
357
373
|
if (memResult)
|
|
358
|
-
dispatch({ type: '
|
|
374
|
+
dispatch({ type: 'SET_CONTRIBUTIONS', contributions: memResult.contributions, total: memResult.total });
|
|
359
375
|
dispatch({ type: 'SET_MEMBERS', members });
|
|
360
376
|
if (billing)
|
|
361
377
|
dispatch({ type: 'SET_BILLING', billing });
|
|
@@ -382,9 +398,9 @@ export default function App() {
|
|
|
382
398
|
if (s.tab !== 'knowledge' || s.activeProjectId !== projectId)
|
|
383
399
|
return;
|
|
384
400
|
try {
|
|
385
|
-
const result = await api.
|
|
386
|
-
if (result && stateRef.current.activeProjectId === projectId && result.total !== stateRef.current.
|
|
387
|
-
dispatch({ type: '
|
|
401
|
+
const result = await api.listContributions(projectId, { limit: PAGE_SIZE, offset: 0 });
|
|
402
|
+
if (result && stateRef.current.activeProjectId === projectId && result.total !== stateRef.current.contributionTotal) {
|
|
403
|
+
dispatch({ type: 'SET_CONTRIBUTIONS', contributions: result.contributions, total: result.total });
|
|
388
404
|
}
|
|
389
405
|
}
|
|
390
406
|
catch { }
|
|
@@ -469,7 +485,7 @@ export default function App() {
|
|
|
469
485
|
exit();
|
|
470
486
|
return;
|
|
471
487
|
}
|
|
472
|
-
// Global: q exits (unless modal open, help open, or in knowledge search/
|
|
488
|
+
// Global: q exits (unless modal open, help open, or in knowledge search/remove/detail mode)
|
|
473
489
|
if (input === 'q' && !s.modal && !s.showHelp && !(s.tab === 'knowledge' && s.knowledgeMode !== 'browse')) {
|
|
474
490
|
exit();
|
|
475
491
|
return;
|
|
@@ -567,15 +583,22 @@ export default function App() {
|
|
|
567
583
|
setTimeout(() => loadTabData(), 0);
|
|
568
584
|
return;
|
|
569
585
|
}
|
|
586
|
+
// Locked-mode short-circuit: when the account is pending deletion,
|
|
587
|
+
// the only meaningful action is `c` (cancel deletion) on the You tab,
|
|
588
|
+
// plus `q`/`ctrl-c` to exit. Other tabs would only show empty API
|
|
589
|
+
// failures, and billing shortcuts don't apply to a locked account.
|
|
590
|
+
const locked = !!s.user?.deletion_scheduled_at;
|
|
570
591
|
// Number keys switch tabs (but not when typing in search mode)
|
|
571
592
|
const tabIdx = parseInt(input, 10);
|
|
572
593
|
if (tabIdx >= 1 && tabIdx <= TABS.length && !key.ctrl && !key.meta && !(s.tab === 'knowledge' && s.knowledgeMode !== 'browse')) {
|
|
594
|
+
if (locked)
|
|
595
|
+
return;
|
|
573
596
|
dispatch({ type: 'SWITCH_TAB', tab: TABS[tabIdx - 1] });
|
|
574
597
|
setTimeout(() => loadTabData(), 0);
|
|
575
598
|
return;
|
|
576
599
|
}
|
|
577
600
|
// Global 'b' shortcut — subscribe/billing portal from banner
|
|
578
|
-
if (input === 'b' && s.userRole === 'admin' && s.tab !== 'billing' && !(s.tab === 'knowledge' && s.knowledgeMode !== 'browse')) {
|
|
601
|
+
if (input === 'b' && !locked && s.userRole === 'admin' && s.tab !== 'billing' && !(s.tab === 'knowledge' && s.knowledgeMode !== 'browse')) {
|
|
579
602
|
await handleBillingShortcut();
|
|
580
603
|
return;
|
|
581
604
|
}
|
|
@@ -628,6 +651,7 @@ export default function App() {
|
|
|
628
651
|
projectScopes: new Map(),
|
|
629
652
|
permProjectIdx: 0,
|
|
630
653
|
permActionIdx: 0,
|
|
654
|
+
globalScope: 'own',
|
|
631
655
|
limitsField: 'expiry',
|
|
632
656
|
expiresInDays: '',
|
|
633
657
|
opsLimit: '',
|
|
@@ -676,16 +700,16 @@ export default function App() {
|
|
|
676
700
|
// ---- Knowledge keys ----
|
|
677
701
|
async function handleKnowledgeKey(input, key) {
|
|
678
702
|
const s = stateRef.current;
|
|
679
|
-
// alt+d or ∂ enters
|
|
703
|
+
// alt+d or ∂ enters remove mode
|
|
680
704
|
if (((key.meta && input === 'd') || input === '∂') && s.userRole !== 'viewer') {
|
|
681
|
-
const list = s.searchResults ?? s.
|
|
705
|
+
const list = s.searchResults ?? s.contributions;
|
|
682
706
|
if (list.length > 0)
|
|
683
|
-
dispatch({ type: '
|
|
707
|
+
dispatch({ type: 'ENTER_REMOVE' });
|
|
684
708
|
return;
|
|
685
709
|
}
|
|
686
710
|
if (s.knowledgeMode === 'detail') {
|
|
687
711
|
if (key.escape || input === 'q') {
|
|
688
|
-
dispatch({ type: '
|
|
712
|
+
dispatch({ type: 'SET_DETAIL_CONTRIBUTION', contributionId: null });
|
|
689
713
|
}
|
|
690
714
|
return;
|
|
691
715
|
}
|
|
@@ -713,54 +737,54 @@ export default function App() {
|
|
|
713
737
|
}
|
|
714
738
|
return;
|
|
715
739
|
}
|
|
716
|
-
if (s.knowledgeMode === '
|
|
717
|
-
const
|
|
740
|
+
if (s.knowledgeMode === 'remove') {
|
|
741
|
+
const removeList = s.searchResults ?? s.contributions;
|
|
718
742
|
const filtered = s.userRole === 'admin'
|
|
719
|
-
?
|
|
720
|
-
:
|
|
721
|
-
if (s.
|
|
743
|
+
? removeList
|
|
744
|
+
: removeList.filter(m => m.contributed_by?.user_id === s.user?.id);
|
|
745
|
+
if (s.removeConfirming && input === 'y') {
|
|
722
746
|
const projectId = s.activeProjectId;
|
|
723
747
|
if (projectId) {
|
|
724
748
|
let failures = 0;
|
|
725
749
|
dispatch({ type: 'SET_OPERATION', operation: 'Removing contributions' });
|
|
726
|
-
await Promise.all([...s.
|
|
750
|
+
await Promise.all([...s.removeSelected].map(async (id) => {
|
|
727
751
|
try {
|
|
728
|
-
await api.
|
|
729
|
-
dispatch({ type: '
|
|
752
|
+
await api.deleteContribution(projectId, id);
|
|
753
|
+
dispatch({ type: 'REMOVE_CONTRIBUTION', id });
|
|
730
754
|
}
|
|
731
755
|
catch {
|
|
732
756
|
failures++;
|
|
733
757
|
}
|
|
734
758
|
}));
|
|
735
759
|
dispatch({ type: 'SET_OPERATION', operation: null });
|
|
736
|
-
dispatch({ type: '
|
|
760
|
+
dispatch({ type: 'EXIT_REMOVE' });
|
|
737
761
|
if (failures > 0)
|
|
738
762
|
showToast(`${failures} failed to remove`, 'error');
|
|
739
763
|
else
|
|
740
764
|
showToast('Removed', 'success');
|
|
741
765
|
}
|
|
742
766
|
}
|
|
743
|
-
else if (s.
|
|
744
|
-
dispatch({ type: '
|
|
767
|
+
else if (s.removeConfirming && (key.escape || input === 'n')) {
|
|
768
|
+
dispatch({ type: 'SET_REMOVE_CONFIRMING', confirming: false });
|
|
745
769
|
}
|
|
746
770
|
else if (key.escape) {
|
|
747
|
-
dispatch({ type: '
|
|
771
|
+
dispatch({ type: 'EXIT_REMOVE' });
|
|
748
772
|
}
|
|
749
773
|
else if (input === ' ') {
|
|
750
|
-
const mem = filtered[s.
|
|
774
|
+
const mem = filtered[s.contributionSelected];
|
|
751
775
|
if (mem)
|
|
752
|
-
dispatch({ type: '
|
|
776
|
+
dispatch({ type: 'TOGGLE_REMOVE', contributionId: mem.id });
|
|
753
777
|
}
|
|
754
778
|
else if (key.upArrow) {
|
|
755
|
-
if (s.
|
|
779
|
+
if (s.contributionSelected > 0)
|
|
756
780
|
dispatch({ type: 'SCROLL_UP' });
|
|
757
781
|
}
|
|
758
782
|
else if (key.downArrow) {
|
|
759
|
-
if (s.
|
|
783
|
+
if (s.contributionSelected < filtered.length - 1)
|
|
760
784
|
dispatch({ type: 'SCROLL_DOWN' });
|
|
761
785
|
}
|
|
762
|
-
else if (key.return && s.
|
|
763
|
-
dispatch({ type: '
|
|
786
|
+
else if (key.return && s.removeSelected.size > 0 && !s.removeConfirming) {
|
|
787
|
+
dispatch({ type: 'SET_REMOVE_CONFIRMING', confirming: true });
|
|
764
788
|
}
|
|
765
789
|
return;
|
|
766
790
|
}
|
|
@@ -768,18 +792,18 @@ export default function App() {
|
|
|
768
792
|
if (input === '/') {
|
|
769
793
|
dispatch({ type: 'ENTER_SEARCH' });
|
|
770
794
|
}
|
|
771
|
-
else if (input === 'd' && s.userRole !== 'viewer' && s.
|
|
772
|
-
dispatch({ type: '
|
|
795
|
+
else if (input === 'd' && s.userRole !== 'viewer' && s.contributions.length > 0) {
|
|
796
|
+
dispatch({ type: 'ENTER_REMOVE' });
|
|
773
797
|
}
|
|
774
798
|
else if (input === 'D' && s.userRole === 'admin') {
|
|
775
799
|
const project = getActiveProject(s);
|
|
776
800
|
if (project)
|
|
777
|
-
dispatch({ type: 'OPEN_MODAL', modal: { kind: 'confirm-
|
|
801
|
+
dispatch({ type: 'OPEN_MODAL', modal: { kind: 'confirm-remove-all', slug: project.slug, contributionCount: s.contributionTotal, input: '' } });
|
|
778
802
|
}
|
|
779
|
-
else if (key.return && s.
|
|
780
|
-
const mem = s.
|
|
803
|
+
else if (key.return && s.contributions.length > 0) {
|
|
804
|
+
const mem = s.contributions[s.contributionSelected];
|
|
781
805
|
if (mem)
|
|
782
|
-
dispatch({ type: '
|
|
806
|
+
dispatch({ type: 'SET_DETAIL_CONTRIBUTION', contributionId: mem.id });
|
|
783
807
|
}
|
|
784
808
|
else if (key.upArrow) {
|
|
785
809
|
dispatch({ type: 'SCROLL_UP' });
|
|
@@ -1033,16 +1057,17 @@ export default function App() {
|
|
|
1033
1057
|
else if (input === 'c' && s.userRole === 'admin') {
|
|
1034
1058
|
const project = getActiveProject(s);
|
|
1035
1059
|
if (project)
|
|
1036
|
-
dispatch({ type: 'OPEN_MODAL', modal: { kind: 'confirm-clear', slug: project.slug,
|
|
1060
|
+
dispatch({ type: 'OPEN_MODAL', modal: { kind: 'confirm-clear', slug: project.slug, contributionCount: s.contributionTotal, input: '' } });
|
|
1037
1061
|
}
|
|
1038
1062
|
else if (input === 'd' && s.userRole === 'admin') {
|
|
1039
1063
|
const project = getActiveProject(s);
|
|
1040
1064
|
if (project)
|
|
1041
|
-
dispatch({ type: 'OPEN_MODAL', modal: { kind: 'confirm-delete', slug: project.slug,
|
|
1065
|
+
dispatch({ type: 'OPEN_MODAL', modal: { kind: 'confirm-delete', slug: project.slug, contributionCount: s.contributionTotal, input: '' } });
|
|
1042
1066
|
}
|
|
1043
1067
|
}
|
|
1044
1068
|
// ---- You keys ----
|
|
1045
1069
|
async function handleYouKey(input, _key) {
|
|
1070
|
+
const s = stateRef.current;
|
|
1046
1071
|
if (input === 'r') {
|
|
1047
1072
|
await runSetup();
|
|
1048
1073
|
const platforms = detectPlatformStatuses();
|
|
@@ -1058,6 +1083,14 @@ export default function App() {
|
|
|
1058
1083
|
else if (input === 'l') {
|
|
1059
1084
|
dispatch({ type: 'OPEN_MODAL', modal: { kind: 'confirm-logout' } });
|
|
1060
1085
|
}
|
|
1086
|
+
else if (input === 'd' && !s.user?.deletion_scheduled_at) {
|
|
1087
|
+
// Open the delete-account confirmation. Email-typed confirm prevents
|
|
1088
|
+
// muscle-memory destruction.
|
|
1089
|
+
dispatch({ type: 'OPEN_MODAL', modal: { kind: 'confirm-account-delete', email: s.user?.email ?? '', input: '' } });
|
|
1090
|
+
}
|
|
1091
|
+
else if (input === 'c' && s.user?.deletion_scheduled_at) {
|
|
1092
|
+
dispatch({ type: 'OPEN_MODAL', modal: { kind: 'confirm-cancel-account-deletion' } });
|
|
1093
|
+
}
|
|
1061
1094
|
}
|
|
1062
1095
|
// ---- Billing shortcut (from banner) ----
|
|
1063
1096
|
async function handleBillingShortcut() {
|
|
@@ -1129,10 +1162,11 @@ export default function App() {
|
|
|
1129
1162
|
}
|
|
1130
1163
|
const modal = s.modal;
|
|
1131
1164
|
// y/n modals
|
|
1132
|
-
if (modal.kind === 'confirm-
|
|
1165
|
+
if (modal.kind === 'confirm-remove' || modal.kind === 'confirm-remove-member' ||
|
|
1133
1166
|
modal.kind === 'confirm-leave' || modal.kind === 'confirm-logout' ||
|
|
1134
1167
|
modal.kind === 'confirm-regen-link' || modal.kind === 'initiate-consensus' ||
|
|
1135
|
-
modal.kind === 'approve-action' || modal.kind === 'confirm-revoke-key'
|
|
1168
|
+
modal.kind === 'approve-action' || modal.kind === 'confirm-revoke-key' ||
|
|
1169
|
+
modal.kind === 'confirm-cancel-account-deletion') {
|
|
1136
1170
|
if (input === 'y') {
|
|
1137
1171
|
const willExit = modal.kind === 'confirm-logout';
|
|
1138
1172
|
await confirmModal(modal);
|
|
@@ -1144,6 +1178,25 @@ export default function App() {
|
|
|
1144
1178
|
}
|
|
1145
1179
|
return;
|
|
1146
1180
|
}
|
|
1181
|
+
// Info-only modals — any key dismisses.
|
|
1182
|
+
if (modal.kind === 'account-delete-blocked' || modal.kind === 'account-delete-scheduled') {
|
|
1183
|
+
dispatch({ type: 'CLOSE_MODAL' });
|
|
1184
|
+
return;
|
|
1185
|
+
}
|
|
1186
|
+
// Type-email-to-confirm: account delete.
|
|
1187
|
+
if (modal.kind === 'confirm-account-delete') {
|
|
1188
|
+
if (key.return && modal.input === modal.email) {
|
|
1189
|
+
await confirmModal(modal);
|
|
1190
|
+
return;
|
|
1191
|
+
}
|
|
1192
|
+
if (key.backspace || key.delete) {
|
|
1193
|
+
dispatch({ type: 'OPEN_MODAL', modal: { ...modal, input: modal.input.slice(0, -1) } });
|
|
1194
|
+
}
|
|
1195
|
+
else if (input.length === 1 && !key.ctrl && !key.meta) {
|
|
1196
|
+
dispatch({ type: 'OPEN_MODAL', modal: { ...modal, input: modal.input + input } });
|
|
1197
|
+
}
|
|
1198
|
+
return;
|
|
1199
|
+
}
|
|
1147
1200
|
// Integration key creation wizard
|
|
1148
1201
|
if (modal.kind === 'create-integration-key') {
|
|
1149
1202
|
// Silo rule: once one project is selected, only same-entity projects
|
|
@@ -1257,6 +1310,15 @@ export default function App() {
|
|
|
1257
1310
|
dispatch({ type: 'OPEN_MODAL', modal: { ...modal, projectScopes: scopes } });
|
|
1258
1311
|
}
|
|
1259
1312
|
}
|
|
1313
|
+
else if (key.return) {
|
|
1314
|
+
dispatch({ type: 'OPEN_MODAL', modal: { ...modal, step: 'scope' } });
|
|
1315
|
+
}
|
|
1316
|
+
return;
|
|
1317
|
+
}
|
|
1318
|
+
if (modal.step === 'scope') {
|
|
1319
|
+
if (key.upArrow || key.downArrow) {
|
|
1320
|
+
dispatch({ type: 'OPEN_MODAL', modal: { ...modal, globalScope: modal.globalScope === 'own' ? 'all' : 'own' } });
|
|
1321
|
+
}
|
|
1260
1322
|
else if (key.return) {
|
|
1261
1323
|
dispatch({ type: 'OPEN_MODAL', modal: { ...modal, step: 'limits' } });
|
|
1262
1324
|
}
|
|
@@ -1628,6 +1690,55 @@ export default function App() {
|
|
|
1628
1690
|
exit();
|
|
1629
1691
|
return false;
|
|
1630
1692
|
}
|
|
1693
|
+
// Account deletion flow.
|
|
1694
|
+
if (modal.kind === 'confirm-account-delete') {
|
|
1695
|
+
try {
|
|
1696
|
+
dispatch({ type: 'SET_OPERATION', operation: 'Scheduling deletion' });
|
|
1697
|
+
const result = await api.scheduleAccountDeletion();
|
|
1698
|
+
dispatch({ type: 'SET_OPERATION', operation: null });
|
|
1699
|
+
// Refresh the user record so the You tab reflects the locked state.
|
|
1700
|
+
const me = await api.me();
|
|
1701
|
+
dispatch({ type: 'SET_USER_DATA', me });
|
|
1702
|
+
dispatch({ type: 'OPEN_MODAL', modal: { kind: 'account-delete-scheduled', scheduledAt: result.deletion_scheduled_at } });
|
|
1703
|
+
}
|
|
1704
|
+
catch (err) {
|
|
1705
|
+
dispatch({ type: 'SET_OPERATION', operation: null });
|
|
1706
|
+
// 409 sole-admin → redirect to the blocked modal with the team list.
|
|
1707
|
+
// ApiError.body is a JSON string; parse it for the structured payload.
|
|
1708
|
+
let handled = false;
|
|
1709
|
+
const raw = err?.body;
|
|
1710
|
+
if (typeof raw === 'string') {
|
|
1711
|
+
try {
|
|
1712
|
+
const parsed = JSON.parse(raw);
|
|
1713
|
+
if (parsed.error?.code === 'SOLE_ADMIN' && parsed.error.details?.teams) {
|
|
1714
|
+
dispatch({ type: 'OPEN_MODAL', modal: { kind: 'account-delete-blocked', teams: parsed.error.details.teams } });
|
|
1715
|
+
handled = true;
|
|
1716
|
+
}
|
|
1717
|
+
}
|
|
1718
|
+
catch { }
|
|
1719
|
+
}
|
|
1720
|
+
if (!handled) {
|
|
1721
|
+
showToast('Failed to schedule deletion', 'error');
|
|
1722
|
+
dispatch({ type: 'CLOSE_MODAL' });
|
|
1723
|
+
}
|
|
1724
|
+
}
|
|
1725
|
+
return false;
|
|
1726
|
+
}
|
|
1727
|
+
if (modal.kind === 'confirm-cancel-account-deletion') {
|
|
1728
|
+
try {
|
|
1729
|
+
dispatch({ type: 'SET_OPERATION', operation: 'Canceling deletion' });
|
|
1730
|
+
await api.cancelAccountDeletion();
|
|
1731
|
+
const me = await api.me();
|
|
1732
|
+
dispatch({ type: 'SET_USER_DATA', me });
|
|
1733
|
+
dispatch({ type: 'SET_OPERATION', operation: null });
|
|
1734
|
+
showToast('Deletion canceled — account restored', 'success');
|
|
1735
|
+
}
|
|
1736
|
+
catch {
|
|
1737
|
+
dispatch({ type: 'SET_OPERATION', operation: null });
|
|
1738
|
+
showToast('Failed to cancel deletion', 'error');
|
|
1739
|
+
}
|
|
1740
|
+
return true;
|
|
1741
|
+
}
|
|
1631
1742
|
// These modals don't require an active project
|
|
1632
1743
|
if (modal.kind === 'create-project') {
|
|
1633
1744
|
if (!modal.input)
|
|
@@ -1705,11 +1816,7 @@ export default function App() {
|
|
|
1705
1816
|
dispatch({ type: 'SET_OPERATION', operation: 'Creating key' });
|
|
1706
1817
|
const projects = [...modal.selectedProjectIds].map(pid => ({
|
|
1707
1818
|
project_id: pid,
|
|
1708
|
-
|
|
1709
|
-
// cross-contributor access) is an explicit opt-in configured via the
|
|
1710
|
-
// HTTP API — prelaunch we don't surface it in the wizard to keep the
|
|
1711
|
-
// default safe and the flow simple.
|
|
1712
|
-
scope: "own",
|
|
1819
|
+
scope: modal.globalScope,
|
|
1713
1820
|
allowed_actions: [...(modal.projectScopes.get(pid) ?? [])],
|
|
1714
1821
|
}));
|
|
1715
1822
|
const result = await api.createApiKey({
|
|
@@ -1779,11 +1886,11 @@ export default function App() {
|
|
|
1779
1886
|
}
|
|
1780
1887
|
break;
|
|
1781
1888
|
}
|
|
1782
|
-
case 'confirm-
|
|
1889
|
+
case 'confirm-remove':
|
|
1783
1890
|
try {
|
|
1784
1891
|
dispatch({ type: 'SET_OPERATION', operation: 'Removing contribution' });
|
|
1785
|
-
await api.
|
|
1786
|
-
dispatch({ type: '
|
|
1892
|
+
await api.deleteContribution(projectId, modal.contributionId);
|
|
1893
|
+
dispatch({ type: 'REMOVE_CONTRIBUTION', id: modal.contributionId });
|
|
1787
1894
|
dispatch({ type: 'SET_OPERATION', operation: null });
|
|
1788
1895
|
showToast('Contribution removed', 'success');
|
|
1789
1896
|
}
|
|
@@ -1792,7 +1899,7 @@ export default function App() {
|
|
|
1792
1899
|
showToast('Failed to remove contribution', 'error');
|
|
1793
1900
|
}
|
|
1794
1901
|
break;
|
|
1795
|
-
case 'confirm-
|
|
1902
|
+
case 'confirm-remove-all':
|
|
1796
1903
|
case 'confirm-clear': {
|
|
1797
1904
|
const project = getActiveProject(s);
|
|
1798
1905
|
if (project && modal.input === project.slug) {
|
|
@@ -1800,7 +1907,7 @@ export default function App() {
|
|
|
1800
1907
|
dispatch({ type: 'SET_OPERATION', operation: 'Clearing contributions' });
|
|
1801
1908
|
// Server gates this behind consensus for team projects. If the
|
|
1802
1909
|
// response includes pending_approval, the action is queued.
|
|
1803
|
-
const resp = await api.
|
|
1910
|
+
const resp = await api.clearContributions(projectId);
|
|
1804
1911
|
if (resp.pending_approval) {
|
|
1805
1912
|
const team = getActiveTeam(s);
|
|
1806
1913
|
if (team) {
|
|
@@ -1811,7 +1918,7 @@ export default function App() {
|
|
|
1811
1918
|
showToast('Clear proposed — waiting for majority', 'info');
|
|
1812
1919
|
}
|
|
1813
1920
|
else {
|
|
1814
|
-
dispatch({ type: '
|
|
1921
|
+
dispatch({ type: 'SET_CONTRIBUTIONS', contributions: [], total: 0 });
|
|
1815
1922
|
dispatch({ type: 'SET_OPERATION', operation: null });
|
|
1816
1923
|
showToast('All contributions cleared', 'success');
|
|
1817
1924
|
}
|
|
@@ -1844,7 +1951,7 @@ export default function App() {
|
|
|
1844
1951
|
}
|
|
1845
1952
|
if (pollIntervalRef.current)
|
|
1846
1953
|
clearInterval(pollIntervalRef.current);
|
|
1847
|
-
dispatch({ type: '
|
|
1954
|
+
dispatch({ type: 'SET_CONTRIBUTIONS', contributions: [], total: 0 });
|
|
1848
1955
|
dispatch({ type: 'SET_MEMBERS', members: [] });
|
|
1849
1956
|
}
|
|
1850
1957
|
showToast('Project deleted', 'success');
|
|
@@ -1895,7 +2002,7 @@ export default function App() {
|
|
|
1895
2002
|
}
|
|
1896
2003
|
if (pollIntervalRef.current)
|
|
1897
2004
|
clearInterval(pollIntervalRef.current);
|
|
1898
|
-
dispatch({ type: '
|
|
2005
|
+
dispatch({ type: 'SET_CONTRIBUTIONS', contributions: [], total: 0 });
|
|
1899
2006
|
dispatch({ type: 'SET_MEMBERS', members: [] });
|
|
1900
2007
|
}
|
|
1901
2008
|
showToast('Left', 'success');
|
|
@@ -1993,7 +2100,7 @@ export default function App() {
|
|
|
1993
2100
|
}
|
|
1994
2101
|
if (pollIntervalRef.current)
|
|
1995
2102
|
clearInterval(pollIntervalRef.current);
|
|
1996
|
-
dispatch({ type: '
|
|
2103
|
+
dispatch({ type: 'SET_CONTRIBUTIONS', contributions: [], total: 0 });
|
|
1997
2104
|
dispatch({ type: 'SET_MEMBERS', members: [] });
|
|
1998
2105
|
}
|
|
1999
2106
|
showToast('Team deleted', 'success');
|
|
@@ -2056,7 +2163,7 @@ function HelpOverlay({ state }) {
|
|
|
2056
2163
|
];
|
|
2057
2164
|
switch (state.tab) {
|
|
2058
2165
|
case 'knowledge':
|
|
2059
|
-
lines.push(['/', 'search'], ['enter', 'expand contribution'], ['↑↓', 'navigate'], ['space', 'load more'], ['d', '
|
|
2166
|
+
lines.push(['/', 'search'], ['enter', 'expand contribution'], ['↑↓', 'navigate'], ['space', 'load more'], ['d', 'remove mode'], ['D', 'remove all (admin)']);
|
|
2060
2167
|
break;
|
|
2061
2168
|
case 'team':
|
|
2062
2169
|
lines.push(['↑↓', 'navigate'], ['i', 'invite'], ['e', 'edit role (admin)'], ['x', 'remove member'], ['c', 'copy invite link'], ['r', 'regenerate link']);
|