feat: project cover art with AI generation, branding rename, RBAC fix, computation graph

- Add DALL-E cover art generation for projects (Azure OpenAI + standard OpenAI)
- CoverArtSection component with generate/upload/remove/focus-point controls
- Client-side image compression (10MB input → WebP/JPEG, max 1920px)
- DALL-E settings in admin panel (deployment, endpoint, API key)
- MCP assistant tools for cover art (generate_project_cover, remove_project_cover)
- Rename "Planarchy" → "plANARCHY" across all UI-facing text (13 files)
- Fix hardcoded canEdit={true} on project detail page — now checks user role
- Computation graph visualization (2D/3D) for calculation rules
- OG image and OpenGraph metadata

Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
2026-03-18 11:31:56 +01:00
parent 21af720f90
commit 093e13b88f
86 changed files with 5623 additions and 744 deletions
@@ -4,6 +4,7 @@ import { useMemo } from "react";
import Link from "next/link";
import { trpc } from "~/lib/trpc/client.js";
import type { WidgetProps } from "../widget-registry.js";
import { InfoTooltip } from "~/components/ui/InfoTooltip.js";
const STATUS_DOT: Record<string, string> = {
ACTIVE: "bg-green-500",
@@ -78,8 +79,9 @@ export function MyProjectsWidget({ config }: WidgetProps) {
<div className="flex-1 overflow-y-auto divide-y divide-gray-100 dark:divide-gray-800">
{favoriteProjects.length > 0 && (
<div>
<div className="px-3 py-1.5 text-[10px] font-semibold uppercase tracking-wider text-amber-600 dark:text-amber-400 bg-amber-50/50 dark:bg-amber-900/10">
<div className="px-3 py-1.5 text-[10px] font-semibold uppercase tracking-wider text-amber-600 dark:text-amber-400 bg-amber-50/50 dark:bg-amber-900/10 flex items-center">
Favorites
<InfoTooltip content="Projects you have starred. Click the star icon on any project to add or remove it from your favorites." />
</div>
{favoriteProjects.map((p) => (
<ProjectRow key={p.id} project={p} isFavorite onToggleFavorite={() => toggleMutation.mutate({ projectId: p.id })} />
@@ -89,8 +91,9 @@ export function MyProjectsWidget({ config }: WidgetProps) {
{responsibleProjects.length > 0 && (
<div>
<div className="px-3 py-1.5 text-[10px] font-semibold uppercase tracking-wider text-brand-600 dark:text-brand-400 bg-brand-50/50 dark:bg-brand-900/10">
<div className="px-3 py-1.5 text-[10px] font-semibold uppercase tracking-wider text-brand-600 dark:text-brand-400 bg-brand-50/50 dark:bg-brand-900/10 flex items-center">
Responsible
<InfoTooltip content="Projects where you are listed as the responsible person. These are automatically shown based on your user name." />
</div>
{responsibleProjects.map((p) => (
<ProjectRow key={p.id} project={p} isFavorite={false} onToggleFavorite={() => toggleMutation.mutate({ projectId: p.id })} />
@@ -130,7 +133,7 @@ function ProjectRow({
<span className="font-mono text-xs text-gray-400 dark:text-gray-500 mr-1.5">{project.shortCode}</span>
{project.name}
</Link>
<span className="flex-shrink-0 text-[10px] text-gray-400 dark:text-gray-500 uppercase">{project.status}</span>
<span className="flex-shrink-0 text-[10px] text-gray-400 dark:text-gray-500 uppercase" title={`Project status: ${project.status}`}>{project.status}</span>
</div>
);
}