mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-10-03 12:46:49 +02:00
Updated website
This commit is contained in:
@@ -223,6 +223,7 @@ export interface Archive {
|
||||
cost: number | null;
|
||||
photos: string[] | null;
|
||||
failure_reason: string | null;
|
||||
quantity: number;
|
||||
energy_kwh: number | null;
|
||||
energy_cost: number | null;
|
||||
created_at: string;
|
||||
@@ -333,6 +334,7 @@ export interface SimilarArchive {
|
||||
// Project types
|
||||
export interface ProjectStats {
|
||||
total_archives: number;
|
||||
total_items: number; // Sum of quantities (total items printed)
|
||||
completed_prints: number;
|
||||
failed_prints: number;
|
||||
queued_prints: number;
|
||||
@@ -403,7 +405,8 @@ export interface ProjectListItem {
|
||||
status: string;
|
||||
target_count: number | null;
|
||||
created_at: string;
|
||||
archive_count: number;
|
||||
archive_count: number; // Number of print jobs
|
||||
total_items: number; // Sum of quantities (total items printed)
|
||||
queue_count: number;
|
||||
progress_percent: number | null;
|
||||
archives: ArchivePreview[];
|
||||
@@ -1407,6 +1410,7 @@ export const api = {
|
||||
cost?: number;
|
||||
failure_reason?: string | null;
|
||||
status?: string;
|
||||
quantity?: number;
|
||||
}) =>
|
||||
request<Archive>(`/archives/${id}`, {
|
||||
method: 'PATCH',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useState, useEffect, useRef } from 'react';
|
||||
import { useMutation, useQueryClient, useQuery } from '@tanstack/react-query';
|
||||
import { X, Save, Tag, Camera, Trash2, Loader2, Plus, FolderKanban } from 'lucide-react';
|
||||
import { X, Save, Tag, Camera, Trash2, Loader2, Plus, FolderKanban, Hash } from 'lucide-react';
|
||||
import { api } from '../api/client';
|
||||
import type { Archive } from '../api/client';
|
||||
import { Button } from './Button';
|
||||
@@ -49,6 +49,7 @@ export function EditArchiveModal({ archive, onClose, existingTags = [] }: EditAr
|
||||
const [tags, setTags] = useState(archive.tags || '');
|
||||
const [failureReason, setFailureReason] = useState(archive.failure_reason || '');
|
||||
const [status, setStatus] = useState(archive.status);
|
||||
const [quantity, setQuantity] = useState(archive.quantity ?? 1);
|
||||
const [photos, setPhotos] = useState<string[]>(archive.photos || []);
|
||||
const [uploadingPhoto, setUploadingPhoto] = useState(false);
|
||||
const [showTagSuggestions, setShowTagSuggestions] = useState(false);
|
||||
@@ -153,6 +154,7 @@ export function EditArchiveModal({ archive, onClose, existingTags = [] }: EditAr
|
||||
project_id: projectId,
|
||||
notes: notes || undefined,
|
||||
tags: tags || undefined,
|
||||
quantity: quantity,
|
||||
};
|
||||
|
||||
// Only include status if changed
|
||||
@@ -242,6 +244,25 @@ export function EditArchiveModal({ archive, onClose, existingTags = [] }: EditAr
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{/* Quantity - number of items printed */}
|
||||
<div>
|
||||
<label className="block text-sm text-bambu-gray mb-1">
|
||||
<Hash className="w-4 h-4 inline mr-1" />
|
||||
Items Printed
|
||||
</label>
|
||||
<input
|
||||
type="number"
|
||||
min={1}
|
||||
value={quantity}
|
||||
onChange={(e) => setQuantity(Math.max(1, parseInt(e.target.value) || 1))}
|
||||
className="w-full px-3 py-2 bg-bambu-dark border border-bambu-dark-tertiary rounded-lg text-white focus:border-bambu-green focus:outline-none"
|
||||
placeholder="1"
|
||||
/>
|
||||
<p className="text-xs text-bambu-gray mt-1">
|
||||
Number of items produced in this print job
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Notes */}
|
||||
<div>
|
||||
<label className="block text-sm text-bambu-gray mb-1">Notes</label>
|
||||
|
||||
@@ -434,8 +434,8 @@ export function ProjectDetailPage() {
|
||||
|
||||
const stats = project.stats;
|
||||
const progressPercent = stats?.progress_percent ?? 0;
|
||||
const successRate = stats && stats.total_archives > 0
|
||||
? ((stats.completed_prints / stats.total_archives) * 100).toFixed(0)
|
||||
const successRate = stats && stats.total_items > 0
|
||||
? ((stats.completed_prints / stats.total_items) * 100).toFixed(0)
|
||||
: null;
|
||||
|
||||
return (
|
||||
@@ -485,7 +485,7 @@ export function ProjectDetailPage() {
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<span className="text-sm text-bambu-gray">Progress</span>
|
||||
<span className="text-sm font-medium text-white">
|
||||
{stats?.completed_prints || 0} / {project.target_count} prints
|
||||
{stats?.completed_prints || 0} / {project.target_count} items
|
||||
</span>
|
||||
</div>
|
||||
<div className="h-3 bg-bambu-dark rounded-full overflow-hidden">
|
||||
@@ -516,16 +516,16 @@ export function ProjectDetailPage() {
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
||||
<StatCard
|
||||
icon={Package}
|
||||
label="Total Prints"
|
||||
value={stats.total_archives}
|
||||
subValue={successRate ? `${successRate}% success rate` : undefined}
|
||||
label="Total Items"
|
||||
value={stats.total_items}
|
||||
subValue={`${stats.total_archives} print job${stats.total_archives !== 1 ? 's' : ''}`}
|
||||
color="text-bambu-green"
|
||||
/>
|
||||
<StatCard
|
||||
icon={CheckCircle}
|
||||
label="Completed"
|
||||
value={stats.completed_prints}
|
||||
subValue={stats.failed_prints > 0 ? `${stats.failed_prints} failed` : undefined}
|
||||
subValue={stats.failed_prints > 0 ? `${stats.failed_prints} failed` : (successRate ? `${successRate}% success` : undefined)}
|
||||
color="text-blue-400"
|
||||
/>
|
||||
<StatCard
|
||||
@@ -1143,6 +1143,7 @@ export function ProjectDetailPage() {
|
||||
project={{
|
||||
...project,
|
||||
archive_count: stats?.total_archives || 0,
|
||||
total_items: stats?.total_items || 0,
|
||||
queue_count: stats?.queued_prints || 0,
|
||||
progress_percent: stats?.progress_percent || null,
|
||||
archives: [],
|
||||
|
||||
@@ -268,11 +268,11 @@ function ProjectCard({ project, onClick, onEdit, onDelete }: ProjectCardProps) {
|
||||
? 'bg-bambu-green/20 text-bambu-green'
|
||||
: 'bg-bambu-dark text-bambu-gray'
|
||||
}`}>
|
||||
{project.archive_count}/{project.target_count} parts
|
||||
{project.total_items}/{project.target_count} items
|
||||
</span>
|
||||
) : project.archive_count > 0 ? (
|
||||
) : project.total_items > 0 ? (
|
||||
<span className="text-xs px-2 py-0.5 rounded-full whitespace-nowrap font-medium bg-bambu-dark text-bambu-gray">
|
||||
{project.archive_count} print{project.archive_count !== 1 ? 's' : ''}
|
||||
{project.total_items} item{project.total_items !== 1 ? 's' : ''}
|
||||
</span>
|
||||
) : null}
|
||||
{isCompleted && (
|
||||
@@ -365,7 +365,7 @@ function ProjectCard({ project, onClick, onEdit, onDelete }: ProjectCardProps) {
|
||||
<div className="flex items-center justify-between text-xs mb-2">
|
||||
<span className="text-bambu-gray">Progress</span>
|
||||
<span className={progressPercent >= 100 ? 'text-bambu-green font-medium' : 'text-white'}>
|
||||
{project.archive_count} / {project.target_count}
|
||||
{project.total_items} / {project.target_count}
|
||||
</span>
|
||||
</div>
|
||||
<div className="h-2.5 bg-bambu-dark/80 rounded-full overflow-hidden backdrop-blur-sm">
|
||||
@@ -384,11 +384,11 @@ function ProjectCard({ project, onClick, onEdit, onDelete }: ProjectCardProps) {
|
||||
{progressPercent.toFixed(0)}% complete
|
||||
</div>
|
||||
</>
|
||||
) : project.archive_count > 0 ? (
|
||||
) : project.total_items > 0 ? (
|
||||
<div className="flex items-center gap-4 text-xs">
|
||||
<div className="flex items-center gap-1.5 text-bambu-gray">
|
||||
<Archive className="w-3.5 h-3.5" />
|
||||
<span>{project.archive_count} print{project.archive_count !== 1 ? 's' : ''} completed</span>
|
||||
<span>{project.total_items} item{project.total_items !== 1 ? 's' : ''} completed</span>
|
||||
</div>
|
||||
{project.queue_count > 0 && (
|
||||
<div className="flex items-center gap-1.5 text-blue-400">
|
||||
@@ -444,9 +444,9 @@ function ProjectCard({ project, onClick, onEdit, onDelete }: ProjectCardProps) {
|
||||
{/* Stats footer */}
|
||||
<div className="flex items-center justify-between pt-3 border-t border-bambu-dark-tertiary">
|
||||
<div className="flex items-center gap-4 text-xs text-bambu-gray">
|
||||
<div className="flex items-center gap-1.5" title="Completed prints">
|
||||
<div className="flex items-center gap-1.5" title="Total items printed">
|
||||
<Archive className="w-3.5 h-3.5" />
|
||||
<span>{project.archive_count}</span>
|
||||
<span>{project.total_items}</span>
|
||||
</div>
|
||||
{project.queue_count > 0 && (
|
||||
<div className="flex items-center gap-1.5 text-blue-400" title="In queue">
|
||||
|
||||
Reference in New Issue
Block a user