perf(renderer): mount team dialogs on demand

This commit is contained in:
777genius 2026-05-31 02:42:48 +03:00
parent b6139d8558
commit 12c578a44d

View file

@ -3403,30 +3403,32 @@ export const TeamDetailView = memo(function TeamDetailView({
<TeamMessagesPanelBridge position="inline" {...sharedMessagesPanelProps} /> <TeamMessagesPanelBridge position="inline" {...sharedMessagesPanelProps} />
)} )}
<ReviewDialog {requestChangesTaskId !== null && (
open={requestChangesTaskId !== null} <ReviewDialog
teamName={teamName} open={true}
taskId={requestChangesTaskId} teamName={teamName}
members={members} taskId={requestChangesTaskId}
onCancel={() => setRequestChangesTaskId(null)} members={members}
onSubmit={(comment, taskRefs) => { onCancel={() => setRequestChangesTaskId(null)}
if (!requestChangesTaskId) { onSubmit={(comment, taskRefs) => {
return; if (!requestChangesTaskId) {
} return;
void (async () => {
try {
await updateKanban(teamName, requestChangesTaskId, {
op: 'request_changes',
comment,
taskRefs,
});
setRequestChangesTaskId(null);
} catch {
// error state is handled in the store and shown in the view
} }
})(); void (async () => {
}} try {
/> await updateKanban(teamName, requestChangesTaskId, {
op: 'request_changes',
comment,
taskRefs,
});
setRequestChangesTaskId(null);
} catch {
// error state is handled in the store and shown in the view
}
})();
}}
/>
)}
<TeamMemberDetailDialogBridge <TeamMemberDetailDialogBridge
open={selectedMember !== null} open={selectedMember !== null}
@ -3511,108 +3513,120 @@ export const TeamDetailView = memo(function TeamDetailView({
</Suspense> </Suspense>
)} )}
<EditTeamDialog {editDialogOpen && (
open={editDialogOpen} <EditTeamDialog
teamName={teamName} open={editDialogOpen}
currentName={data.config.name} teamName={teamName}
currentDescription={data.config.description ?? ''} currentName={data.config.name}
currentColor={data.config.color ?? ''} currentDescription={data.config.description ?? ''}
currentMembers={membersWithLiveBranches.filter((m) => !isLeadMember(m))} currentColor={data.config.color ?? ''}
leadMember={membersWithLiveBranches.find((m) => isLeadMember(m)) ?? null} currentMembers={membersWithLiveBranches.filter((m) => !isLeadMember(m))}
resolvedMemberColorMap={resolvedMemberColorMap} leadMember={membersWithLiveBranches.find((m) => isLeadMember(m)) ?? null}
isTeamAlive={data.isAlive && !isTeamProvisioning} resolvedMemberColorMap={resolvedMemberColorMap}
isTeamProvisioning={isTeamProvisioning} isTeamAlive={data.isAlive && !isTeamProvisioning}
projectPath={data.config.projectPath} isTeamProvisioning={isTeamProvisioning}
onClose={() => setEditDialogOpen(false)} projectPath={data.config.projectPath}
onChangeLeadRuntime={handleChangeLeadRuntime} onClose={() => setEditDialogOpen(false)}
onSaved={() => void selectTeam(teamName)} onChangeLeadRuntime={handleChangeLeadRuntime}
/> onSaved={() => void selectTeam(teamName)}
/>
)}
<AddMemberDialog {addMemberDialogOpen && (
open={addMemberDialogOpen} <AddMemberDialog
teamName={teamName} open={addMemberDialogOpen}
existingNames={membersWithLiveBranches.map((m) => m.name)} teamName={teamName}
existingMembers={membersWithLiveBranches} existingNames={membersWithLiveBranches.map((m) => m.name)}
projectPath={data.config.projectPath} existingMembers={membersWithLiveBranches}
adding={addingMemberLoading} projectPath={data.config.projectPath}
onClose={() => setAddMemberDialogOpen(false)} adding={addingMemberLoading}
onAdd={(entries: AddMemberEntry[]) => { onClose={() => setAddMemberDialogOpen(false)}
setAddingMemberLoading(true); onAdd={(entries: AddMemberEntry[]) => {
void (async () => { setAddingMemberLoading(true);
try { void (async () => {
for (const entry of entries) { try {
await addMember(teamName, { for (const entry of entries) {
name: entry.name, await addMember(teamName, {
role: entry.role, name: entry.name,
workflow: entry.workflow, role: entry.role,
isolation: entry.isolation, workflow: entry.workflow,
providerId: entry.providerId, isolation: entry.isolation,
model: entry.model, providerId: entry.providerId,
effort: entry.effort, model: entry.model,
mcpPolicy: entry.mcpPolicy, effort: entry.effort,
}); mcpPolicy: entry.mcpPolicy,
});
}
setAddMemberDialogOpen(false);
} catch {
// error shown via store
} finally {
setAddingMemberLoading(false);
} }
setAddMemberDialogOpen(false); })();
} catch { }}
// error shown via store />
} finally { )}
setAddingMemberLoading(false);
}
})();
}}
/>
<Dialog {removeMemberConfirm !== null && (
open={removeMemberConfirm !== null} <Dialog
onOpenChange={(open) => { open={true}
if (!open) setRemoveMemberConfirm(null); onOpenChange={(open) => {
}} if (!open) setRemoveMemberConfirm(null);
> }}
<DialogContent className="max-w-sm"> >
<DialogHeader> <DialogContent className="max-w-sm">
<DialogTitle>{t('detail.removeMember.title')}</DialogTitle> <DialogHeader>
<DialogDescription> <DialogTitle>{t('detail.removeMember.title')}</DialogTitle>
{t('detail.removeMember.description', { member: removeMemberConfirm })} <DialogDescription>
</DialogDescription> {t('detail.removeMember.description', { member: removeMemberConfirm })}
</DialogHeader> </DialogDescription>
<DialogFooter> </DialogHeader>
<Button variant="ghost" size="sm" onClick={() => setRemoveMemberConfirm(null)}> <DialogFooter>
{t('detail.actions.cancel')} <Button
</Button> variant="ghost"
<Button size="sm"
variant="destructive" onClick={() => setRemoveMemberConfirm(null)}
size="sm" >
onClick={() => { {t('detail.actions.cancel')}
const name = removeMemberConfirm; </Button>
setRemoveMemberConfirm(null); <Button
closeSelectedMemberDialog(); variant="destructive"
if (name) void removeMember(teamName, name); size="sm"
}} onClick={() => {
> const name = removeMemberConfirm;
{t('detail.actions.remove')} setRemoveMemberConfirm(null);
</Button> closeSelectedMemberDialog();
</DialogFooter> if (name) void removeMember(teamName, name);
</DialogContent> }}
</Dialog> >
{t('detail.actions.remove')}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
)}
<Dialog open={deleteConfirmOpen} onOpenChange={setDeleteConfirmOpen}> {deleteConfirmOpen && (
<DialogContent className="max-w-sm"> <Dialog open={deleteConfirmOpen} onOpenChange={setDeleteConfirmOpen}>
<DialogHeader> <DialogContent className="max-w-sm">
<DialogTitle>{t('detail.deleteTeam.title')}</DialogTitle> <DialogHeader>
<DialogDescription> <DialogTitle>{t('detail.deleteTeam.title')}</DialogTitle>
{t('detail.deleteTeam.description', { team: data.config.name })} <DialogDescription>
</DialogDescription> {t('detail.deleteTeam.description', { team: data.config.name })}
</DialogHeader> </DialogDescription>
<DialogFooter> </DialogHeader>
<Button variant="ghost" size="sm" onClick={() => setDeleteConfirmOpen(false)}> <DialogFooter>
{t('detail.actions.cancel')} <Button variant="ghost" size="sm" onClick={() => setDeleteConfirmOpen(false)}>
</Button> {t('detail.actions.cancel')}
<Button variant="destructive" size="sm" onClick={confirmDeleteTeam}> </Button>
{t('detail.actions.delete')} <Button variant="destructive" size="sm" onClick={confirmDeleteTeam}>
</Button> {t('detail.actions.delete')}
</DialogFooter> </Button>
</DialogContent> </DialogFooter>
</Dialog> </DialogContent>
</Dialog>
)}
{launchDialogOpen && ( {launchDialogOpen && (
<Suspense <Suspense
@ -3711,20 +3725,22 @@ export const TeamDetailView = memo(function TeamDetailView({
onDeleteTask={handleDeleteTask} onDeleteTask={handleDeleteTask}
/> />
<TrashDialog {trashOpen && (
open={trashOpen} <TrashDialog
tasks={deletedTasks} open={trashOpen}
onClose={() => setTrashOpen(false)} tasks={deletedTasks}
onRestore={(taskId) => { onClose={() => setTrashOpen(false)}
void (async () => { onRestore={(taskId) => {
try { void (async () => {
await restoreTask(teamName, taskId); try {
} catch { await restoreTask(teamName, taskId);
// error via store } catch {
} // error via store
})(); }
}} })();
/> }}
/>
)}
{reviewDialogState.open && ( {reviewDialogState.open && (
<Suspense fallback={null}> <Suspense fallback={null}>