Import repositories from gitlab
This commit is contained in:
24
next/app/contact/page.js
Normal file
24
next/app/contact/page.js
Normal file
@@ -0,0 +1,24 @@
|
||||
import getData from "@/api";
|
||||
import { BlocksRenderer } from "@strapi/blocks-react-renderer";
|
||||
import styles from "./style.module.scss";
|
||||
import Email from "@/components/email";
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
export async function generateMetadata() {
|
||||
return {
|
||||
metadataBase: `${process.env.NEXT_PUBLIC_ORIGIN}`,
|
||||
title: "Contactez nous !",
|
||||
};
|
||||
}
|
||||
|
||||
export default async function Contact() {
|
||||
const site = await getData("site", {});
|
||||
|
||||
const content = site.data?.attributes.contact_text;
|
||||
return (
|
||||
<main className={styles.main}>
|
||||
<article>{content ? <BlocksRenderer content={content} /> : null}</article>
|
||||
{site.data?.attributes.contact_mail ? <Email></Email> : null}
|
||||
</main>
|
||||
);
|
||||
}
|
||||
37
next/app/contact/style.module.scss
Normal file
37
next/app/contact/style.module.scss
Normal file
@@ -0,0 +1,37 @@
|
||||
.main {
|
||||
display: flex;
|
||||
flex: 1 1 0%;
|
||||
flex-flow: column;
|
||||
align-items: center;
|
||||
|
||||
article {
|
||||
width: 60rem;
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
max-width: 90vw;
|
||||
align-self: center;
|
||||
|
||||
h2, h3, h4 {
|
||||
font-family: var(--font-details);
|
||||
}
|
||||
|
||||
h2 {
|
||||
align-self: center;
|
||||
font-family: var(--font-details);
|
||||
font-size: 2rem;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 641px) {
|
||||
.main {
|
||||
article {
|
||||
text-align: justify;
|
||||
}
|
||||
}
|
||||
}
|
||||
216
next/app/editions/[editionId]/page.js
Normal file
216
next/app/editions/[editionId]/page.js
Normal file
@@ -0,0 +1,216 @@
|
||||
import getData from "@/api";
|
||||
import styles from "./style.module.scss";
|
||||
import EditionElement from "@/components/editionElement";
|
||||
import EditionGallery from "@/components/editionGallery";
|
||||
import PressBlock from "@/components/pressBlock";
|
||||
import Empty from "@/components/empty";
|
||||
import VideoBlock from "@/components/videoBlock";
|
||||
import Fanfare from "@/components/fanfare";
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
export async function generateMetadata({ params }) {
|
||||
const data = await getData(`editions/${params.editionId}`, {
|
||||
populate: {
|
||||
flyer: {
|
||||
fields: ["name", "alternativeText", "caption", "url"],
|
||||
},
|
||||
},
|
||||
filters: {
|
||||
$or: [{ id: { $eq: params.editionId } }],
|
||||
},
|
||||
});
|
||||
const activeEditionData = await getData("site", {
|
||||
populate: {
|
||||
edition: {
|
||||
fields: ["id"],
|
||||
},
|
||||
},
|
||||
fields: ["author"],
|
||||
});
|
||||
const activeEdition = activeEditionData?.data?.attributes.edition.data.id;
|
||||
const edition = data.data?.attributes.publishedAt ? data : null;
|
||||
const flyer = edition?.data?.attributes.flyer.data.attributes;
|
||||
|
||||
return edition
|
||||
? {
|
||||
metadataBase: `${process.env.NEXT_PUBLIC_ORIGIN}`,
|
||||
title: edition.title,
|
||||
description: edition.description,
|
||||
alternates: {
|
||||
canonical:
|
||||
data.data.id !== activeEdition
|
||||
? `${process.env.NEXT_PUBLIC_ORIGIN}/editions/${params.editionId}`
|
||||
: `${process.env.NEXT_PUBLIC_ORIGIN}`,
|
||||
},
|
||||
openGraph: {
|
||||
title: edition.title,
|
||||
url:
|
||||
data.data.id !== activeEdition
|
||||
? `${process.env.NEXT_PUBLIC_ORIGIN}/editions/${params.editionId}`
|
||||
: `${process.env.NEXT_PUBLIC_ORIGIN}`,
|
||||
description: edition.description,
|
||||
images: {
|
||||
url: `${process.env.NEXT_PUBLIC_IMG_URI}${flyer.url}`,
|
||||
width: flyer.width,
|
||||
height: flyer.height,
|
||||
},
|
||||
authors: [activeEditionData.data.attributes.author],
|
||||
type: "website",
|
||||
locale: "fr_FR",
|
||||
siteName: "Le Fefan - Festival de Fanfares",
|
||||
},
|
||||
}
|
||||
: {};
|
||||
}
|
||||
|
||||
export default async function Edition({ params }) {
|
||||
const data = await getData(`editions/${params.editionId}`, {
|
||||
populate: {
|
||||
flyer: {
|
||||
fields: ["name", "alternativeText", "caption", "url"],
|
||||
},
|
||||
gallery: {
|
||||
fields: ["name", "alternativeText", "caption", "url"],
|
||||
},
|
||||
programs: {
|
||||
fields: ["map_uri", "introduction", "description", "title", "type"],
|
||||
populate: {
|
||||
bands: {
|
||||
fields: ["name", "location"],
|
||||
},
|
||||
},
|
||||
},
|
||||
statistics: {
|
||||
fields: ["name", "value", "publishedAt"],
|
||||
},
|
||||
social_links: {
|
||||
fields: ["uri", "type"],
|
||||
},
|
||||
articles: {
|
||||
fields: ["title", "link", "excerpt", "publishedAt"],
|
||||
},
|
||||
fields: ["movie"],
|
||||
},
|
||||
filters: {
|
||||
$or: [{ id: { $eq: params.editionId } }],
|
||||
},
|
||||
});
|
||||
const edition = data.data?.attributes.publishedAt ? data : null;
|
||||
const flyer = edition?.data?.attributes.flyer.data.attributes;
|
||||
const gallery = edition?.data?.attributes.gallery.data;
|
||||
const statistics = edition?.data?.attributes.statistics.data;
|
||||
const allArticles = edition?.data?.attributes.articles.data;
|
||||
const articles = allArticles
|
||||
? allArticles.filter((el) => el.attributes.publishedAt != null)
|
||||
: [];
|
||||
const movie = edition.data.attributes.movie
|
||||
? edition.data.attributes.movie
|
||||
: null;
|
||||
const programs = edition?.data?.attributes?.programs?.data
|
||||
? edition.data.attributes.programs.data
|
||||
: [];
|
||||
const program =
|
||||
programs.filter((el) => el.attributes.type === "city-wide")[0] ?? null;
|
||||
const bands = program ? program?.attributes?.bands.data : [];
|
||||
|
||||
return (
|
||||
<main className={styles.main}>
|
||||
{edition ? (
|
||||
<>
|
||||
<h2>{edition.data.attributes.title}</h2>
|
||||
<h3>{edition.data.attributes.subtitle}</h3>
|
||||
<EditionElement
|
||||
flyerImg={`${process.env.NEXT_PUBLIC_IMG_URI}${flyer.url}`}
|
||||
flyerAlt={flyer.alternativeText}
|
||||
blocks={statistics.map(({ id, attributes }) => ({
|
||||
id,
|
||||
type: "stat",
|
||||
title: attributes.name,
|
||||
value: attributes.value,
|
||||
}))}
|
||||
/>
|
||||
{movie && bands ? (
|
||||
<section className={styles.smallProgram}>
|
||||
{bands.length > 0 ? (
|
||||
<article className={styles.featuring}>
|
||||
<h4>Les fanfares</h4>
|
||||
{bands.map(({ id, attributes: attr }) => {
|
||||
return (
|
||||
<Fanfare
|
||||
key={id}
|
||||
location={attr.location}
|
||||
name={attr.name}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</article>
|
||||
) : null}
|
||||
{movie ? (
|
||||
<>
|
||||
<VideoBlock
|
||||
src={movie}
|
||||
title={`Aftermovie du festival ${
|
||||
edition.data.attributes?.title ?? ""
|
||||
}`}
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
</section>
|
||||
) : null}
|
||||
{articles.length > 0 ? (
|
||||
<>
|
||||
<h4>Ils ont parlé de nous !</h4>
|
||||
{articles.map(({ id, attributes }, index) => {
|
||||
const offset = index + (articles.length - gallery.length) / 2;
|
||||
return index < gallery.length ? (
|
||||
<PressBlock
|
||||
key={id}
|
||||
left={{
|
||||
type: "article",
|
||||
title: attributes.title,
|
||||
content: attributes.excerpt,
|
||||
link: attributes.link,
|
||||
}}
|
||||
right={{
|
||||
type: "image",
|
||||
alt: gallery[index].attributes.alternativeText,
|
||||
src: `${process.env.NEXT_PUBLIC_IMG_URI}${gallery[index].attributes.url}`,
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<PressBlock
|
||||
key={id}
|
||||
left={{
|
||||
type: "article",
|
||||
title: attributes.title,
|
||||
content: attributes.excerpt,
|
||||
link: attributes.link,
|
||||
}}
|
||||
right={
|
||||
articles[offset]
|
||||
? {
|
||||
type: "article",
|
||||
title: articles[offset].title,
|
||||
content: articles[offset].excerpt,
|
||||
link: articles[offset].link,
|
||||
}
|
||||
: { type: null }
|
||||
}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
) : null}
|
||||
{articles.length <= gallery.length ? (
|
||||
<>
|
||||
<h4>Les photos du Fefan</h4>
|
||||
<EditionGallery images={gallery.slice(articles.length)} />
|
||||
</>
|
||||
) : null}
|
||||
</>
|
||||
) : (
|
||||
<Empty message="Il n'y a aucune information pour cette édition à afficher." />
|
||||
)}
|
||||
</main>
|
||||
);
|
||||
}
|
||||
64
next/app/editions/[editionId]/style.module.scss
Normal file
64
next/app/editions/[editionId]/style.module.scss
Normal file
@@ -0,0 +1,64 @@
|
||||
.main {
|
||||
flex: 1 1 0%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-flow: column;
|
||||
padding-top: 1rem;
|
||||
|
||||
h2 {
|
||||
font-family: var(--font-details);
|
||||
font-size: 2rem;
|
||||
font-weight: 400;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 0.75rem;
|
||||
color: var(--fg-1);
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
margin: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-family: var(--font-details);
|
||||
font-size: 1.8rem;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.smallProgram {
|
||||
margin-top: 2rem;
|
||||
display: flex;
|
||||
|
||||
.featuring {
|
||||
|
||||
h4 {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
display: grid;
|
||||
align-self: center;
|
||||
grid-template-columns: repeat(2, calc(80rem / 6));
|
||||
align-self: stretch;
|
||||
padding: 0 0.5rem;
|
||||
justify-self: stretch;
|
||||
align-items: stretch;
|
||||
justify-items: stretch;
|
||||
align-content: start;
|
||||
grid-auto-rows: max-content;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 60rem) {
|
||||
.main {
|
||||
.smallProgram {
|
||||
flex-direction: column;
|
||||
|
||||
.featuring {
|
||||
grid-template-columns: 1fr;
|
||||
align-self: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
132
next/app/editions/page.js
Normal file
132
next/app/editions/page.js
Normal file
@@ -0,0 +1,132 @@
|
||||
import getData from "@/api";
|
||||
import styles from "./style.module.scss";
|
||||
import EditionElement from "@/components/editionElement";
|
||||
import Empty from "@/components/empty";
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
export async function generateMetadata() {
|
||||
const site = await getData("site", {
|
||||
fields: ["description", "author"],
|
||||
});
|
||||
|
||||
const data = await getData("editions", {
|
||||
populate: {
|
||||
gallery: {
|
||||
fields: ["name", "url", "alternativeText"],
|
||||
},
|
||||
statistics: {
|
||||
fields: ["name", "value"],
|
||||
},
|
||||
flyer: {
|
||||
fields: ["name", "url", "alternativeText", "width", "height"],
|
||||
},
|
||||
},
|
||||
});
|
||||
const editions = data.data;
|
||||
|
||||
return site.data
|
||||
? {
|
||||
metadataBase: `${process.env.NEXT_PUBLIC_ORIGIN}`,
|
||||
title: "Editions précédentes — Le Fefan",
|
||||
description: site.data.attributes.description,
|
||||
alternates: {
|
||||
canonical: "/editions",
|
||||
},
|
||||
openGraph: {
|
||||
title: "Editions précédentes — Le Fefan",
|
||||
url: `${process.env.NEXT_PUBLIC_ORIGIN}/prog/city-wide`,
|
||||
description: site.data.attributes.description,
|
||||
images: editions.map(({ attributes: attr }) => ({
|
||||
width: attr.flyer.data.attributes.width,
|
||||
height: attr.flyer.data.attributes.height,
|
||||
alt: attr.flyer.data.attributes.alternativeText,
|
||||
url: attr.flyer.data.attributes.url,
|
||||
})),
|
||||
authors: [site.data.attributes.author],
|
||||
type: "website",
|
||||
locale: "fr_FR",
|
||||
siteName: "Le Fefan - Festival de Fanfares",
|
||||
},
|
||||
}
|
||||
: {};
|
||||
}
|
||||
|
||||
export default async function Editions() {
|
||||
const site = await getData("site", {
|
||||
fields: ["id"],
|
||||
populate: {
|
||||
edition: {
|
||||
fields: ["id"],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const data = await getData("editions", {
|
||||
populate: {
|
||||
gallery: {
|
||||
fields: ["name", "url", "alternativeText"],
|
||||
},
|
||||
statistics: {
|
||||
fields: ["name", "value"],
|
||||
},
|
||||
flyer: {
|
||||
fields: ["name", "url", "alternativeText"],
|
||||
},
|
||||
fields: ["movie"],
|
||||
},
|
||||
});
|
||||
|
||||
const editions = (data.data ?? []).filter(
|
||||
(e) => e.id !== site.data?.attributes.edition.data?.id
|
||||
);
|
||||
|
||||
return (
|
||||
<main className={styles.main}>
|
||||
{editions ? (
|
||||
editions.map(({ id, attributes: attr }) => {
|
||||
const stats = attr.statistics.data.map(({ id, attributes }) => ({
|
||||
id,
|
||||
type: "stat",
|
||||
title: attributes.name,
|
||||
value: attributes.value,
|
||||
}));
|
||||
const images = attr.gallery.data.map(({ id, attributes }) => ({
|
||||
id,
|
||||
type: "image",
|
||||
title: attributes.alternativeText,
|
||||
value: `${process.env.NEXT_PUBLIC_IMG_URI}${attributes.url}`,
|
||||
}));
|
||||
const movie = attr.movie
|
||||
? [
|
||||
{
|
||||
id: "movie",
|
||||
type: "video",
|
||||
title: `Aftermovie ${attr.title}`,
|
||||
value: attr.movie,
|
||||
mode: "thumbnail",
|
||||
},
|
||||
]
|
||||
: null;
|
||||
|
||||
return (
|
||||
<EditionElement
|
||||
full
|
||||
key={id}
|
||||
href={`/editions/${id}`}
|
||||
title={attr.title}
|
||||
blocks={
|
||||
movie
|
||||
? stats.concat(movie).concat(images)
|
||||
: stats.concat(images)
|
||||
}
|
||||
flyerImg={`${process.env.NEXT_PUBLIC_IMG_URI}${attr.flyer.data.attributes.url}`}
|
||||
flyerAlt={attr.flyer.data.attributes.alternativeText}
|
||||
/>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<Empty message="Il n'y a aucune édition à afficher."></Empty>
|
||||
)}
|
||||
</main>
|
||||
);
|
||||
}
|
||||
12
next/app/editions/style.module.scss
Normal file
12
next/app/editions/style.module.scss
Normal file
@@ -0,0 +1,12 @@
|
||||
.main {
|
||||
flex: 1 1 0%;
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
overflow: auto;
|
||||
scroll-snap-type: y mandatory;
|
||||
|
||||
// .editionLink {
|
||||
// }
|
||||
}
|
||||
BIN
next/app/favicon.ico
Normal file
BIN
next/app/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 739 B |
44
next/app/globals.css
Normal file
44
next/app/globals.css
Normal file
@@ -0,0 +1,44 @@
|
||||
:root {
|
||||
--bg-0: #FFFFCC;
|
||||
--fg-0: #412D0A;
|
||||
--fg-0-translucid: rgba(65, 45, 10, 0.35);
|
||||
--fg-1: #957948;
|
||||
--fg-2: #CCC08F;
|
||||
--fg-3: #DDDDAA;
|
||||
--fefan-1: #1B519E;
|
||||
--fefan-2: #295DAB;
|
||||
--fefan-3: #32A1DA;
|
||||
--fefan-4: #B8529F;
|
||||
--transition-time: 0.2s;
|
||||
--font-body: 'Krub', sans-serif;
|
||||
--font-details: 'Shadows Into Light', sans-serif;
|
||||
--border-width: 1px;
|
||||
}
|
||||
|
||||
html, body {
|
||||
background: var(--bg-0);
|
||||
color: var(--fg-0);
|
||||
height: 100vh;
|
||||
margin: 0;
|
||||
font-family: var(--font-body);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
a:not(.not-a-link) {
|
||||
text-decoration-color: transparent;
|
||||
color: transparent;
|
||||
background: linear-gradient(175deg, var(--fefan-1) 0%, var(--fefan-1) 45%, var(--fefan-2) 55%, var(--fefan-3) 70%, var(--fefan-4) 85%);
|
||||
background-clip: text;
|
||||
background-size: 250% 100%;
|
||||
transition: all ease var(--transition-time);
|
||||
|
||||
&:hover {
|
||||
text-decoration-color: var(--fefan-1);
|
||||
background-position-x: 100%;
|
||||
}
|
||||
|
||||
&:visited {
|
||||
color: unset;
|
||||
}
|
||||
}
|
||||
28
next/app/layout.js
Normal file
28
next/app/layout.js
Normal file
@@ -0,0 +1,28 @@
|
||||
import './globals.css'
|
||||
import Header from '@/components/header'
|
||||
import Footer from '@/components/footer'
|
||||
|
||||
export const dynqmic = 'force-dynamic'
|
||||
|
||||
export const metadata = {
|
||||
generator: 'Next.js',
|
||||
applicationName: 'Le Fefan',
|
||||
keywords: ['Fefan', 'Festival', 'Fanfare', 'Cuivres', 'Villeurbanne', 'Evenement', 'Musique'],
|
||||
authors: [{ name: 'Girasol' }],
|
||||
creator: 'Girasol',
|
||||
}
|
||||
|
||||
export default function RootLayout({ children }) {
|
||||
return (
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Krub:wght@500;700&family=Shadows+Into+Light&display=swap" />
|
||||
</head>
|
||||
<body>
|
||||
<Header />
|
||||
{children}
|
||||
<Footer />
|
||||
</body>
|
||||
</html>
|
||||
)
|
||||
}
|
||||
22
next/app/legal/page.js
Normal file
22
next/app/legal/page.js
Normal file
@@ -0,0 +1,22 @@
|
||||
import getData from "@/api";
|
||||
import { BlocksRenderer } from "@strapi/blocks-react-renderer";
|
||||
import styles from "./style.module.scss";
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
export async function generateMetadata() {
|
||||
return {
|
||||
metadataBase: `${process.env.NEXT_PUBLIC_ORIGIN}`,
|
||||
title: "Mentions légales",
|
||||
};
|
||||
}
|
||||
|
||||
export default async function Legal() {
|
||||
const site = await getData("site", {});
|
||||
const content = site.data?.attributes.legal;
|
||||
|
||||
return (
|
||||
<main className={styles.main}>
|
||||
<article>{content ? <BlocksRenderer content={content} /> : null}</article>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
29
next/app/legal/style.module.scss
Normal file
29
next/app/legal/style.module.scss
Normal file
@@ -0,0 +1,29 @@
|
||||
.main {
|
||||
display: flex;
|
||||
flex: 1 1 0%;
|
||||
flex-flow: column;
|
||||
align-items: center;
|
||||
|
||||
article {
|
||||
width: 60rem;
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
max-width: 90vw;
|
||||
align-self: center;
|
||||
|
||||
h2, h3, h4 {
|
||||
font-family: var(--font-details);
|
||||
}
|
||||
|
||||
h2 {
|
||||
align-self: center;
|
||||
font-family: var(--font-details);
|
||||
font-size: 2rem;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
114
next/app/page.js
Normal file
114
next/app/page.js
Normal file
@@ -0,0 +1,114 @@
|
||||
import getData from "@/api";
|
||||
import styles from "./page.module.scss";
|
||||
import { BlocksRenderer } from "@strapi/blocks-react-renderer";
|
||||
import Button from "@/components/button";
|
||||
import ImageBlock from "@/components/imageBlock";
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
export async function generateMetadata() {
|
||||
const site = await getData("site", {
|
||||
populate: {
|
||||
edition: {
|
||||
populate: {
|
||||
flyer: {
|
||||
fields: [
|
||||
"name",
|
||||
"url",
|
||||
"alternativeText",
|
||||
"caption",
|
||||
"width",
|
||||
"height",
|
||||
],
|
||||
},
|
||||
programs: true,
|
||||
},
|
||||
fields: ["id", "introduction", "title", "subtitle", "description"],
|
||||
},
|
||||
},
|
||||
fields: ["author"],
|
||||
});
|
||||
const current_edition = site.data?.attributes.edition.data?.attributes;
|
||||
const flyer =
|
||||
site.data?.attributes.edition.data?.attributes.flyer.data.attributes;
|
||||
|
||||
return current_edition
|
||||
? {
|
||||
metadataBase: `${process.env.NEXT_PUBLIC_ORIGIN}`,
|
||||
title: current_edition.title,
|
||||
description: current_edition.description,
|
||||
alternates: {
|
||||
canonical: "/",
|
||||
},
|
||||
openGraph: {
|
||||
title: current_edition.title,
|
||||
url: `${process.env.NEXT_PUBLIC_ORIGIN}`,
|
||||
description: current_edition.description,
|
||||
images: {
|
||||
url: `${process.env.NEXT_PUBLIC_IMG_URI}${flyer.url}`,
|
||||
width: flyer.width,
|
||||
height: flyer.height,
|
||||
},
|
||||
authors: [site.data.attributes.author],
|
||||
type: "website",
|
||||
locale: "fr_FR",
|
||||
siteName: "Le Fefan - Festival de Fanfares",
|
||||
},
|
||||
}
|
||||
: {};
|
||||
}
|
||||
|
||||
export default async function Home() {
|
||||
const site = await getData("site", {
|
||||
populate: {
|
||||
edition: {
|
||||
populate: {
|
||||
flyer: {
|
||||
fields: ["name", "url", "alternativeText", "caption"],
|
||||
},
|
||||
programs: true,
|
||||
},
|
||||
fields: ["id", "introduction", "title", "subtitle"],
|
||||
},
|
||||
},
|
||||
});
|
||||
const current_edition = site.data?.attributes.edition.data?.attributes;
|
||||
const flyer =
|
||||
site.data?.attributes.edition.data?.attributes.flyer.data.attributes;
|
||||
const introduction = current_edition?.introduction;
|
||||
const programs = current_edition?.programs.data;
|
||||
|
||||
return (
|
||||
<main className={styles.main}>
|
||||
{current_edition ? (
|
||||
<section className={styles.home}>
|
||||
{flyer ? (
|
||||
<ImageBlock
|
||||
className={styles.flyer}
|
||||
alt={flyer.alternativeText}
|
||||
src={`${process.env.NEXT_PUBLIC_IMG_URI}${flyer.url}`}
|
||||
/>
|
||||
) : null}
|
||||
<article>
|
||||
<h2>{current_edition.title}</h2>
|
||||
<h3>{current_edition.subtitle}</h3>
|
||||
{introduction ? <BlocksRenderer content={introduction} /> : null}
|
||||
<nav>
|
||||
{programs.filter((e) => e.attributes.type === "city-wide")
|
||||
.length > 0 ? (
|
||||
<Button as="a" type="primary" href="/prog/city-wide">
|
||||
Voir la programmation
|
||||
</Button>
|
||||
) : null}
|
||||
{programs.filter((e) => e.attributes.type === "village").length >
|
||||
0 ? (
|
||||
<Button as="a" type="secondary" href="/prog/village">
|
||||
Le Village
|
||||
</Button>
|
||||
) : null}
|
||||
</nav>
|
||||
</article>
|
||||
</section>
|
||||
) : null}
|
||||
</main>
|
||||
);
|
||||
}
|
||||
84
next/app/page.module.scss
Normal file
84
next/app/page.module.scss
Normal file
@@ -0,0 +1,84 @@
|
||||
.main {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
|
||||
.home {
|
||||
margin-top: 2rem;
|
||||
display: flex;
|
||||
width: 60rem;
|
||||
max-width: 90vw;
|
||||
align-items: flex-start;
|
||||
align-self: center;
|
||||
|
||||
article {
|
||||
padding-left: 1rem;
|
||||
}
|
||||
|
||||
.flyer {
|
||||
width: 20rem;
|
||||
max-width: 30vw;
|
||||
}
|
||||
|
||||
nav {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
p {
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-family: var(--font-details);
|
||||
margin: -0.5rem 0;
|
||||
font-size: 2rem;
|
||||
font-weight: 400;
|
||||
}
|
||||
h3 {
|
||||
font-size: 0.75rem;
|
||||
color: var(--fg-1);
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 992px) {
|
||||
.main {
|
||||
.home {
|
||||
flex-flow: column;
|
||||
align-items: center;
|
||||
|
||||
article {
|
||||
padding-left: 0px;
|
||||
}
|
||||
|
||||
.flyer {
|
||||
width: 50vw;
|
||||
max-width: 50vw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 641px) {
|
||||
.main {
|
||||
.home {
|
||||
article {
|
||||
text-align: justify;
|
||||
}
|
||||
.flyer {
|
||||
width: 90vw;
|
||||
max-width: 90vw;
|
||||
padding-bottom: 2rem;
|
||||
padding-bottom: 2rem;
|
||||
}
|
||||
nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-flow: column;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
136
next/app/prog/city-wide/page.js
Normal file
136
next/app/prog/city-wide/page.js
Normal file
@@ -0,0 +1,136 @@
|
||||
import getData from "@/api";
|
||||
import styles from "./style.module.scss";
|
||||
import Fanfare from "@/components/fanfare";
|
||||
import TimeSlot from "@/components/timeSlot";
|
||||
import Empty from "@/components/empty";
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
export async function generateMetadata() {
|
||||
const site = await getData("site", {
|
||||
populate: {
|
||||
edition: {
|
||||
populate: {
|
||||
flyer: {
|
||||
fields: [
|
||||
"name",
|
||||
"url",
|
||||
"alternativeText",
|
||||
"caption",
|
||||
"width",
|
||||
"height",
|
||||
],
|
||||
},
|
||||
programs: true,
|
||||
},
|
||||
fields: ["id", "introduction", "title", "subtitle"],
|
||||
},
|
||||
},
|
||||
fields: ["author"],
|
||||
});
|
||||
const current_edition = site.data?.attributes.edition.data?.attributes;
|
||||
const flyer =
|
||||
site.data?.attributes.edition.data?.attributes.flyer.data.attributes;
|
||||
const programs = current_edition?.programs.data;
|
||||
const cityWide = programs?.filter(
|
||||
(e) => e.attributes.type === "city-wide"
|
||||
)[0];
|
||||
|
||||
return cityWide
|
||||
? {
|
||||
metadataBase: `${process.env.NEXT_PUBLIC_ORIGIN}`,
|
||||
title: cityWide.attributes.title,
|
||||
description: cityWide.attributes.description,
|
||||
alternates: {
|
||||
canonical: "/prog/city-wide",
|
||||
},
|
||||
openGraph: {
|
||||
title: cityWide.attributes.title,
|
||||
url: `${process.env.NEXT_PUBLIC_ORIGIN}/prog/city-wide`,
|
||||
description: cityWide.attributes.description,
|
||||
images: {
|
||||
url: `${process.env.NEXT_PUBLIC_IMG_URI}${flyer.url}`,
|
||||
width: flyer.width,
|
||||
height: flyer.height,
|
||||
},
|
||||
authors: [site.data.attributes.author],
|
||||
type: "website",
|
||||
locale: "fr_FR",
|
||||
siteName: "Le Fefan - Festival de Fanfares",
|
||||
},
|
||||
}
|
||||
: {};
|
||||
}
|
||||
|
||||
export default async function CityWide() {
|
||||
const site = await getData("site", {
|
||||
populate: {
|
||||
edition: {
|
||||
populate: {
|
||||
programs: {
|
||||
populate: {
|
||||
bands: {
|
||||
fields: ["*"],
|
||||
},
|
||||
time_slots: {
|
||||
fields: ["*"],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
const edition = site.data?.attributes.edition ?? {};
|
||||
const program =
|
||||
edition.data?.attributes.programs.data.filter(
|
||||
({ attributes: r }) => r.type === "city-wide"
|
||||
)[0] ?? {};
|
||||
const time_slots = program.attributes?.time_slots.data ?? [];
|
||||
|
||||
return (
|
||||
<main className={styles.main}>
|
||||
{program.attributes ? (
|
||||
<>
|
||||
<h2>{program.attributes.title}</h2>
|
||||
<section className={styles.program}>
|
||||
{time_slots.map(({ id, attributes: attr }) => {
|
||||
return (
|
||||
<TimeSlot
|
||||
key={id}
|
||||
content={attr.content}
|
||||
start={attr.start}
|
||||
end={attr.end}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</section>
|
||||
<section className={styles.program}>
|
||||
<article className={styles.featuring}>
|
||||
{program.attributes.bands.data.length === 0 ? null : (
|
||||
<h3>Les Fanfares</h3>
|
||||
)}
|
||||
<p className={styles.introduction}>
|
||||
{program.attributes.description}
|
||||
</p>
|
||||
{program.attributes.bands.data.map(({ id, attributes: attr }) => {
|
||||
return (
|
||||
<Fanfare key={id} location={attr.location} name={attr.name} />
|
||||
);
|
||||
})}
|
||||
</article>
|
||||
<iframe
|
||||
className={styles.map}
|
||||
src={program.attributes.map_uri}
|
||||
width={640}
|
||||
height={480}
|
||||
></iframe>
|
||||
</section>
|
||||
|
||||
<section className={styles.program}></section>
|
||||
</>
|
||||
) : (
|
||||
<Empty message="Pas de programmation à afficher pour le moment"></Empty>
|
||||
)}
|
||||
</main>
|
||||
);
|
||||
}
|
||||
95
next/app/prog/city-wide/style.module.scss
Normal file
95
next/app/prog/city-wide/style.module.scss
Normal file
@@ -0,0 +1,95 @@
|
||||
.main {
|
||||
display: flex;
|
||||
flex: 1 1 0%;
|
||||
flex-flow: column;
|
||||
|
||||
.program {
|
||||
display: grid;
|
||||
width: 80rem;
|
||||
max-width: 90vw;
|
||||
align-self: center;
|
||||
grid-template-columns: repeat(3, calc(80rem / 3));
|
||||
grid-auto-rows: max-content;
|
||||
|
||||
&+.program {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
h3 {
|
||||
grid-column: 1 / -1;
|
||||
font-family: var(--font-details);
|
||||
margin: 0.25rem 0px;
|
||||
font-size: 2rem;
|
||||
font-weight: 400;
|
||||
justify-self: stretch;
|
||||
}
|
||||
}
|
||||
|
||||
.map {
|
||||
width: auto;
|
||||
grid-column: span 2;
|
||||
place-self: stretch;
|
||||
border: medium;
|
||||
}
|
||||
|
||||
.featuring {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, calc(80rem / 6));
|
||||
place-self: stretch;
|
||||
padding: 0px 0.5rem;
|
||||
place-items: stretch;
|
||||
align-content: start;
|
||||
grid-auto-rows: max-content;
|
||||
|
||||
h3 {
|
||||
font-family: var(--font-details);
|
||||
grid-column: 1 / -1;
|
||||
margin: 0.25rem 0px;
|
||||
font-size: 2rem;
|
||||
font-weight: 400;
|
||||
justify-self: stretch;
|
||||
max-width: 80vw;
|
||||
}
|
||||
|
||||
.introduction {
|
||||
grid-column: 1 / -1;
|
||||
justify-self: stretch;
|
||||
text-align: left;
|
||||
max-width: 90vw;
|
||||
padding-right: 2rem;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
h2 {
|
||||
align-self: center;
|
||||
font-family: var(--font-details);
|
||||
font-size: 2rem;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 80rem) {
|
||||
.main {
|
||||
.program {
|
||||
grid-template-columns: repeat(2, calc(50vw - 3rem));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 60rem) {
|
||||
.main {
|
||||
.program {
|
||||
grid-template-columns: repeat(1, 90vw);
|
||||
}
|
||||
|
||||
.featuring {
|
||||
.introduction {
|
||||
padding-right: unset;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
131
next/app/prog/village/page.js
Normal file
131
next/app/prog/village/page.js
Normal file
@@ -0,0 +1,131 @@
|
||||
import Empty from "@/components/empty";
|
||||
import styles from "./style.module.scss";
|
||||
import getData from "@/api";
|
||||
import ImageBlock from "@/components/imageBlock";
|
||||
import { BlocksRenderer } from "@strapi/blocks-react-renderer";
|
||||
import TimeSlot from "@/components/timeSlot";
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
export async function generateMetadata() {
|
||||
const site = await getData("site", {
|
||||
populate: {
|
||||
edition: {
|
||||
populate: {
|
||||
flyer: {
|
||||
fields: [
|
||||
"name",
|
||||
"url",
|
||||
"alternativeText",
|
||||
"caption",
|
||||
"width",
|
||||
"height",
|
||||
],
|
||||
},
|
||||
programs: true,
|
||||
},
|
||||
fields: ["id", "introduction", "title", "subtitle"],
|
||||
},
|
||||
},
|
||||
fields: ["author"],
|
||||
});
|
||||
const current_edition = site.data?.attributes.edition.data?.attributes;
|
||||
const flyer =
|
||||
site.data?.attributes.edition.data?.attributes.flyer.data.attributes;
|
||||
const programs = current_edition?.programs.data;
|
||||
const village = programs?.filter((e) => e.attributes.type === "village")[0];
|
||||
|
||||
return village
|
||||
? {
|
||||
metadataBase: `${process.env.NEXT_PUBLIC_ORIGIN}`,
|
||||
title: village.attributes.title,
|
||||
description: village.attributes.description,
|
||||
alternates: {
|
||||
canonical: "/prog/village",
|
||||
},
|
||||
openGraph: {
|
||||
title: village.attributes.title,
|
||||
url: `${process.env.NEXT_PUBLIC_ORIGIN}/prog/village`,
|
||||
description: village.attributes.description,
|
||||
images: {
|
||||
url: `${process.env.NEXT_PUBLIC_IMG_URI}${flyer.url}`,
|
||||
width: flyer.width,
|
||||
height: flyer.height,
|
||||
},
|
||||
authors: [site.data.attributes.author],
|
||||
type: "website",
|
||||
locale: "fr_FR",
|
||||
siteName: "Le Fefan - Festival de Fanfares",
|
||||
},
|
||||
}
|
||||
: {};
|
||||
}
|
||||
|
||||
export default async function Village() {
|
||||
const site = await getData("site", {
|
||||
populate: {
|
||||
edition: {
|
||||
populate: {
|
||||
programs: {
|
||||
populate: {
|
||||
sticker: {
|
||||
fields: ["alternativeText", "url"],
|
||||
},
|
||||
bands: {
|
||||
fields: ["*"],
|
||||
},
|
||||
time_slots: {
|
||||
fields: ["*"],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
const edition = site.data?.attributes.edition ?? {};
|
||||
const program =
|
||||
edition.data?.attributes.programs.data.filter(
|
||||
({ attributes: r }) => r.type === "village"
|
||||
)[0] ?? {};
|
||||
const time_slots = program.attributes?.time_slots.data ?? [];
|
||||
|
||||
return (
|
||||
<main className={styles.main}>
|
||||
{program.attributes ? (
|
||||
<>
|
||||
<h2>{program.attributes?.title ?? "Le Village"}</h2>
|
||||
<section className={styles.villageSection}>
|
||||
<article>
|
||||
{program.attributes?.introduction ? (
|
||||
<BlocksRenderer content={program.attributes.introduction} />
|
||||
) : null}
|
||||
</article>
|
||||
{program.attributes?.sticker?.data ? (
|
||||
<ImageBlock
|
||||
src={`${process.env.NEXT_PUBLIC_IMG_URI}${program.attributes.sticker.data.attributes.url}`}
|
||||
alt={program.attributes.sticker.data.attributes.alternativeText}
|
||||
className={styles.villageImg}
|
||||
/>
|
||||
) : (
|
||||
<div></div>
|
||||
)}
|
||||
<article>
|
||||
{time_slots.map(({ id, attributes: attr }) => {
|
||||
return (
|
||||
<TimeSlot
|
||||
key={id}
|
||||
content={attr.content}
|
||||
start={attr.start}
|
||||
end={attr.end}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</article>
|
||||
</section>
|
||||
</>
|
||||
) : (
|
||||
<Empty message="Le village est encore secret"></Empty>
|
||||
)}
|
||||
</main>
|
||||
);
|
||||
}
|
||||
40
next/app/prog/village/style.module.scss
Normal file
40
next/app/prog/village/style.module.scss
Normal file
@@ -0,0 +1,40 @@
|
||||
.main {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
|
||||
h2 {
|
||||
font-family: var(--font-details);
|
||||
font-size: 2rem;
|
||||
font-weight: 400;
|
||||
text-align: center;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
.villageSection {
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
width: 60rem;
|
||||
align-self: center;
|
||||
|
||||
.villageImg {
|
||||
width: 18rem;
|
||||
align-self: center;
|
||||
flex: 0;
|
||||
}
|
||||
|
||||
article {
|
||||
flex: 1;
|
||||
padding: 0 0.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 60rem) {
|
||||
.main {
|
||||
.villageSection {
|
||||
flex-flow: column;
|
||||
width: 96vw;
|
||||
}
|
||||
}
|
||||
}
|
||||
74
next/app/sitemap.js
Normal file
74
next/app/sitemap.js
Normal file
@@ -0,0 +1,74 @@
|
||||
import getData from "@/api";
|
||||
|
||||
export default async function sitemap() {
|
||||
// Main page information
|
||||
const site = await getData("site", {
|
||||
fields: ["updatedAt"],
|
||||
populate: {
|
||||
edition: {
|
||||
fields: ["id", "updatedAt", "publishedAt"],
|
||||
populate: {
|
||||
programs: true,
|
||||
fields: ["type", "updatedAt", "publishedAt"],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const edition = site.data?.attributes.edition.data;
|
||||
const siteLastModified =
|
||||
site && edition
|
||||
? new Date(
|
||||
Math.max(
|
||||
new Date(site.data?.attributes.updatedAt),
|
||||
new Date(edition.attributes.updatedAt)
|
||||
)
|
||||
)
|
||||
: new Date();
|
||||
|
||||
// Programs information
|
||||
const programs = (edition?.attributes.programs.data ?? []).filter(
|
||||
(p) => p.attributes.publishedAt !== null
|
||||
);
|
||||
|
||||
// Editions information
|
||||
const editions = await getData("editions", { fields: ["id", "updatedAt"] });
|
||||
const previousEditions = editions.data?.filter(
|
||||
(e) => e.id !== edition.id && e.attributes.publishedAt !== null
|
||||
);
|
||||
const editionsLastModifiedTimestamp = Math.max(
|
||||
...(previousEditions ?? []).map((e) => new Date(e.attributes.updatedAt))
|
||||
);
|
||||
|
||||
const editionsLastModified =
|
||||
editionsLastModifiedTimestamp === -Infinity
|
||||
? siteLastModified
|
||||
: new Date(editionsLastModifiedTimestamp);
|
||||
|
||||
return [
|
||||
{
|
||||
url: `${process.env.NEXT_PUBLIC_ORIGIN}`,
|
||||
lastModified: siteLastModified,
|
||||
changeFrequency: "yearly",
|
||||
prority: 1,
|
||||
},
|
||||
...(programs ?? []).map((p) => ({
|
||||
url: `${process.env.NEXT_PUBLIC_ORIGIN}/prog/${p.attributes.type}`,
|
||||
lastModified: new Date(p.attributes.updatedAt),
|
||||
changeFrequency: "yearly",
|
||||
priority: 0.8,
|
||||
})),
|
||||
{
|
||||
url: `${process.env.NEXT_PUBLIC_ORIGIN}/editions`,
|
||||
lastModified: editionsLastModified,
|
||||
changeFrequency: "yearly",
|
||||
priority: 0.6,
|
||||
},
|
||||
...(previousEditions ?? []).map((e) => ({
|
||||
url: `${process.env.NEXT_PUBLIC_ORIGIN}/editions/${e.id}`,
|
||||
lastModified: new Date(e.attributes.updatedAt),
|
||||
changeFrequency: "yearly",
|
||||
priority: 0.5,
|
||||
})),
|
||||
];
|
||||
}
|
||||
63
next/app/sponsor/page.js
Normal file
63
next/app/sponsor/page.js
Normal file
@@ -0,0 +1,63 @@
|
||||
import getData from "@/api";
|
||||
import styles from "./style.module.scss";
|
||||
import Link from "next/link";
|
||||
import Empty from "@/components/empty";
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
export async function generateMetadata() {
|
||||
return {
|
||||
metadataBase: `${process.env.NEXT_PUBLIC_ORIGIN}`,
|
||||
title: "Partenaires",
|
||||
};
|
||||
}
|
||||
|
||||
export default async function Sponsor() {
|
||||
const site = await getData("site", {
|
||||
populate: {
|
||||
edition: {
|
||||
fields: [],
|
||||
populate: {
|
||||
sponsors: {
|
||||
fields: ["name", "uri", "image"],
|
||||
populate: {
|
||||
image: {
|
||||
fields: ["alternativeText", "name", "url"],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
const edition = site.data?.attributes.edition.data.attributes;
|
||||
const sponsors = edition?.sponsors.data;
|
||||
|
||||
return (
|
||||
<main className={styles.main}>
|
||||
<h2>Ils nous soutiennent</h2>
|
||||
{sponsors && sponsors.length > 0 ? (
|
||||
<section className={styles.sponsors}>
|
||||
{sponsors.map(({ id, attributes: attr }) => {
|
||||
return (
|
||||
<figure key={id}>
|
||||
<Link target="_blank" href={attr.uri}>
|
||||
<img
|
||||
alt={attr.image.data.attributes.alternativeText}
|
||||
src={`${process.env.NEXT_PUBLIC_IMG_URI}${attr.image.data.attributes.url}`}
|
||||
/>
|
||||
</Link>
|
||||
<figcaption>
|
||||
<Link target="_blank" href={attr.uri}>
|
||||
{attr.name}
|
||||
</Link>
|
||||
</figcaption>
|
||||
</figure>
|
||||
);
|
||||
})}
|
||||
</section>
|
||||
) : (
|
||||
<Empty message="Pas de sponsors à afficher pour l'édition en cours" />
|
||||
)}
|
||||
</main>
|
||||
);
|
||||
}
|
||||
49
next/app/sponsor/style.module.scss
Normal file
49
next/app/sponsor/style.module.scss
Normal file
@@ -0,0 +1,49 @@
|
||||
.main {
|
||||
flex: 1 1 0%;
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
|
||||
h2 {
|
||||
align-self: center;
|
||||
font-family: var(--font-details);
|
||||
font-size: 2rem;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
|
||||
.sponsors {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 20rem);
|
||||
place-items: end center;
|
||||
align-self: center;
|
||||
|
||||
figure {
|
||||
padding: 1rem 0px;
|
||||
}
|
||||
|
||||
figcaption {
|
||||
padding: 0.25rem 0px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 13.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.sponsors figure:last-child {
|
||||
grid-column-start: span 4;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 70rem) {
|
||||
.sponsors {
|
||||
grid-template-columns: 80vw;
|
||||
|
||||
img {
|
||||
width: calc(80vw - 20rem);
|
||||
}
|
||||
}
|
||||
.sponsors figure:last-child {
|
||||
grid-column-start: unset;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user