PrintControls.tsx 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. import { useState } from 'react';
  2. import { useMutation, useQueryClient } from '@tanstack/react-query';
  3. import { api, isConfirmationRequired } from '../../api/client';
  4. import type { PrinterStatus } from '../../api/client';
  5. import { Pause, Play, Square, Loader2 } from 'lucide-react';
  6. import { ConfirmModal } from '../ConfirmModal';
  7. interface PrintControlsProps {
  8. printerId: number;
  9. status: PrinterStatus | null | undefined;
  10. }
  11. export function PrintControls({ printerId, status }: PrintControlsProps) {
  12. const queryClient = useQueryClient();
  13. const [confirmModal, setConfirmModal] = useState<{
  14. action: string;
  15. token: string;
  16. warning: string;
  17. } | null>(null);
  18. const isConnected = status?.connected ?? false;
  19. const isPrinting = status?.state === 'RUNNING';
  20. const isPaused = status?.state === 'PAUSE';
  21. const pauseMutation = useMutation({
  22. mutationFn: () => api.pausePrint(printerId),
  23. onSuccess: () => {
  24. queryClient.invalidateQueries({ queryKey: ['printerStatuses'] });
  25. },
  26. });
  27. const resumeMutation = useMutation({
  28. mutationFn: () => api.resumePrint(printerId),
  29. onSuccess: () => {
  30. queryClient.invalidateQueries({ queryKey: ['printerStatuses'] });
  31. },
  32. });
  33. const stopMutation = useMutation({
  34. mutationFn: (token?: string) => api.stopPrint(printerId, token),
  35. onSuccess: (result) => {
  36. if (isConfirmationRequired(result)) {
  37. setConfirmModal({
  38. action: 'stop',
  39. token: result.token,
  40. warning: result.warning,
  41. });
  42. } else {
  43. queryClient.invalidateQueries({ queryKey: ['printerStatuses'] });
  44. }
  45. },
  46. });
  47. const handleStop = () => {
  48. stopMutation.mutate(undefined);
  49. };
  50. const handleConfirmStop = () => {
  51. if (confirmModal) {
  52. stopMutation.mutate(confirmModal.token);
  53. setConfirmModal(null);
  54. }
  55. };
  56. const isLoading = pauseMutation.isPending || resumeMutation.isPending || stopMutation.isPending;
  57. return (
  58. <>
  59. <div>
  60. <h3 className="text-sm font-medium text-bambu-gray mb-3">Print Controls</h3>
  61. <div className="flex gap-2">
  62. {/* Pause Button */}
  63. <button
  64. onClick={() => pauseMutation.mutate()}
  65. disabled={!isConnected || !isPrinting || isLoading}
  66. className="flex-1 flex items-center justify-center gap-2 px-4 py-3 rounded-lg bg-yellow-500/20 text-yellow-500 hover:bg-yellow-500/30 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
  67. >
  68. {pauseMutation.isPending ? (
  69. <Loader2 className="w-5 h-5 animate-spin" />
  70. ) : (
  71. <Pause className="w-5 h-5" />
  72. )}
  73. <span className="font-medium">Pause</span>
  74. </button>
  75. {/* Resume Button */}
  76. <button
  77. onClick={() => resumeMutation.mutate()}
  78. disabled={!isConnected || !isPaused || isLoading}
  79. className="flex-1 flex items-center justify-center gap-2 px-4 py-3 rounded-lg bg-bambu-green/20 text-bambu-green hover:bg-bambu-green/30 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
  80. >
  81. {resumeMutation.isPending ? (
  82. <Loader2 className="w-5 h-5 animate-spin" />
  83. ) : (
  84. <Play className="w-5 h-5" />
  85. )}
  86. <span className="font-medium">Resume</span>
  87. </button>
  88. {/* Stop Button */}
  89. <button
  90. onClick={handleStop}
  91. disabled={!isConnected || (!isPrinting && !isPaused) || isLoading}
  92. className="flex-1 flex items-center justify-center gap-2 px-4 py-3 rounded-lg bg-red-500/20 text-red-500 hover:bg-red-500/30 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
  93. >
  94. {stopMutation.isPending ? (
  95. <Loader2 className="w-5 h-5 animate-spin" />
  96. ) : (
  97. <Square className="w-5 h-5" />
  98. )}
  99. <span className="font-medium">Stop</span>
  100. </button>
  101. </div>
  102. {/* Error Message */}
  103. {(pauseMutation.error || resumeMutation.error || stopMutation.error) && (
  104. <p className="mt-2 text-sm text-red-400">
  105. {(pauseMutation.error || resumeMutation.error || stopMutation.error)?.message}
  106. </p>
  107. )}
  108. </div>
  109. {/* Confirmation Modal */}
  110. {confirmModal && (
  111. <ConfirmModal
  112. title="Confirm Stop"
  113. message={confirmModal.warning}
  114. confirmText="Stop Print"
  115. variant="danger"
  116. onConfirm={handleConfirmStop}
  117. onCancel={() => setConfirmModal(null)}
  118. />
  119. )}
  120. </>
  121. );
  122. }