This commit is contained in:
2025-05-05 11:19:03 -03:00
parent 9be27c9ccb
commit fef31734d6
11 changed files with 234 additions and 20 deletions

View File

@@ -26,6 +26,7 @@
"react": "^19.1.0",
"react-dom": "^19.1.0",
"react-hook-form": "^7.56.0",
"smartypants": "^0.2.2",
"tailwindcss": "^4.0.13",
"vanilla-cookieconsent": "^3.1.0",
"zod": "^3.24.3"
@@ -7120,6 +7121,16 @@
"integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==",
"license": "MIT"
},
"node_modules/smartypants": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/smartypants/-/smartypants-0.2.2.tgz",
"integrity": "sha512-TzobUYoEft/xBtb2voRPryAUIvYguG0V7Tt3de79I1WfXgCwelqVsGuZSnu3GFGRZhXR90AeEYIM+icuB/S06Q==",
"license": "BSD-3-Clause",
"bin": {
"smartypants": "bin/smartypants.js",
"smartypantsu": "bin/smartypantsu.js"
}
},
"node_modules/smol-toml": {
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.3.4.tgz",

View File

@@ -27,6 +27,7 @@
"react": "^19.1.0",
"react-dom": "^19.1.0",
"react-hook-form": "^7.56.0",
"smartypants": "^0.2.2",
"tailwindcss": "^4.0.13",
"vanilla-cookieconsent": "^3.1.0",
"zod": "^3.24.3"

View File

@@ -5,7 +5,7 @@
<slot name="label" />
</summary>
<div
class="absolute right-0 top-full translate-y-1 bg-stone-900 p-2.5 border border-white/15 min-w-56 rounded-lg shadow drop-shadow"
class="absolute right-0 top-full translate-y-1 bg-stone-900 border border-white/15 min-w-74 rounded-lg shadow drop-shadow"
>
<slot />
</div>

View File

@@ -9,7 +9,7 @@ const courses = await getCollection(
({ data }) => data.draft != true,
);
const { title } = Astro.props;
const { title, course } = Astro.props;
---
<nav class="sticky top-0 z-10 bg-lime-400 py-3 drop-shadow shadow-sm">
@@ -27,8 +27,18 @@ const { title } = Astro.props;
<BuyDropdown class="ml-auto">
<Fragment slot="label">Contratar agora</Fragment>
<ul>
<li>...</li>
<ul class="divide-y divide-white/10">
<li class="uppercase py-2.5 px-5">EDUSEG&reg; Flexível</li>
<li class="uppercase py-2.5 px-5">EDUSEG&reg; In-Company</li>
<li class="uppercase py-2.5 px-5">EDUSEG&reg; Conteúdo</li>
<li class="py-2.5 px-5">
Contratar {
new Intl.NumberFormat("pt-BR", {
style: "currency",
currency: "BRL",
}).format(course.unit_price)
} p/ matrícula
</li>
</ul>
</BuyDropdown>
</Container>

View File

@@ -30,7 +30,9 @@ import Modal from "~/components/Modal.astro";
<h2 class="text-xl/6 font-semibold">
Catálogo completo sempre à sua disposição
</h2>
<ul class="list-disc max-lg:pl-3.5 space-y-1.5 text-sm/5">
<ul
class="list-disc max-lg:pl-3.5 space-y-1.5 text-sm/5 marker:text-lime-400"
>
<li>Acesse todos os cursos imediatamente</li>
<li>Pague apenas pelo que usar, mês a mês</li>
<li>Gestão e autonomia direto pela plataforma</li>
@@ -58,7 +60,9 @@ import Modal from "~/components/Modal.astro";
<h2 class="text-xl/6 font-semibold">
Treinamento presencial na sua empresa
</h2>
<ul class="list-disc max-lg:pl-3.5 space-y-1.5 text-sm/5">
<ul
class="list-disc max-lg:pl-3.5 space-y-1.5 text-sm/5 marker:text-lime-400"
>
<li>Atendemos nas principais cidades do Brasil</li>
<li>Instrutores especialistas com vivência prática</li>
<li>Ideal para grandes turmas ou treinamentos práticos</li>
@@ -85,7 +89,9 @@ import Modal from "~/components/Modal.astro";
<h2 class="text-xl/6 font-semibold">
Leve nosso conteúdo para sua plataforma
</h2>
<ul class="list-disc max-lg:pl-3.5 space-y-1.5 text-sm/5">
<ul
class="list-disc max-lg:pl-3.5 space-y-1.5 text-sm/5 marker:text-lime-400"
>
<li>Customização completa com sua identidade visual</li>
<li>Entregamos em formato SCORM para qualquer LMS</li>
<li>Ideal para empresas com ambiente EAD próprio</li>
@@ -104,3 +110,7 @@ import Modal from "~/components/Modal.astro";
</div>
</section>
</Container>
<script is:inline>
console.log(window);
</script>

View File

@@ -0,0 +1,99 @@
---
export type Image = {
src: string;
alt: string;
};
export type SEOMetadata = {
name: string;
title: string;
description: string;
image: Image;
canonicalURL?: URL | string | null;
locale?: string;
};
export type OpenGraph = Partial<SEOMetadata> & {
type?: string;
};
export type Twitter = Partial<SEOMetadata> & {
handle?: string;
card?: "summary" | "summary_large_image";
};
export type Props = SEOMetadata & {
og?: OpenGraph;
twitter?: Twitter;
};
const {
name,
title,
description,
image,
locale = "en",
canonicalURL = new URL(Astro.url.pathname, Astro.site),
} = Astro.props;
const og = {
name,
title,
description,
canonicalURL,
image,
locale,
type: "website",
...Astro.props.og,
} satisfies OpenGraph;
const twitter = {
name,
title,
description,
canonicalURL,
image,
locale,
card: "summary_large_image",
...Astro.props.twitter,
} satisfies Twitter;
/**
* Enforce some standard canonical URL formatting across the site.
*/
function formatCanonicalURL(url: string | URL) {
const path = url.toString();
const hasQueryParams = path.includes("?");
// If there are query params, make sure the URL has no trailing slash
if (hasQueryParams) {
path.replace(/\/?$/, "");
}
// otherwise, canonical URL always has a trailing slash
return path.replace(/\/?$/, hasQueryParams ? "" : "/");
}
---
{/* Page Metadata */}
{
canonicalURL && (
<link rel="canonical" href={formatCanonicalURL(canonicalURL)} />
)
}
<meta name="description" content={description} />
{/* OpenGraph Tags */}
<meta property="og:title" content={og.title} />
<meta property="og:type" content={og.type} />
{
og.canonicalURL && (
<meta property="og:url" content={formatCanonicalURL(og.canonicalURL)} />
)
}
<meta property="og:locale" content={og.locale} />
<meta property="og:description" content={og.description} />
<meta property="og:site_name" content={og.name} />
{og.image && <meta property="og:image" content={og.image.src} />}
{og.image && <meta property="og:image:alt" content={og.image.alt} />}
{/* Twitter Tags */}
{twitter.card && <meta name="twitter:card" content={twitter.card} />}
{twitter.handle && <meta name="twitter:site" content={twitter.handle} />}
<meta name="twitter:title" content={twitter.title} />
<meta name="twitter:description" content={twitter.description} />
{twitter.image && <meta name="twitter:image" content={twitter.image.src} />}
{twitter.image && <meta name="twitter:image:alt" content={twitter.image.alt} />}

View File

@@ -0,0 +1,37 @@
export type SocialLink = {
me?: string;
/** Longer descriptive label, e.g. `"Join the Astro community on Discord"` */
text: string;
/** Short label with the name of the platform, e.g. `"Discord"`*/
label: string;
icon: string;
href: string;
/** Platform ID, e.g. `"discord"` */
platform: string;
footerOnly?: boolean;
};
export type SiteInfo = {
name: string;
title: string;
description: string;
image: {
src: string;
alt: string;
};
socialLinks: SocialLink[];
};
const siteInfo: SiteInfo = {
name: "EDUSEG&reg;",
title: " Educação que garante sua segurança",
description:
"Astro builds fast content sites, powerful web applications, dynamic server APIs, and everything in-between.",
image: {
src: "/og/social.jpg",
alt: "Educação que garante sua segurança",
},
socialLinks: [],
};
export default siteInfo;

View File

@@ -1,22 +1,18 @@
---
import "~/styles/app.css";
import BaseHead, {
type Props as HeadProps,
} from "./_components/BaseHead.astro";
import Header from "./_components/Header.astro";
import Footer from "./_components/Footer.astro";
interface Props extends HeadProps {}
---
<!doctype html>
<html lang="pt-br">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="generator" content={Astro.generator} />
<link rel="sitemap" href="/sitemap-index.xml" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<slot name="head">
<title>
Educação que garante sua segurança &mdash; EDUSEG&reg;
</title>
</slot>
<BaseHead {...Astro.props} />
<script
is:inline
@@ -31,7 +27,6 @@ import Footer from "./_components/Footer.astro";
data-type="text/partytown"
data-category="analytics"
>
console.log("a");
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
@@ -47,7 +42,7 @@ import Footer from "./_components/Footer.astro";
<slot name="nav" />
<main id="content" data-pagefind-body>
<main id="main" data-pagefind-body>
<slot />
</main>

View File

@@ -0,0 +1,50 @@
---
import smartypants from "smartypants";
import siteInfo from "~/data/site-info";
import SEO from "~/components/SEO.astro";
export type Props = {
title?: string;
rawTitle?: string;
description?: string;
image?: { src: string; alt: string };
canonicalURL?: URL | null;
pageType?: "website" | "article";
};
const twitterHandle = "edusegdotcomdotbr";
const {
rawTitle,
description = siteInfo.description,
image = siteInfo.image,
canonicalURL = new URL(Astro.request.url, Astro.site),
pageType = "website",
} = Astro.props;
const title =
rawTitle ??
[Astro.props.title, siteInfo.name].filter(Boolean).join(" &mdash; ");
const resolvedImage = {
src: new URL(image.src, Astro.site).toString(),
alt: image.alt,
};
---
{/* High Priority Global Metadata */}
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title set:html={smartypants(title, 1)} />
<meta name="generator" content={Astro.generator} />
<link rel="sitemap" href="/sitemap-index.xml" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<SEO
name={siteInfo.name}
title={title}
description={description}
image={resolvedImage}
twitter={{ handle: twitterHandle }}
og={{ type: pageType }}
canonicalURL={canonicalURL}
/>

View File

@@ -28,7 +28,7 @@ const { data } = course;
const { Content } = await render(course);
---
<Layout>
<Layout title={data.title}>
<Fragment slot="head">
<title>{data.title} &mdash; EDUSEG&reg;</title>
</Fragment>

View File

@@ -71,6 +71,7 @@ const currency = new Intl.NumberFormat("pt-BR", {
const r = await checkout(item);
const json = await r.json();
target.disabled = true;
location.href = `https://checkout.betaeducacao.com.br/${json.id}`;
});
</script>