Updated website

This commit is contained in:
maziggy
2026-01-02 10:17:21 +01:00
parent 019a9822a0
commit 4e25377e5b
4 changed files with 43 additions and 17 deletions
+5 -1
View File
@@ -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',
+22 -1
View File
@@ -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>
+8 -7
View File
@@ -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: [],
+8 -8
View File
@@ -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">