feat: integrate with contentfull
parent
3c94fcf793
commit
f067f77918
|
@ -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
|
@ -1 +0,0 @@
|
||||||
yarnPath: .yarn/releases/yarn-3.6.3.cjs
|
|
|
@ -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"
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>→</span>
|
<span>→</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;
|
||||||
|
|
|
@ -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>
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -7,24 +7,32 @@ 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% ">
|
||||||
|
<main>
|
||||||
|
<h1>
|
||||||
|
<a href="/">
|
||||||
|
<img alt="Logo" src="/logo_les-particules_noir.jpg" style="width:80px; vertical-align: middle" />
|
||||||
|
Les particules
|
||||||
|
</a>
|
||||||
|
</h1>
|
||||||
<slot />
|
<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;
|
||||||
|
@ -36,6 +44,17 @@ const { title } = Astro.props;
|
||||||
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,
|
||||||
Bitstream Vera Sans Mono, Courier New, monospace;
|
Bitstream Vera Sans Mono, Courier New, monospace;
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
|
@ -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',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -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>
|
|
@ -1,30 +1,30 @@
|
||||||
---
|
---
|
||||||
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>
|
|
||||||
<h1>
|
|
||||||
<img alt="Logo" src="/logo_les-particules_noir.jpg" style="width:80px; vertical-align: middle" />
|
|
||||||
Les particules
|
|
||||||
</h1>
|
|
||||||
<p class="instructions">
|
<p class="instructions">
|
||||||
La troupe d'improvisation théâtrale chatouilleuse, curieuse, créative et autogérée<br/>
|
La troupe d'improvisation théâtrale chatouilleuse, curieuse, créative et autogérée<br/>
|
||||||
📌 Albi, Occitanie
|
📌 Albi, Occitanie
|
||||||
</p>
|
</p>
|
||||||
<div class="dates">
|
<div class="dates">
|
||||||
<span class="title">Retrouvez-nous prochainement <span>→</span></span>
|
<div class="prochainement">Retrouvez-nous prochainement <span>→</span></div>
|
||||||
<ul>
|
<div class="evenements">
|
||||||
<li>28 octobre 2023 à 19h30 au <a target="_blank" href="https://cartessurtable.wixsite.com/cartes-sur-table">Cartes sur Table</a> à Gaillac</li>
|
{evenements.map(evenement => (
|
||||||
<li>12 novembre 2023 à 18h30 au <a target="_blank" href="https://lescenophage.org/">Scénophage</a> à Gaillac</li>
|
<Evenement evenement={evenement} />
|
||||||
</ul>
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ul role="list" class="link-card-grid">
|
<ul role="list" class="link-card-grid">
|
||||||
<Card
|
<Card
|
||||||
title="Les Particules Fines"
|
title="Les Particules Fines"
|
||||||
href="/fines"
|
href="/fines"
|
||||||
span={true}
|
|
||||||
>
|
>
|
||||||
Cours d’impro adultes débutant·e·s<br/>
|
Cours d’impro adultes débutant·e·s<br/>
|
||||||
<strong>C’est complet !</strong>
|
<strong>C’est complet !</strong>
|
||||||
|
@ -42,15 +42,9 @@ import Card from '../components/Card.astro';
|
||||||
É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 {
|
||||||
|
box-shadow: 0.2em 0.2em 1em black;
|
||||||
|
|
||||||
|
.prochainement {
|
||||||
|
font-size: 1.2em;
|
||||||
|
font-weight: 700;
|
||||||
|
margin-bottom: 1em;
|
||||||
}
|
}
|
||||||
.dates .title {
|
|
||||||
font-weight: bold;
|
|
||||||
opacity: 0.8;
|
|
||||||
}
|
}
|
||||||
.dates ul {
|
.dates ul {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
}
|
}
|
||||||
.dates li {
|
|
||||||
color:#444;
|
|
||||||
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
Loading…
Reference in New Issue