fix: full-width content area + auto-create MediaAssets on render complete

- Remove max-w-* constraints from all data/table pages so content fills available width after sidebar (Billing, MediaBrowser, OrderDetail, WorkerManagement, WorkerActivity, Materials, Tenants, AssetLibrary, NewProductOrder, ProductDetail)
- Narrow form/settings pages keep their max-width (NotificationSettings, Preferences, NewOrder, Notifications)
- render_order_line_task: create MediaAsset record on render success so results immediately appear in Media Browser without requiring the retroactive import button

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-07 00:17:17 +01:00
parent f5ca91ee02
commit 5029a94608
11 changed files with 36 additions and 10 deletions
+26
View File
@@ -644,6 +644,32 @@ def render_order_line_task(self, order_line_id: str):
) )
session.commit() session.commit()
if success:
# Create MediaAsset so the render appears in the Media Browser
try:
from app.domains.media.models import MediaAsset, MediaAssetType as MAT
_ext = str(output_path).rsplit(".", 1)[-1].lower() if "." in str(output_path) else "bin"
_mime = "video/mp4" if _ext in ("mp4", "webm") else ("image/jpeg" if _ext in ("jpg", "jpeg") else "image/png")
_is_anim = bool(line.output_type and line.output_type.is_animation)
_at = MAT.turntable if _is_anim else MAT.still
_tenant_id = line.product.cad_file.tenant_id if (line.product and line.product.cad_file) else None
_existing = session.execute(
select(MediaAsset.id).where(MediaAsset.storage_key == output_path).limit(1)
).scalar_one_or_none()
if not _existing:
_asset = MediaAsset(
tenant_id=_tenant_id,
order_line_id=line.id,
product_id=line.product_id,
asset_type=_at,
storage_key=output_path,
mime_type=_mime,
)
session.add(_asset)
session.commit()
except Exception:
logger.exception("Failed to create MediaAsset for order_line %s", order_line_id)
if success: if success:
emit(order_line_id, f"Render completed in {elapsed:.1f}s", "success") emit(order_line_id, f"Render completed in {elapsed:.1f}s", "success")
else: else:
+1 -1
View File
@@ -273,7 +273,7 @@ export default function AssetLibraryPage() {
}) })
return ( return (
<div className="p-8 max-w-5xl mx-auto"> <div className="p-8">
{/* Header */} {/* Header */}
<div className="mb-6 flex items-center justify-between"> <div className="mb-6 flex items-center justify-between">
<div> <div>
+1 -1
View File
@@ -125,7 +125,7 @@ export default function BillingPage() {
const paidCount = invoices.filter(inv => inv.status === 'paid').length const paidCount = invoices.filter(inv => inv.status === 'paid').length
return ( return (
<div className="p-6 space-y-5 max-w-screen-xl"> <div className="p-6 space-y-5">
{/* Header */} {/* Header */}
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>
+1 -1
View File
@@ -200,7 +200,7 @@ export default function MaterialsPage() {
const totalAliases = materials.reduce((sum, m) => sum + m.aliases.length, 0) const totalAliases = materials.reduce((sum, m) => sum + m.aliases.length, 0)
return ( return (
<div className="p-8 max-w-5xl mx-auto"> <div className="p-8">
{/* Header */} {/* Header */}
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
<FlaskConical size={22} className="text-accent" /> <FlaskConical size={22} className="text-accent" />
+1 -1
View File
@@ -259,7 +259,7 @@ export default function MediaBrowserPage() {
} }
return ( return (
<div className="p-6 space-y-5 max-w-screen-xl"> <div className="p-6 space-y-5">
{/* Header */} {/* Header */}
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>
+1 -1
View File
@@ -331,7 +331,7 @@ export default function NewProductOrderPage() {
} }
return ( return (
<div className="p-8 max-w-6xl mx-auto"> <div className="p-8">
{/* Header */} {/* Header */}
<div className="flex items-center gap-4 mb-6"> <div className="flex items-center gap-4 mb-6">
<Link to="/orders/new" className="btn-secondary"><ArrowLeft size={16} />Back</Link> <Link to="/orders/new" className="btn-secondary"><ArrowLeft size={16} />Back</Link>
+1 -1
View File
@@ -204,7 +204,7 @@ export default function OrderDetailPage() {
const activeFilterCount = countActiveFilters(filters) const activeFilterCount = countActiveFilters(filters)
return ( return (
<div className="p-8 max-w-6xl mx-auto"> <div className="p-8">
{/* Header */} {/* Header */}
<div className="flex items-center gap-4 mb-6 flex-wrap"> <div className="flex items-center gap-4 mb-6 flex-wrap">
<Link to="/orders" className="btn-secondary"> <Link to="/orders" className="btn-secondary">
+1 -1
View File
@@ -311,7 +311,7 @@ export default function ProductDetailPage() {
if (!product) return <div className="p-8 text-red-500">Product not found</div> if (!product) return <div className="p-8 text-red-500">Product not found</div>
return ( return (
<div className="p-8 max-w-4xl mx-auto"> <div className="p-8">
{/* Back */} {/* Back */}
<Link to="/products" className="inline-flex items-center gap-1 text-sm text-content-secondary hover:text-content mb-4"> <Link to="/products" className="inline-flex items-center gap-1 text-sm text-content-secondary hover:text-content mb-4">
<ArrowLeft size={15} /> Products <ArrowLeft size={15} /> Products
+1 -1
View File
@@ -116,7 +116,7 @@ export default function TenantsPage() {
} }
return ( return (
<div className="p-6 max-w-5xl mx-auto"> <div className="p-6">
{/* Header */} {/* Header */}
<div className="flex items-center justify-between mb-6"> <div className="flex items-center justify-between mb-6">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
+1 -1
View File
@@ -61,7 +61,7 @@ export default function WorkerActivityPage() {
const isEmpty = !isLoading && events.length === 0 const isEmpty = !isLoading && events.length === 0
return ( return (
<div className="p-8 max-w-5xl mx-auto space-y-6"> <div className="p-8 space-y-6">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<Activity size={22} className="text-accent" /> <Activity size={22} className="text-accent" />
<h1 className="text-2xl font-bold text-content">Worker Activity</h1> <h1 className="text-2xl font-bold text-content">Worker Activity</h1>
+1 -1
View File
@@ -189,7 +189,7 @@ export default function WorkerManagement() {
const queueDepths = queueData?.queue_depths ?? {} const queueDepths = queueData?.queue_depths ?? {}
return ( return (
<div className="p-8 max-w-5xl mx-auto space-y-8"> <div className="p-8 space-y-8">
{/* Header */} {/* Header */}
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>