feat: integrate with contentfull

main
Jalil Arfaoui 2024-01-02 00:34:17 +01:00
parent 3c94fcf793
commit f067f77918
15 changed files with 6866 additions and 5631 deletions

6
.idea/encodings.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="file://$PROJECT_DIR$/src/pages/test-evenement.astro" charset="UTF-8" />
</component>
</project>

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
yarnPath: .yarn/releases/yarn-3.6.3.cjs

View File

@ -1,4 +1,10 @@
import { defineConfig } from 'astro/config'; import { defineConfig } from 'astro/config';
import node from "@astrojs/node";
// https://astro.build/config // https://astro.build/config
export default defineConfig({}); export default defineConfig({
output: "hybrid",
adapter: node({
mode: "standalone"
})
});

6479
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -11,7 +11,12 @@
"astro": "astro" "astro": "astro"
}, },
"dependencies": { "dependencies": {
"astro": "^2.0.2" "@astrojs/node": "^7.0.3",
"@contentful/rich-text-html-renderer": "^16.3.0",
"astro": "^4.0.8",
"contentful": "^10.6.15"
}, },
"packageManager": "yarn@3.6.3" "devDependencies": {
"@types/node": "^20.10.6"
}
} }

View File

@ -7,10 +7,12 @@ export interface Props {
} }
const { href, target, title, span} = Astro.props; const { href, target, title, span} = Astro.props;
const Wrapper = href ? 'a' : 'div'
--- ---
<li class:list={["link-card",{ span }]} > <li class:list={["link-card",{ span }]} >
<a href={href} target={target}> <Wrapper class="content" href={href} target={target}>
<h2> <h2>
{title} {title}
<span>&rarr;</span> <span>&rarr;</span>
@ -18,7 +20,7 @@ const { href, target, title, span} = Astro.props;
<p> <p>
<slot /> <slot />
</p> </p>
</a> </Wrapper>
</li> </li>
<style> <style>
.link-card { .link-card {
@ -26,18 +28,16 @@ const { href, target, title, span} = Astro.props;
display: flex; display: flex;
padding: 0.15rem; padding: 0.15rem;
background-color: white; background-color: white;
background-image: var(--accent-gradient);
background-size: 400%; background-size: 400%;
border-radius: 0.5rem; border-radius: 0.5rem;
background-position: 100%; box-shadow: 0.2em 0.2em 1em black;
transition: background-position 0.6s cubic-bezier(0.22, 1, 0.36, 1); max-width: 80ch;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1);
} }
.link-card.span { .link-card.span {
grid-column: 1/-1; grid-column: 1/-1;
} }
.link-card > a { .link-card > .content {
width: 100%; width: 100%;
text-decoration: none; text-decoration: none;
line-height: 1.4; line-height: 1.4;

View File

@ -0,0 +1,97 @@
---
import { documentToHtmlString } from "@contentful/rich-text-html-renderer";
const { evenement } = Astro.props;
---
<section class="evenement">
<a href=`/evenements/${evenement.slug}`>
<img src={evenement.affiche.fields.file.url} alt=`Affiche de ${evenement.nom}` />
</a>
<div class="infos">
<div class="nom">
<a href=`/evenements/${evenement.slug}`>
{evenement.nom}
</a>
</div>
<div class="date">
le {new Date(evenement.date).toLocaleDateString()}
</div>
<div class="lieu">
à {evenement.lieu}
</div>
<div class="description">
<Fragment set:html={documentToHtmlString(evenement.description)} />
</div>
<a class="suite" href=`/evenements/${evenement.slug}`>
Suite…
</a>
</div>
</section>
<style>
.evenement {
display: flex;
flex-direction: row;
column-gap: 1em;
color:rgb(68,68,68);
font-size: 1.25rem;
line-height: 1.4;
.nom {
font-weight: 700;
font-size:20px;
a {
text-decoration: none;
color: rgb(17,17,17);
}
}
& > a {
width: 30%;
flex-grow: 0;
}
img {
width: 100%;
}
.infos {
flex-grow: 0;
width: 70%;
position: relative;
.description {
font-size: 0.8em;
max-height: 10em;
overflow: hidden;
}
.suite {
font-size: 0.7em;
position: absolute;
bottom: 1em;
right: 1em;
}
}
}
@media (max-width: 48em) {
.evenement {
flex-direction: column;
& > a {
width: 100%;
}
.infos {
.description, .date, .lieu, .suite {
display: none
}
}
}
}
</style>

6
src/env.d.ts vendored
View File

@ -1 +1,7 @@
/// <reference types="astro/client" /> /// <reference types="astro/client" />
interface ImportMetaEnv {
readonly CONTENTFUL_SPACE_ID: string;
readonly CONTENTFUL_DELIVERY_TOKEN: string;
readonly CONTENTFUL_PREVIEW_TOKEN: string;
readonly WEBHOOK_TOKEN: string;
}

View File

@ -7,34 +7,53 @@ const { title } = Astro.props;
--- ---
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="fr">
<head> <head>
<meta charset="UTF-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width" /> <meta name="viewport" content="width=device-width" />
<link rel="icon" type="image/svg+xml" href="./favicon.jpg" /> <link rel="icon" type="image/svg+xml" href="/favicon.jpg" />
<meta name="generator" content={Astro.generator} /> <meta name="generator" content={Astro.generator} />
<title>{title}</title> <title>{title}</title>
</head> </head>
<body style=" <body style="
background: url('/logo_les-particules_orange.jpg') top no-repeat; background: url('/logo_les-particules_orange.jpg') top no-repeat;
height:fit-content; min-height: 100% "> height:fit-content; min-height: 100% ">
<slot /> <main>
<h1>
<a href="/">
<img alt="Logo" src="/logo_les-particules_noir.jpg" style="width:80px; vertical-align: middle" />
Les particules
</a>
</h1>
<slot />
</main>
</body> </body>
</html> </html>
<style is:global> <style is:global>
:root { :root {
--accent: 124, 58, 237; --accent: orange;
--accent-gradient: linear-gradient(45deg, rgb(var(--accent)), #da62c4 30%, white 60%); --accent-gradient: linear-gradient(45deg, var(--accent), red 30%, white 60%);
} }
html { html {
font-family: system-ui, sans-serif; font-family: system-ui, sans-serif;
background-color: #F6F6F6; background-color: #F6F6F6;
} }
html, body { html, body {
height: 100%; height: 100%;
padding:0; padding:0;
margin:0; margin:0;
} }
h1 a {
text-decoration: none;
color:black;
}
main {
margin: auto;
padding: 1.5rem;
max-width: 80ch;
}
code { code {
font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono,

40
src/lib/contentful.ts Normal file
View File

@ -0,0 +1,40 @@
import contentful, {type Entry, type EntryFieldTypes} from "contentful";
export const contentfulClient = contentful.createClient({
space: import.meta.env.CONTENTFUL_SPACE_ID,
accessToken: import.meta.env.CONTENTFUL_DELIVERY_TOKEN,
host: "cdn.contentful.com",
});
export interface ContentFulEvenement {
contentTypeId: "evenement",
fields: {
nom: EntryFieldTypes.Text
slug: EntryFieldTypes.Text,
description: EntryFieldTypes.RichText,
date: EntryFieldTypes.Date,
lieu: EntryFieldTypes.Location,
affiche: EntryFieldTypes.AssetLink,
}
}
export const evenementFromContentfull = ({ fields: { nom, slug, description, date, affiche, lieu } }: Entry<ContentFulEvenement>) => ({
nom, slug, description, date, affiche, lieu
})
export const sortByDate = (evenements: Entry<ContentFulEvenement>[]): Entry<ContentFulEvenement>[] => {
return evenements.sort((a, b) => {
if (!a.fields.date) return 1;
if (!b.fields.date) return -1;
return new Date(a.fields.date as string).getTime() - new Date(b.fields.date as string).getTime();
});
};
export const fetchEvenements = async () => {
const entries = await contentfulClient.getEntries<ContentFulEvenement>({
content_type: "evenement",
});
return sortByDate(entries.items).map(evenementFromContentfull)
}

60
src/pages/api/webhook.ts Normal file
View File

@ -0,0 +1,60 @@
import { exec } from 'child_process';
let isBuildInProgress = false;
export async function POST({ request }: { request: Request }): Promise<Response> {
const data = await request.json();
if (!data.token || data.token !== import.meta.env.WEBHOOK_TOKEN) {
return new Response(
JSON.stringify({ status: "unauthorized" }),
{ status: 401, headers: { "Content-Type": "application/json" } }
)
}
try {
if (isBuildInProgress) {
return new Response(
JSON.stringify({status: 'error', message: 'Build already in progress'}),
{
status: 409,
headers: {
'Content-Type': 'application/json',
},
}
);
}
isBuildInProgress = true;
exec('npm run build', (error, stdout, stderr) => {
if (error) {
console.error(`Error executing build: ${error}`);
}
if (stdout) {
console.log(`Build stdout: ${stdout}`);
}
if (stderr) {
console.error(`Build stderr: ${stderr}`);
}
isBuildInProgress = false;
});
return new Response(JSON.stringify({status: 'success'}), {
status: 200,
headers: {
'Content-Type': 'application/json',
},
});
} catch (error) {
console.error("Error in webhook:", error);
// Return a error response
return new Response(JSON.stringify({status: 'error', message: error?.toString()}), {
status: 500,
headers: {
'Content-Type': 'application/json',
},
});
}
}

View File

@ -0,0 +1,79 @@
---
import { Document } from '@contentful/rich-text-types';
import { documentToHtmlString } from "@contentful/rich-text-html-renderer";
import { fetchEvenements} from "../../lib/contentful";
import Layout from "../../layouts/Layout.astro";
import Card from "../../components/Card.astro";
export async function getStaticPaths() {
const evenement = await fetchEvenements()
return evenement.map((evenement) => ({
params: {slug: evenement.slug},
props: {
nom: evenement.nom,
description: evenement.description ? documentToHtmlString(evenement.description as Document) : "",
date: evenement.date ? new Date(evenement.date as string).toLocaleDateString() : "",
lieu: evenement.lieu,
affiche: evenement.affiche
},
}));
}
const { nom, description, date, lieu, affiche } = Astro.props;
---
<Layout>
<h1>{nom}</h1>
<div class="content">
<img alt=`Affiche de ${nom}` src={affiche.fields.file.url} />
<Card title={date}>
<div>
à <a target="_blank" href="https://cartessurtable.wixsite.com/cartes-sur-table">{lieu}</a>
</div>
<article set:html={description} />
</Card>
</div>
</Layout>
<style is:global>
main {
margin: auto;
padding: 1.5rem;
max-width: none;
}
main:after {
display: block;
content: '';
clear: both;
}
</style>
<style>
img {
float:left;
width: 30%;
margin: 1em;
}
article {
font-size: 1.2em;
}
@media (max-width: 48em) {
.content {
display: flex;
flex-direction: column-reverse;
img {
float:none;
width: 100%;
border: black solid 0.1em;
border-radius: 1em;
margin: 1em auto;
box-shadow: 0.2em 0.2em 1em black;
}
}
}
</style>

View File

@ -1,56 +1,50 @@
--- ---
import Layout from '../layouts/Layout.astro'; import Layout from '../layouts/Layout.astro';
import Card from '../components/Card.astro'; import Card from '../components/Card.astro';
import { fetchEvenements } from "../lib/contentful";
import Evenement from "../components/Evenement.astro";
const evenements = await fetchEvenements()
--- ---
<Layout title="Les Particules"> <Layout title="Les Particules">
<main> <p class="instructions">
<h1> La troupe d'improvisation théâtrale chatouilleuse, curieuse, créative et autogérée<br/>
<img alt="Logo" src="/logo_les-particules_noir.jpg" style="width:80px; vertical-align: middle" /> 📌 Albi, Occitanie
Les particules </p>
</h1> <div class="dates">
<p class="instructions"> <div class="prochainement">Retrouvez-nous prochainement&nbsp;<span>&rarr;</span></div>
La troupe d'improvisation théâtrale chatouilleuse, curieuse, créative et autogérée<br/> <div class="evenements">
📌 Albi, Occitanie {evenements.map(evenement => (
</p> <Evenement evenement={evenement} />
<div class="dates"> ))}
<span class="title">Retrouvez-nous prochainement <span>&rarr;</span></span>
<ul>
<li>28 octobre 2023 à 19h30 au <a target="_blank" href="https://cartessurtable.wixsite.com/cartes-sur-table">Cartes sur Table</a> à Gaillac</li>
<li>12 novembre 2023 à 18h30 au <a target="_blank" href="https://lescenophage.org/">Scénophage</a> à Gaillac</li>
</ul>
</div> </div>
<ul role="list" class="link-card-grid"> </div>
<Card <ul role="list" class="link-card-grid">
title="Les Particules Fines" <Card
href="/fines" title="Les Particules Fines"
span={true} href="/fines"
> >
Cours dimpro adultes débutant·e·s<br/> Cours dimpro adultes débutant·e·s<br/>
<strong>Cest complet !</strong> <strong>Cest complet !</strong>
</Card> </Card>
<Card <Card
href="./inscription-newsletter" href="./inscription-newsletter"
title="Newsletter" title="Newsletter"
> >
Tous nos évènements directement dans votre boite aux lettres ! Tous nos évènements directement dans votre boite aux lettres !
</Card> </Card>
<Card <Card
href="/contact" href="/contact"
title="Contact" title="Contact"
> >
Écrivez-nous ou suivez-nous sur les réseaux Écrivez-nous ou suivez-nous sur les réseaux
</Card> </Card>
</ul> </ul>
</main>
</Layout> </Layout>
<style> <style>
main {
margin: auto;
padding: 1.5rem;
max-width: 60ch;
}
h1 { h1 {
font-size: 3rem; font-size: 3rem;
font-weight: 800; font-weight: 800;
@ -61,7 +55,7 @@ import Card from '../components/Card.astro';
-webkit-background-clip: text; -webkit-background-clip: text;
-webkit-text-fill-color: transparent; -webkit-text-fill-color: transparent;
background-size: 400%; background-size: 400%;
background-position: 0%; background-position: 0;
} }
.instructions, .dates { .instructions, .dates {
line-height: 1.6; line-height: 1.6;
@ -80,20 +74,20 @@ import Card from '../components/Card.astro';
gap: 1rem; gap: 1rem;
padding: 0; padding: 0;
} }
.dates { .evenements {
color:#111; display: flex;
font-size: 1.25rem; flex-direction: column;
line-height: 1.4;
} }
.dates .title { .dates {
font-weight: bold; box-shadow: 0.2em 0.2em 1em black;
opacity: 0.8;
.prochainement {
font-size: 1.2em;
font-weight: 700;
margin-bottom: 1em;
}
} }
.dates ul { .dates ul {
list-style: none; list-style: none;
} }
.dates li {
color:#444;
}
</style> </style>

4681
yarn.lock

File diff suppressed because it is too large Load Diff