CompactHistoryRow.tsx 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. import {
  2. CheckCircle,
  3. XCircle,
  4. SkipForward,
  5. X,
  6. RefreshCw,
  7. Trash2,
  8. Printer,
  9. Timer,
  10. Layers,
  11. } from 'lucide-react';
  12. import { api } from '../api/client';
  13. import { type TimeFormat, formatDuration, formatRelativeTime } from '../utils/date';
  14. import type { PrintQueueItem, Permission } from '../api/client';
  15. import { Button } from './Button';
  16. const STATUS_CONFIG = {
  17. completed: { icon: CheckCircle, color: 'text-emerald-400', border: 'border-l-emerald-500' },
  18. failed: { icon: XCircle, color: 'text-red-400', border: 'border-l-red-500' },
  19. skipped: { icon: SkipForward, color: 'text-orange-400', border: 'border-l-gray-500' },
  20. cancelled: { icon: X, color: 'text-gray-400', border: 'border-l-gray-500' },
  21. } as const;
  22. export function CompactHistoryRow({
  23. item,
  24. onRequeue,
  25. onRemove,
  26. timeFormat = 'system',
  27. hasPermission,
  28. canModify,
  29. t,
  30. }: {
  31. item: PrintQueueItem;
  32. onRequeue: () => void;
  33. onRemove: () => void;
  34. timeFormat?: TimeFormat;
  35. hasPermission: (permission: Permission) => boolean;
  36. canModify: (resource: 'queue' | 'archives' | 'library', action: 'update' | 'delete' | 'reprint', createdById: number | null | undefined) => boolean;
  37. t: (key: string, options?: Record<string, unknown>) => string;
  38. }) {
  39. const config = STATUS_CONFIG[item.status as keyof typeof STATUS_CONFIG] || STATUS_CONFIG.cancelled;
  40. const StatusIcon = config.icon;
  41. const displayName = item.archive_name || item.library_file_name || `File #${item.archive_id || item.library_file_id}`;
  42. const thumbnailUrl = item.archive_thumbnail
  43. ? api.getArchiveThumbnail(item.archive_id!)
  44. : item.library_file_thumbnail
  45. ? api.getLibraryFileThumbnailUrl(item.library_file_id!)
  46. : null;
  47. const completedTime = item.completed_at || item.created_at;
  48. return (
  49. <div className={`flex items-center gap-2 sm:gap-3 px-3 py-2 bg-bambu-dark-secondary rounded-lg border border-bambu-dark-tertiary border-l-[3px] ${config.border}`}>
  50. {/* Status icon */}
  51. <StatusIcon className={`w-4 h-4 shrink-0 ${config.color}`} />
  52. {/* Thumbnail */}
  53. <div className="w-8 h-8 shrink-0 bg-bambu-dark rounded overflow-hidden">
  54. {thumbnailUrl ? (
  55. <img src={thumbnailUrl} alt="" className="w-full h-full object-cover" />
  56. ) : (
  57. <div className="w-full h-full flex items-center justify-center text-bambu-gray">
  58. <Layers className="w-4 h-4" />
  59. </div>
  60. )}
  61. </div>
  62. {/* File name */}
  63. <span className="text-sm text-white font-medium truncate min-w-0 flex-1">
  64. {displayName}
  65. </span>
  66. {/* Printer */}
  67. {item.printer_name && (
  68. <span className="hidden sm:flex items-center gap-1 text-xs text-bambu-gray shrink-0">
  69. <Printer className="w-3 h-3" />
  70. <span className="truncate max-w-[100px]">{item.printer_name}</span>
  71. </span>
  72. )}
  73. {/* Duration */}
  74. {item.print_time_seconds && (
  75. <span className="hidden sm:flex items-center gap-1 text-xs text-bambu-gray shrink-0">
  76. <Timer className="w-3 h-3" />
  77. {formatDuration(item.print_time_seconds)}
  78. </span>
  79. )}
  80. {/* Completed time */}
  81. <span className="text-xs text-bambu-gray shrink-0">
  82. {formatRelativeTime(completedTime, timeFormat, t)}
  83. </span>
  84. {/* Actions */}
  85. <div className="flex items-center gap-0.5 shrink-0">
  86. <Button
  87. variant="ghost"
  88. size="sm"
  89. onClick={onRequeue}
  90. disabled={!hasPermission('queue:create')}
  91. title={!hasPermission('queue:create') ? t('queue.permissions.noRequeue') : t('queue.actions.requeue')}
  92. className="text-bambu-green hover:text-bambu-green/80 hover:bg-bambu-green/10 p-1.5"
  93. >
  94. <RefreshCw className="w-3.5 h-3.5" />
  95. </Button>
  96. <Button
  97. variant="ghost"
  98. size="sm"
  99. onClick={onRemove}
  100. disabled={!canModify('queue', 'delete', item.created_by_id)}
  101. title={!canModify('queue', 'delete', item.created_by_id) ? t('queue.permissions.noRemove') : t('common.remove')}
  102. className="p-1.5"
  103. >
  104. <Trash2 className="w-3.5 h-3.5" />
  105. </Button>
  106. </div>
  107. </div>
  108. );
  109. }