Modulo makers3d desarrollado con codex V 0.0.1
This commit is contained in:
@@ -0,0 +1,170 @@
|
||||
import Link from "next/link";
|
||||
import { headers } from "next/headers";
|
||||
import { notFound } from "next/navigation";
|
||||
|
||||
import FavoriteToggle from "../../components/FavoriteToggle";
|
||||
import WorkHeroGallery from "../../components/WorkHeroGallery";
|
||||
import WorkShareActions from "../../components/WorkShareActions";
|
||||
import { serverFetch } from "../../lib/api";
|
||||
|
||||
function toText(value: unknown, fallback = "") {
|
||||
return typeof value === "string" && value ? value : fallback;
|
||||
}
|
||||
|
||||
function asArray(value: unknown): string[] {
|
||||
return Array.isArray(value) ? value.map((item) => String(item)) : [];
|
||||
}
|
||||
|
||||
function asRecords(value: unknown): Array<Record<string, unknown>> {
|
||||
return Array.isArray(value) ? value.filter((item): item is Record<string, unknown> => Boolean(item) && typeof item === "object" && !Array.isArray(item)) : [];
|
||||
}
|
||||
|
||||
function galleryFor(work: Record<string, unknown>) {
|
||||
const images = [toText(work.image_url), ...asArray(work.gallery_urls)].filter(Boolean);
|
||||
return Array.from(new Set(images));
|
||||
}
|
||||
|
||||
function draftDataFor(work: Record<string, unknown>) {
|
||||
return work.draft_data && typeof work.draft_data === "object" && !Array.isArray(work.draft_data)
|
||||
? work.draft_data as Record<string, unknown>
|
||||
: {};
|
||||
}
|
||||
|
||||
export default async function WorkPage({ params }: { params: Promise<{ slug: string }> }) {
|
||||
const { slug } = await params;
|
||||
let work: Record<string, unknown>;
|
||||
try {
|
||||
const data = await serverFetch<{ work: Record<string, unknown> }>(`/works/${slug}`);
|
||||
work = data.work;
|
||||
} catch {
|
||||
notFound();
|
||||
}
|
||||
const requestHeaders = await headers();
|
||||
const gallery = galleryFor(work);
|
||||
const draftData = draftDataFor(work);
|
||||
const beforeImage = toText(draftData.beforeImageUrl);
|
||||
const afterImage = toText(draftData.afterImageUrl);
|
||||
const showBeforeAfter = Boolean(draftData.includeBeforeAfter && beforeImage && afterImage);
|
||||
const relatedWorks = asRecords(work.related_works);
|
||||
const title = toText(work.title, "Trabajo publicado");
|
||||
const makerName = toText(work.maker_name, "Maker");
|
||||
const host = requestHeaders.get("x-forwarded-host") || requestHeaders.get("host") || "dev.jazari.com.ar";
|
||||
const protocol = requestHeaders.get("x-forwarded-proto") || (host.includes("localhost") ? "http" : "https");
|
||||
const workUrl = `${protocol}://${host}/works/${slug}`;
|
||||
const contactHref = `/consultas/nueva?makerId=${encodeURIComponent(String(work.maker_id))}&sourceType=work&sourceId=${encodeURIComponent(String(work.id))}&makerName=${encodeURIComponent(makerName)}&contextTitle=${encodeURIComponent(title)}&returnTo=${encodeURIComponent(`/works/${slug}`)}`;
|
||||
|
||||
return (
|
||||
<main className="page-shell work-detail-page">
|
||||
<section className="work-detail-phone">
|
||||
<WorkHeroGallery gallery={gallery} title={title} workId={String(work.id)} shareUrl={workUrl} />
|
||||
|
||||
<section className="work-detail-title-card">
|
||||
<h1>{title}</h1>
|
||||
<div className="work-detail-tags">
|
||||
<span>{toText(work.technology, "FDM")}</span>
|
||||
<span>{toText(work.material, "PLA")}</span>
|
||||
</div>
|
||||
<Link href={`/makers/${toText(work.maker_slug)}`} className="work-detail-maker-card">
|
||||
<span>{makerName.slice(0, 2).toUpperCase()}</span>
|
||||
<div>
|
||||
<strong>{makerName}</strong>
|
||||
<small><span className="map-home-star">*</span> 4,9 | 128 opiniones | Aceptando trabajos</small>
|
||||
</div>
|
||||
<b>></b>
|
||||
</Link>
|
||||
</section>
|
||||
|
||||
<section className="work-detail-section">
|
||||
<h2>Que se hizo?</h2>
|
||||
<p>{toText(work.summary, "Se reprodujo una pieza a medida para resolver una necesidad real del cliente.")}</p>
|
||||
<div className="work-detail-feature-grid">
|
||||
<article><span>01</span><strong>Pieza funcional</strong></article>
|
||||
<article><span>02</span><strong>Resiste calor</strong></article>
|
||||
<article><span>03</span><strong>1 unidad</strong></article>
|
||||
<article><span>04</span><strong>48 h fabricacion</strong></article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{showBeforeAfter ? (
|
||||
<section className="work-detail-section">
|
||||
<div className="work-detail-section-head">
|
||||
<h2>Antes / Resultado</h2>
|
||||
<Link href="#galeria">Ver mas</Link>
|
||||
</div>
|
||||
<div className="work-detail-before-after">
|
||||
<article>
|
||||
<img src={beforeImage} alt="Antes" />
|
||||
<span>Antes</span>
|
||||
</article>
|
||||
<b>></b>
|
||||
<article>
|
||||
<img src={afterImage} alt="Resultado" />
|
||||
<span>Resultado</span>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
) : null}
|
||||
|
||||
<section className="work-detail-section">
|
||||
<h2>Historia del proyecto</h2>
|
||||
<p>{toText(work.story, "El cliente necesitaba recuperar una pieza o validar una solucion. Se tomo una referencia, se ajusto el diseno y se fabrico una version funcional lista para probar.")}</p>
|
||||
</section>
|
||||
|
||||
<section className="work-detail-section">
|
||||
<details open>
|
||||
<summary>Detalles tecnicos</summary>
|
||||
<div className="work-detail-specs">
|
||||
<span>Tecnologia</span><strong>{toText(work.technology, "FDM")}</strong>
|
||||
<span>Material</span><strong>{toText(work.material, "PETG")}</strong>
|
||||
<span>Acabado</span><strong>Lijado y ajuste manual</strong>
|
||||
<span>Resolucion</span><strong>0,16 mm capa</strong>
|
||||
<span>Relleno</span><strong>40%</strong>
|
||||
<span>Tiempo</span><strong>48 horas</strong>
|
||||
</div>
|
||||
</details>
|
||||
</section>
|
||||
|
||||
<section id="galeria" className="work-detail-section">
|
||||
<div className="work-detail-section-head">
|
||||
<h2>Galeria</h2>
|
||||
<span>{gallery.length} fotos</span>
|
||||
</div>
|
||||
<div className="work-detail-gallery-grid">
|
||||
{gallery.map((image, index) => (
|
||||
<img key={`${image}-${index}`} src={image} alt={`${title} ${index + 1}`} />
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{relatedWorks.length ? (
|
||||
<section className="work-detail-section">
|
||||
<div className="work-detail-section-head">
|
||||
<h2>Tambien puede interesarte</h2>
|
||||
<Link href="/discover">Ver todos</Link>
|
||||
</div>
|
||||
<div className="work-detail-related-rail">
|
||||
{relatedWorks.map((item) => (
|
||||
<Link key={String(item.id)} href={`/works/${toText(item.slug)}`}>
|
||||
<img src={toText(item.image_url, "/demo/work-custom.svg")} alt={toText(item.title, "Trabajo sugerido")} />
|
||||
<strong>{toText(item.title, "Trabajo sugerido")}</strong>
|
||||
<span>{toText(item.technology, "FDM")} | {toText(item.material, "PETG")}</span>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
) : null}
|
||||
|
||||
<section className="work-detail-section work-detail-share">
|
||||
<h2>Compartir este trabajo</h2>
|
||||
<p>Comparte este enlace para mostrar la referencia o enviarla por WhatsApp.</p>
|
||||
<WorkShareActions title={title} url={workUrl} />
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<div className="work-detail-bottom-cta">
|
||||
<FavoriteToggle targetType="work" targetId={String(work.id)} compact />
|
||||
<a href={contactHref} className="button button-primary">Quiero algo similar</a>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user