fix(team): show launch dialog loading fallback
This commit is contained in:
parent
e88d3a1e98
commit
bafd4d7194
5 changed files with 138 additions and 5 deletions
|
|
@ -25,6 +25,7 @@ import {
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { useShallow } from 'zustand/react/shallow';
|
import { useShallow } from 'zustand/react/shallow';
|
||||||
|
|
||||||
|
import { LaunchTeamDialogLoadingFallback } from '../team/dialogs/LaunchTeamDialogLoadingFallback';
|
||||||
import { ScheduleRunLogDialog } from '../team/schedule/ScheduleRunLogDialog';
|
import { ScheduleRunLogDialog } from '../team/schedule/ScheduleRunLogDialog';
|
||||||
import { ScheduleRunRow } from '../team/schedule/ScheduleRunRow';
|
import { ScheduleRunRow } from '../team/schedule/ScheduleRunRow';
|
||||||
import { ScheduleStatusBadge } from '../team/schedule/ScheduleStatusBadge';
|
import { ScheduleStatusBadge } from '../team/schedule/ScheduleStatusBadge';
|
||||||
|
|
@ -588,7 +589,16 @@ export const SchedulesView = (): React.JSX.Element => {
|
||||||
|
|
||||||
{/* Create/Edit Dialog */}
|
{/* Create/Edit Dialog */}
|
||||||
{dialogOpen && (
|
{dialogOpen && (
|
||||||
<Suspense fallback={null}>
|
<Suspense
|
||||||
|
fallback={
|
||||||
|
<LaunchTeamDialogLoadingFallback
|
||||||
|
mode="schedule"
|
||||||
|
teamName={editingSchedule?.teamName}
|
||||||
|
isEditingSchedule={Boolean(editingSchedule)}
|
||||||
|
onClose={handleClose}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
>
|
||||||
<LaunchTeamDialog
|
<LaunchTeamDialog
|
||||||
mode="schedule"
|
mode="schedule"
|
||||||
open={dialogOpen}
|
open={dialogOpen}
|
||||||
|
|
|
||||||
|
|
@ -85,6 +85,7 @@ import { useShallow } from 'zustand/react/shallow';
|
||||||
|
|
||||||
import { AddMemberDialog } from './dialogs/AddMemberDialog';
|
import { AddMemberDialog } from './dialogs/AddMemberDialog';
|
||||||
import { EditTeamDialog } from './dialogs/EditTeamDialog';
|
import { EditTeamDialog } from './dialogs/EditTeamDialog';
|
||||||
|
import { LaunchTeamDialogLoadingFallback } from './dialogs/LaunchTeamDialogLoadingFallback';
|
||||||
import { ReviewDialog } from './dialogs/ReviewDialog';
|
import { ReviewDialog } from './dialogs/ReviewDialog';
|
||||||
import { executeTeamRelaunch } from './dialogs/teamRelaunchFlow';
|
import { executeTeamRelaunch } from './dialogs/teamRelaunchFlow';
|
||||||
import { KanbanBoard } from './kanban/KanbanBoard';
|
import { KanbanBoard } from './kanban/KanbanBoard';
|
||||||
|
|
@ -2580,7 +2581,15 @@ export const TeamDetailView = memo(function TeamDetailView({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{launchDialogOpen && (
|
{launchDialogOpen && (
|
||||||
<Suspense fallback={null}>
|
<Suspense
|
||||||
|
fallback={
|
||||||
|
<LaunchTeamDialogLoadingFallback
|
||||||
|
mode={launchDialogState.mode}
|
||||||
|
teamName={teamName}
|
||||||
|
onClose={closeLaunchDialog}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
>
|
||||||
<LaunchTeamDialog
|
<LaunchTeamDialog
|
||||||
mode={launchDialogState.mode}
|
mode={launchDialogState.mode}
|
||||||
open={launchDialogOpen}
|
open={launchDialogOpen}
|
||||||
|
|
@ -3418,7 +3427,15 @@ export const TeamDetailView = memo(function TeamDetailView({
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
{launchDialogOpen && (
|
{launchDialogOpen && (
|
||||||
<Suspense fallback={null}>
|
<Suspense
|
||||||
|
fallback={
|
||||||
|
<LaunchTeamDialogLoadingFallback
|
||||||
|
mode={launchDialogState.mode}
|
||||||
|
teamName={teamName}
|
||||||
|
onClose={closeLaunchDialog}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
>
|
||||||
<LaunchTeamDialog
|
<LaunchTeamDialog
|
||||||
mode={launchDialogState.mode}
|
mode={launchDialogState.mode}
|
||||||
open={launchDialogOpen}
|
open={launchDialogOpen}
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,7 @@ import {
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { useShallow } from 'zustand/react/shallow';
|
import { useShallow } from 'zustand/react/shallow';
|
||||||
|
|
||||||
|
import { LaunchTeamDialogLoadingFallback } from './dialogs/LaunchTeamDialogLoadingFallback';
|
||||||
import { executeTeamRelaunch } from './dialogs/teamRelaunchFlow';
|
import { executeTeamRelaunch } from './dialogs/teamRelaunchFlow';
|
||||||
import { TeamEmptyState } from './TeamEmptyState';
|
import { TeamEmptyState } from './TeamEmptyState';
|
||||||
import { EMPTY_TEAM_FILTER, TeamListFilterPopover } from './TeamListFilterPopover';
|
import { EMPTY_TEAM_FILTER, TeamListFilterPopover } from './TeamListFilterPopover';
|
||||||
|
|
@ -1094,7 +1095,15 @@ export const TeamListView = memo(function TeamListView(): React.JSX.Element {
|
||||||
);
|
);
|
||||||
|
|
||||||
const launchDialogElement = launchDialogOpen && (
|
const launchDialogElement = launchDialogOpen && (
|
||||||
<Suspense fallback={null}>
|
<Suspense
|
||||||
|
fallback={
|
||||||
|
<LaunchTeamDialogLoadingFallback
|
||||||
|
mode={launchDialogMode}
|
||||||
|
teamName={launchDialogTeamName}
|
||||||
|
onClose={() => setLaunchDialogOpen(false)}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
>
|
||||||
{launchDialogMode === 'relaunch' ? (
|
{launchDialogMode === 'relaunch' ? (
|
||||||
<LaunchTeamDialog
|
<LaunchTeamDialog
|
||||||
mode="relaunch"
|
mode="relaunch"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,86 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { useAppTranslation } from '@features/localization/renderer';
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogDescription,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
} from '@renderer/components/ui/dialog';
|
||||||
|
import { Loader2 } from 'lucide-react';
|
||||||
|
|
||||||
|
type LaunchTeamDialogLoadingMode = 'launch' | 'relaunch' | 'schedule';
|
||||||
|
|
||||||
|
interface LaunchTeamDialogLoadingFallbackProps {
|
||||||
|
readonly mode: LaunchTeamDialogLoadingMode;
|
||||||
|
readonly teamName?: string;
|
||||||
|
readonly isEditingSchedule?: boolean;
|
||||||
|
readonly onClose: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const LaunchTeamDialogLoadingFallback = ({
|
||||||
|
mode,
|
||||||
|
teamName,
|
||||||
|
isEditingSchedule = false,
|
||||||
|
onClose,
|
||||||
|
}: LaunchTeamDialogLoadingFallbackProps): React.JSX.Element => {
|
||||||
|
const { t } = useAppTranslation('team');
|
||||||
|
const { t: tCommon } = useAppTranslation('common');
|
||||||
|
|
||||||
|
const title =
|
||||||
|
mode === 'schedule'
|
||||||
|
? isEditingSchedule
|
||||||
|
? t('launch.title.editSchedule')
|
||||||
|
: t('launch.title.createSchedule')
|
||||||
|
: mode === 'relaunch'
|
||||||
|
? t('launch.title.relaunch')
|
||||||
|
: t('launch.title.launch');
|
||||||
|
|
||||||
|
const description =
|
||||||
|
mode === 'schedule'
|
||||||
|
? isEditingSchedule && teamName
|
||||||
|
? t('launch.description.editSchedule', { team: teamName })
|
||||||
|
: teamName
|
||||||
|
? t('launch.description.createScheduleForTeam', { team: teamName })
|
||||||
|
: t('launch.description.createSchedule')
|
||||||
|
: mode === 'relaunch'
|
||||||
|
? t('launch.description.relaunchPrefix')
|
||||||
|
: t('launch.description.launchPrefix');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog
|
||||||
|
open
|
||||||
|
onOpenChange={(nextOpen) => {
|
||||||
|
if (!nextOpen) {
|
||||||
|
onClose();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DialogContent className="max-w-[52rem]">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle className="text-sm">{title}</DialogTitle>
|
||||||
|
<DialogDescription className="text-xs">
|
||||||
|
{mode === 'schedule' ? (
|
||||||
|
description
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
{description} <span className="font-mono font-medium">{teamName}</span>{' '}
|
||||||
|
{mode === 'relaunch'
|
||||||
|
? t('launch.description.relaunchSuffix')
|
||||||
|
: t('launch.description.launchSuffix')}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
<div
|
||||||
|
className="flex items-center gap-2 rounded-md border border-[var(--color-border)] bg-[var(--color-surface-overlay)] px-3 py-2 text-xs text-[var(--color-text-muted)]"
|
||||||
|
aria-live="polite"
|
||||||
|
>
|
||||||
|
<Loader2 className="size-3.5 shrink-0 animate-spin" />
|
||||||
|
<span>{tCommon('states.loading')}</span>
|
||||||
|
</div>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -19,6 +19,8 @@ import {
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { useShallow } from 'zustand/react/shallow';
|
import { useShallow } from 'zustand/react/shallow';
|
||||||
|
|
||||||
|
import { LaunchTeamDialogLoadingFallback } from '../dialogs/LaunchTeamDialogLoadingFallback';
|
||||||
|
|
||||||
import { ScheduleEmptyState } from './ScheduleEmptyState';
|
import { ScheduleEmptyState } from './ScheduleEmptyState';
|
||||||
import { ScheduleRunLogDialog } from './ScheduleRunLogDialog';
|
import { ScheduleRunLogDialog } from './ScheduleRunLogDialog';
|
||||||
import { ScheduleRunRow } from './ScheduleRunRow';
|
import { ScheduleRunRow } from './ScheduleRunRow';
|
||||||
|
|
@ -311,7 +313,16 @@ export const ScheduleSection = ({ teamName }: ScheduleSectionProps): React.JSX.E
|
||||||
|
|
||||||
{/* Create/Edit Dialog */}
|
{/* Create/Edit Dialog */}
|
||||||
{dialogOpen && (
|
{dialogOpen && (
|
||||||
<Suspense fallback={null}>
|
<Suspense
|
||||||
|
fallback={
|
||||||
|
<LaunchTeamDialogLoadingFallback
|
||||||
|
mode="schedule"
|
||||||
|
teamName={teamName}
|
||||||
|
isEditingSchedule={Boolean(editingSchedule)}
|
||||||
|
onClose={handleClose}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
>
|
||||||
<LaunchTeamDialog
|
<LaunchTeamDialog
|
||||||
mode="schedule"
|
mode="schedule"
|
||||||
open={dialogOpen}
|
open={dialogOpen}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue