feat(catalogo): link de afiliado por idioma e botão alinhado no rodapé do card
Release / Plano (reaproveitar a imagem?) (push) Successful in 12s
CI / Lint e auditorias (push) Successful in 1m52s
CI / Trivy (dependências e segredos) (push) Successful in 14s
CI / Imagem Docker (linux/arm64) (push) Failing after 1m22s
Release / ci (push) Failing after 3m31s
Release / Publica a imagem (linux/arm64) (push) Skipped
Release / Cria a release no Gitea (push) Skipped
Release / Deploy no Coolify (push) Skipped
Release / Plano (reaproveitar a imagem?) (push) Successful in 12s
CI / Lint e auditorias (push) Successful in 1m52s
CI / Trivy (dependências e segredos) (push) Successful in 14s
CI / Imagem Docker (linux/arm64) (push) Failing after 1m22s
Release / ci (push) Failing after 3m31s
Release / Publica a imagem (linux/arm64) (push) Skipped
Release / Cria a release no Gitea (push) Skipped
Release / Deploy no Coolify (push) Skipped
O botão do AliExpress passa a ficar no rodapé do card, alinhado entre os cards. O link usa a coluna Link_<idioma> da planilha (en, es, fr, de, zh-CN) e cai no Link (Brasil) quando ela está vazia. Colunas de link não viram filtro, e a API lê A1:AZ1000. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
5fba78308c
commit
900c61df3d
5 files changed
+39
-14
No files matched your search
@@ -8,6 +8,9 @@ Tudo o que muda de relevante no ESPDocs está aqui. O formato segue o
|
||||
|
||||
### Adicionado
|
||||
|
||||
- Catálogo de placas com link de afiliado por país: a planilha ganha as colunas `Link_en`,
|
||||
`Link_es`, `Link_fr`, `Link_de` e `Link_zh-CN`, e o botão do card usa a do idioma da página,
|
||||
caindo no `Link` (Brasil) quando a coluna está vazia. As colunas de link nunca viram filtro.
|
||||
- Deploy automático no Coolify: depois de publicar a imagem, o `release.yml` pede o deploy e
|
||||
espera o resultado. Só implanta quando o `latest` muda, então merge só de documentação ou
|
||||
tag não reiniciam o container. Um disparo manual do workflow força o deploy.
|
||||
@@ -26,6 +29,8 @@ Tudo o que muda de relevante no ESPDocs está aqui. O formato segue o
|
||||
|
||||
### Corrigido
|
||||
|
||||
- Card do catálogo: o botão "Ver no AliExpress" fica sempre no rodapé, alinhado entre os cards.
|
||||
- A API do catálogo lê `A1:AZ1000`, para comportar as colunas novas da planilha.
|
||||
- O link do arquivo do Fritzing na apostila da formação 1 apontava para `/apostilas/1/`,
|
||||
caminho que não existe.
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import Header from "@/components/Header";
|
||||
import Footer from "@/components/Footer";
|
||||
import PageHero from "@/components/PageHero";
|
||||
import BoardCard from "@/components/BoardCard";
|
||||
import { isLinkColumn } from "@/lib/boardLinks";
|
||||
import { AlertTriangle, RefreshCw, Search, ShoppingCart, SlidersHorizontal } from "lucide-react";
|
||||
|
||||
const HEADER_MAP_EN = {
|
||||
@@ -184,7 +185,7 @@ export default function PlacasPage() {
|
||||
|
||||
const generateFilterOptions = (data, headers) => {
|
||||
const options = {};
|
||||
const filterableColumns = headers.filter((h) => !excludedColumns.includes(h));
|
||||
const filterableColumns = headers.filter((h) => !excludedColumns.includes(h) && !isLinkColumn(h));
|
||||
|
||||
filterableColumns.forEach((column) => {
|
||||
const uniqueValues = new Set();
|
||||
@@ -265,7 +266,7 @@ export default function PlacasPage() {
|
||||
|
||||
const activeFilterCount = Object.keys(activeFilters).length + (searchTerm ? 1 : 0);
|
||||
|
||||
const filterableColumns = headers.filter((h) => !excludedColumns.includes(h));
|
||||
const filterableColumns = headers.filter((h) => !excludedColumns.includes(h) && !isLinkColumn(h));
|
||||
|
||||
const hero = (
|
||||
<PageHero
|
||||
|
||||
@@ -16,7 +16,7 @@ export async function GET() {
|
||||
|
||||
const response = await sheets.spreadsheets.values.get({
|
||||
spreadsheetId,
|
||||
range: 'A1:Z1000',
|
||||
range: 'A1:AZ1000',
|
||||
key: process.env.GOOGLE_SHEETS_API_KEY,
|
||||
});
|
||||
|
||||
|
||||
+18
-11
@@ -2,6 +2,7 @@
|
||||
|
||||
import { ArrowUpRight, Award, Check, CircleHelp } from "lucide-react";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { isLinkColumn, linkForLocale } from "@/lib/boardLinks";
|
||||
|
||||
const HEADER_MAP_EN = {
|
||||
"Nome": "Name",
|
||||
@@ -105,9 +106,13 @@ export default function BoardCard({ board }) {
|
||||
const isDe = pathname === "/de" || pathname.startsWith("/de/");
|
||||
const isZh = pathname === "/zh-CN" || pathname.startsWith("/zh-CN/");
|
||||
|
||||
const mainFields = ["Nome", "Link", "Imagem", "Loja Oficial"];
|
||||
const locale = isEn ? "en" : isEs ? "es" : isFr ? "fr" : isDe ? "de" : isZh ? "zh-CN" : "pt";
|
||||
const link = linkForLocale(board, locale);
|
||||
const imagem = board.Imagem && link ? board.Imagem.replace(/href="[^"]*"/, `href="${link}"`) : board.Imagem;
|
||||
|
||||
const technicalFields = Object.keys(board).filter((key) => !mainFields.includes(key) && board[key]);
|
||||
const mainFields = ["Nome", "Imagem", "Loja Oficial"];
|
||||
|
||||
const technicalFields = Object.keys(board).filter((key) => !isLinkColumn(key) && !mainFields.includes(key) && board[key]);
|
||||
|
||||
const valor = (campo) => board[campo]?.toString().toLowerCase().trim();
|
||||
const recursos = technicalFields.filter((campo) => ["sim", "opcional"].includes(valor(campo)));
|
||||
@@ -118,7 +123,7 @@ export default function BoardCard({ board }) {
|
||||
{board.Imagem && (
|
||||
<div
|
||||
className="flex shrink-0 justify-center border-b border-line bg-surface-2 p-4 [&_img]:h-44 [&_img]:w-auto [&_img]:max-w-full [&_img]:object-contain"
|
||||
dangerouslySetInnerHTML={{ __html: board.Imagem }}
|
||||
dangerouslySetInnerHTML={{ __html: imagem }}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -167,14 +172,16 @@ export default function BoardCard({ board }) {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{board.Link && (
|
||||
<a href={board.Link} target="_blank" rel="noopener noreferrer" className="btn btn-primary mt-6 w-full">
|
||||
{isEn ? "View on AliExpress" : isEs ? "Ver en AliExpress" : isFr ? "Voir sur AliExpress" : isZh ? "在 AliExpress 查看" : isDe ? "Auf AliExpress ansehen" : "Ver no AliExpress"}
|
||||
<ArrowUpRight className="h-4 w-4" aria-hidden="true" />
|
||||
<span className="sr-only">
|
||||
{isEn ? " (opens in new tab)" : isEs ? " (abre en una pestaña nueva)" : isFr ? " (ouvre un nouvel onglet)" : isZh ? "(在新标签页打开)" : isDe ? " (öffnet in neuem Tab)" : " (abre em nova aba)"}
|
||||
</span>
|
||||
</a>
|
||||
{link && (
|
||||
<div className="mt-auto pt-6">
|
||||
<a href={link} target="_blank" rel="noopener noreferrer" className="btn btn-primary w-full">
|
||||
{isEn ? "View on AliExpress" : isEs ? "Ver en AliExpress" : isFr ? "Voir sur AliExpress" : isZh ? "在 AliExpress 查看" : isDe ? "Auf AliExpress ansehen" : "Ver no AliExpress"}
|
||||
<ArrowUpRight className="h-4 w-4" aria-hidden="true" />
|
||||
<span className="sr-only">
|
||||
{isEn ? " (opens in new tab)" : isEs ? " (abre en una pestaña nueva)" : isFr ? " (ouvre un nouvel onglet)" : isZh ? "(在新标签页打开)" : isDe ? " (öffnet in neuem Tab)" : " (abre em nova aba)"}
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</article>
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
// Colunas de link da planilha: "Link" é o padrão (Brasil) e "Link_<locale>" o link
|
||||
// de afiliado gerado para o país de envio daquele idioma.
|
||||
export const LINK_COLUMN_PREFIX = "Link_";
|
||||
|
||||
export function isLinkColumn(coluna) {
|
||||
return coluna === "Link" || coluna.startsWith(LINK_COLUMN_PREFIX);
|
||||
}
|
||||
|
||||
export function linkForLocale(board, locale) {
|
||||
const especifico = locale && locale !== "pt" ? board[`${LINK_COLUMN_PREFIX}${locale}`] : "";
|
||||
return (especifico || board.Link || "").trim();
|
||||
}
|
||||
Reference in new issue
Block a user