perf(renderer): mount team dialogs on demand
This commit is contained in:
parent
b6139d8558
commit
12c578a44d
1 changed files with 151 additions and 135 deletions
|
|
@ -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}>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue