feat(qr): offline show_list диалог для QR-автоматизации КИПиА
- clientAutomationEngine: onShowTaskDialog callback для show_list - QrHandler: Dialog со списком найденных задач, навигация на /forms/11/tasks/:id
This commit is contained in:
@@ -208,6 +208,7 @@ export async function handleAutomationActions(
|
|||||||
deps: {
|
deps: {
|
||||||
toast: (opts: { title?: string; description?: string; variant?: 'default' | 'destructive' }) => void;
|
toast: (opts: { title?: string; description?: string; variant?: 'default' | 'destructive' }) => void;
|
||||||
navigate?: (path: string) => void;
|
navigate?: (path: string) => void;
|
||||||
|
onShowTaskDialog?: (tasks: Array<{ id: number; title: string }>) => void;
|
||||||
},
|
},
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
const holder = extractActions(result);
|
const holder = extractActions(result);
|
||||||
@@ -244,7 +245,11 @@ export async function handleAutomationActions(
|
|||||||
|
|
||||||
if (holder.legacy) {
|
if (holder.legacy) {
|
||||||
handled = true;
|
handled = true;
|
||||||
await handleLegacyAction(holder.legacy, deps);
|
await handleLegacyAction(holder.legacy, {
|
||||||
|
toast: deps.toast,
|
||||||
|
navigate: deps.navigate,
|
||||||
|
onShowTaskDialog: deps.onShowTaskDialog,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return handled;
|
return handled;
|
||||||
@@ -291,6 +296,7 @@ async function handleLegacyAction(
|
|||||||
deps: {
|
deps: {
|
||||||
toast: (opts: { title?: string; description?: string; variant?: 'default' | 'destructive' }) => void;
|
toast: (opts: { title?: string; description?: string; variant?: 'default' | 'destructive' }) => void;
|
||||||
navigate?: (path: string) => void;
|
navigate?: (path: string) => void;
|
||||||
|
onShowTaskDialog?: (tasks: Array<{ id: number; title: string }>) => void;
|
||||||
},
|
},
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const nav = deps.navigate ?? navigate;
|
const nav = deps.navigate ?? navigate;
|
||||||
@@ -317,9 +323,13 @@ async function handleLegacyAction(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'show_list': {
|
case 'show_list': {
|
||||||
// Legacy show_list has no direct screen — notify the user instead.
|
if (deps.onShowTaskDialog && action.tasks.length > 0) {
|
||||||
const titles = action.tasks.map((t) => t.title).join(', ');
|
deps.onShowTaskDialog(action.tasks);
|
||||||
deps.toast({ title: 'Найдены задачи', description: titles || 'Список пуст' });
|
} else {
|
||||||
|
// Fallback when no dialog handler is provided.
|
||||||
|
const titles = action.tasks.map((t) => t.title).join(', ');
|
||||||
|
deps.toast({ title: 'Найдены задачи', description: titles || 'Список пуст' });
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,13 @@ import { runOfflineAutomations, handleAutomationActions, type ClientAutomationRe
|
|||||||
import { checkServerReachable } from '@/lib/syncEngine';
|
import { checkServerReachable } from '@/lib/syncEngine';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
DialogDescription,
|
||||||
|
} from '@/components/ui/dialog';
|
||||||
import { Loader2, Terminal, ArrowLeft } from 'lucide-react';
|
import { Loader2, Terminal, ArrowLeft } from 'lucide-react';
|
||||||
import { apiRequest } from '@/lib/queryClient';
|
import { apiRequest } from '@/lib/queryClient';
|
||||||
|
|
||||||
@@ -17,6 +24,10 @@ export default function QrHandler() {
|
|||||||
|
|
||||||
const [results, setResults] = useState<ClientAutomationResult[]>([]);
|
const [results, setResults] = useState<ClientAutomationResult[]>([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
|
const [taskDialog, setTaskDialog] = useState<{ open: boolean; tasks: Array<{ id: number; title: string }> }>({
|
||||||
|
open: false,
|
||||||
|
tasks: [],
|
||||||
|
});
|
||||||
|
|
||||||
const templateKey = params.templateKey ?? '';
|
const templateKey = params.templateKey ?? '';
|
||||||
const objectId = params.objectId ? parseInt(params.objectId) : undefined;
|
const objectId = params.objectId ? parseInt(params.objectId) : undefined;
|
||||||
@@ -57,7 +68,11 @@ export default function QrHandler() {
|
|||||||
if (res.error) {
|
if (res.error) {
|
||||||
toast({ title: 'Ошибка автоматизации', description: res.error, variant: 'destructive' });
|
toast({ title: 'Ошибка автоматизации', description: res.error, variant: 'destructive' });
|
||||||
} else if (res.result) {
|
} else if (res.result) {
|
||||||
await handleAutomationActions(res.result, { toast, navigate });
|
await handleAutomationActions(res.result, {
|
||||||
|
toast,
|
||||||
|
navigate,
|
||||||
|
onShowTaskDialog: (tasks) => setTaskDialog({ open: true, tasks }),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -82,7 +97,11 @@ export default function QrHandler() {
|
|||||||
);
|
);
|
||||||
for (const res of data.results) {
|
for (const res of data.results) {
|
||||||
if (res.result) {
|
if (res.result) {
|
||||||
await handleAutomationActions(res.result, { toast, navigate });
|
await handleAutomationActions(res.result, {
|
||||||
|
toast,
|
||||||
|
navigate,
|
||||||
|
onShowTaskDialog: (tasks) => setTaskDialog({ open: true, tasks }),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -165,6 +184,30 @@ export default function QrHandler() {
|
|||||||
))}
|
))}
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
|
<Dialog open={taskDialog.open} onOpenChange={(open) => setTaskDialog((prev) => ({ ...prev, open }))}>
|
||||||
|
<DialogContent className="max-w-sm">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle className="text-base">Найдены заявки</DialogTitle>
|
||||||
|
<DialogDescription>Выберите задачу для открытия</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
<div className="space-y-2 max-h-[60vh] overflow-auto">
|
||||||
|
{taskDialog.tasks.map((task) => (
|
||||||
|
<Button
|
||||||
|
key={task.id}
|
||||||
|
variant="outline"
|
||||||
|
className="w-full justify-start h-auto py-2 px-3 text-left"
|
||||||
|
onClick={() => {
|
||||||
|
setTaskDialog({ open: false, tasks: [] });
|
||||||
|
navigate(`/forms/11/tasks/${task.id}`);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span className="text-sm">#{task.id} {task.title}</span>
|
||||||
|
</Button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user