Import repositories from gitlab
This commit is contained in:
15
next/.dockerignore
Normal file
15
next/.dockerignore
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
Dockerfile
|
||||||
|
.dockerignore
|
||||||
|
node_modules
|
||||||
|
npm-debug.log
|
||||||
|
README.md
|
||||||
|
.next
|
||||||
|
.git
|
||||||
|
/out/
|
||||||
|
/build
|
||||||
|
.DS_Store
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
.vercel
|
||||||
|
*.tsbuildinfo
|
||||||
|
next-env.d.ts
|
||||||
5
next/.env.example
Normal file
5
next/.env.example
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
NEXT_PUBLIC_CONTENT_URI=http://127.0.0.1:1337/api
|
||||||
|
NEXT_PUBLIC_IMG_URI=http://127.0.0.1:1337
|
||||||
|
NEXT_PUBLIC_ORIGIN=http://localhost:3000
|
||||||
|
NEXT_PRIVATE_CONTENT_URI=https://fefan-backend:1337/api
|
||||||
|
NEXT_PRIVATE_IMG_URI=https://fefan-backend:1337
|
||||||
3
next/.eslintrc.json
Normal file
3
next/.eslintrc.json
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"extends": "next/core-web-vitals"
|
||||||
|
}
|
||||||
38
next/.gitignore
vendored
Normal file
38
next/.gitignore
vendored
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
/node_modules
|
||||||
|
/.pnp
|
||||||
|
.pnp.js
|
||||||
|
.yarn/install-state.gz
|
||||||
|
|
||||||
|
# testing
|
||||||
|
/coverage
|
||||||
|
|
||||||
|
# next.js
|
||||||
|
/.next/
|
||||||
|
/out/
|
||||||
|
|
||||||
|
# production
|
||||||
|
/build
|
||||||
|
|
||||||
|
# misc
|
||||||
|
.DS_Store
|
||||||
|
*.pem
|
||||||
|
|
||||||
|
# debug
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
|
||||||
|
# local env files
|
||||||
|
.env*.local
|
||||||
|
|
||||||
|
# vercel
|
||||||
|
.vercel
|
||||||
|
|
||||||
|
# typescript
|
||||||
|
*.tsbuildinfo
|
||||||
|
next-env.d.ts
|
||||||
|
|
||||||
|
.env
|
||||||
32
next/Dockerfile
Normal file
32
next/Dockerfile
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
FROM node:20.10-alpine AS base
|
||||||
|
|
||||||
|
FROM base AS deps
|
||||||
|
RUN apk add --no-cache libc6-compat
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY package.json yarn.lock ./
|
||||||
|
RUN yarn --frozen-lockfile
|
||||||
|
|
||||||
|
# Copy source for runtime build
|
||||||
|
FROM base AS runner
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
ENV NODE_ENV production
|
||||||
|
ENV NEXT_TELEMETRY_DISABLED 1
|
||||||
|
|
||||||
|
RUN addgroup --system --gid 1001 nodejs
|
||||||
|
RUN adduser --system --uid 1001 nextjs
|
||||||
|
|
||||||
|
COPY --from=deps /app/node_modules ./node_modules
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Build at container startup instead of build time
|
||||||
|
RUN mkdir .next && chown nextjs:nodejs .next
|
||||||
|
USER nextjs
|
||||||
|
|
||||||
|
EXPOSE 3000
|
||||||
|
ENV PORT 3000
|
||||||
|
ENV HOSTNAME "0.0.0.0"
|
||||||
|
|
||||||
|
# Build and start in one step
|
||||||
|
CMD ["sh", "-c", "yarn build && yarn start"]
|
||||||
36
next/README.md
Normal file
36
next/README.md
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
First, run the development server:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run dev
|
||||||
|
# or
|
||||||
|
yarn dev
|
||||||
|
# or
|
||||||
|
pnpm dev
|
||||||
|
# or
|
||||||
|
bun dev
|
||||||
|
```
|
||||||
|
|
||||||
|
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||||
|
|
||||||
|
You can start editing the page by modifying `app/page.js`. The page auto-updates as you edit the file.
|
||||||
|
|
||||||
|
This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
|
||||||
|
|
||||||
|
## Learn More
|
||||||
|
|
||||||
|
To learn more about Next.js, take a look at the following resources:
|
||||||
|
|
||||||
|
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
||||||
|
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
||||||
|
|
||||||
|
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
|
||||||
|
|
||||||
|
## Deploy on Vercel
|
||||||
|
|
||||||
|
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
||||||
|
|
||||||
|
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
|
||||||
19
next/api/index.js
Normal file
19
next/api/index.js
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import qs from "qs";
|
||||||
|
|
||||||
|
async function getData(path, query) {
|
||||||
|
const queryString = qs.stringify(query);
|
||||||
|
const isServerSide = typeof window === "undefined";
|
||||||
|
const res = await fetch(
|
||||||
|
`${
|
||||||
|
isServerSide
|
||||||
|
? process.env.NEXT_PRIVATE_CONTENT_URI ??
|
||||||
|
"http://fefan-backend:1337/api"
|
||||||
|
: process.env.NEXT_PUBLIC_CONTENT_URI ?? "https://content.fefan.fr/api"
|
||||||
|
}/${path}?${queryString}`,
|
||||||
|
{ cache: "no-store" }
|
||||||
|
);
|
||||||
|
|
||||||
|
return await res.json();
|
||||||
|
}
|
||||||
|
|
||||||
|
export default getData;
|
||||||
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
20
next/components/articleBlock/index.jsx
Normal file
20
next/components/articleBlock/index.jsx
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { BlocksRenderer } from "@strapi/blocks-react-renderer";
|
||||||
|
import Button from "../button";
|
||||||
|
import styles from "./style.module.scss";
|
||||||
|
|
||||||
|
export default function ArticleBlock({title, content, link, className, ...props}) {
|
||||||
|
return (
|
||||||
|
<article {...props} className={`${styles.articleBlock} ${className ?? ''}`}>
|
||||||
|
<h4>{title}</h4>
|
||||||
|
{content ? <BlocksRenderer content={content}/> : null}
|
||||||
|
<Button
|
||||||
|
type="secondary"
|
||||||
|
as="a"
|
||||||
|
href={link}
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
Lire la suite
|
||||||
|
</Button>
|
||||||
|
</article>
|
||||||
|
);
|
||||||
|
}
|
||||||
17
next/components/articleBlock/style.module.scss
Normal file
17
next/components/articleBlock/style.module.scss
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
.articleBlock {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
margin: 1rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
margin-top: 1rem;
|
||||||
|
align-self: flex-end;
|
||||||
|
}
|
||||||
|
}
|
||||||
28
next/components/button/index.jsx
Normal file
28
next/components/button/index.jsx
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import styles from './style.module.scss';
|
||||||
|
|
||||||
|
export default function Button({as, type, children, ...props}) {
|
||||||
|
switch (as ?? 'a') {
|
||||||
|
case 'a':
|
||||||
|
return (
|
||||||
|
<a
|
||||||
|
tabIndex="0"
|
||||||
|
{...props}
|
||||||
|
className={`${styles[type ?? 'secondary']} ${styles.button} not-a-link ${props.className}`}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
case 'button':
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
tabIndex="0"
|
||||||
|
{...props}
|
||||||
|
className={`${styles[type ?? 'secondary']} ${styles.button} not-a-link ${props.className}`}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
return (null);
|
||||||
|
}
|
||||||
47
next/components/button/style.module.scss
Normal file
47
next/components/button/style.module.scss
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
.button {
|
||||||
|
appearance: none;
|
||||||
|
font-size: unset;
|
||||||
|
background: unset;
|
||||||
|
font-family: unset;
|
||||||
|
display: inline-block;
|
||||||
|
padding: 1.25rem 1.5rem;
|
||||||
|
border-radius: 2.25rem;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
margin: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.secondary {
|
||||||
|
color: unset;
|
||||||
|
border: solid 1px var(--fg-2);
|
||||||
|
|
||||||
|
&:hover, &:focus {
|
||||||
|
background: var(--fg-3);
|
||||||
|
border: solid 1px var(--fg-3);
|
||||||
|
box-shadow: 0 0.25rem 0.35rem rgba(0, 0, 0, 0.45);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:visited {
|
||||||
|
color: unset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.primary {
|
||||||
|
color: white;
|
||||||
|
box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.25);
|
||||||
|
text-decoration-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-size: 250% 100%;
|
||||||
|
transition: all ease var(--transition-time);
|
||||||
|
|
||||||
|
&:hover, &:focus {
|
||||||
|
text-decoration-color: var(--fefan-1);
|
||||||
|
background-position-x: 100%;
|
||||||
|
box-shadow: 0 0.25rem 0.35rem rgba(0, 0, 0, 0.45);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
&:visited {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
32
next/components/editionElement/editionBlock/index.jsx
Normal file
32
next/components/editionElement/editionBlock/index.jsx
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import ImageBlock from "@/components/imageBlock";
|
||||||
|
import StatBlock from "@/components/statBlock";
|
||||||
|
import VideoBlock from "@/components/videoBlock";
|
||||||
|
|
||||||
|
import styles from './style.module.scss';
|
||||||
|
|
||||||
|
export default async function EditionBlock({type, ...props}) {
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case 'stat':
|
||||||
|
return (
|
||||||
|
<article className={styles.editionBlock}>
|
||||||
|
<StatBlock {...props} />
|
||||||
|
</article>
|
||||||
|
);
|
||||||
|
|
||||||
|
case 'image':
|
||||||
|
return (
|
||||||
|
<article className={styles.editionBlock}>
|
||||||
|
<ImageBlock {...props} src={props.value} alt={props.title}/>
|
||||||
|
</article>
|
||||||
|
);
|
||||||
|
|
||||||
|
case 'video':
|
||||||
|
return (
|
||||||
|
<article className={styles.editionBlock}>
|
||||||
|
<VideoBlock {...props} src={props.value} title={props.title}/>
|
||||||
|
</article>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
.editionBlock {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
31
next/components/editionElement/index.jsx
Normal file
31
next/components/editionElement/index.jsx
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import ImageBlock from '../imageBlock';
|
||||||
|
import EditionBlock from './editionBlock';
|
||||||
|
import styles from './style.module.scss';
|
||||||
|
|
||||||
|
export default async function EditionElement(props) {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className={`${styles.editionSection} ${props.full ? styles.fullViewport : ''}`}>
|
||||||
|
{props.href ?
|
||||||
|
(
|
||||||
|
<a className={styles.image} href={props.href}>
|
||||||
|
<img src={props.flyerImg} alt={props.flyerAlt}/>
|
||||||
|
</a>
|
||||||
|
) : (
|
||||||
|
<ImageBlock className={styles.image} alt={props.flyerAlt} src={props.flyerImg} />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
{props.blocks.slice(0, 6).map(block => (
|
||||||
|
<EditionBlock {...block} key={`${block.type}-${block.id}`} href={props.href} />
|
||||||
|
))}
|
||||||
|
{props.full ? <aside className={styles.callToAction}>
|
||||||
|
<a href={props.href}>
|
||||||
|
<h2>{props.title}</h2>
|
||||||
|
<svg width="40px" height="40px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M6 12H18M18 12L13 7M18 12L13 17" stroke="var(--fefan-1)" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
</aside> : null}
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
157
next/components/editionElement/style.module.scss
Normal file
157
next/components/editionElement/style.module.scss
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
.editionSection {
|
||||||
|
--cell-width: 14.25rem;
|
||||||
|
|
||||||
|
display: grid;
|
||||||
|
scroll-snap-align: center;
|
||||||
|
flex: none;
|
||||||
|
grid-template-columns: repeat(4, max-content);
|
||||||
|
grid-template-rows: repeat(2, max-content);
|
||||||
|
align-content: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
&.fullViewport {
|
||||||
|
min-height: calc(2 * var(--cell-width) + 10rem + 4 * var(--border-width));
|
||||||
|
height: calc(100vh - 2 * var(--border-width) - 5rem - 2rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.image {
|
||||||
|
display: block;
|
||||||
|
grid-row: span 2;
|
||||||
|
margin: 0.5rem;
|
||||||
|
img {
|
||||||
|
display: block;
|
||||||
|
height: calc(2 * var(--cell-width) + 1rem + 4 * var(--border-width));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
img.image {
|
||||||
|
display: block;
|
||||||
|
height: calc(2 * var(--cell-width) + 1rem + 4 * var(--border-width));
|
||||||
|
}
|
||||||
|
|
||||||
|
article {
|
||||||
|
border: solid 1px var(--fg-2);
|
||||||
|
width: var(--cell-width);
|
||||||
|
height: var(--cell-width);
|
||||||
|
justify-content: center;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
object-fit: fill;
|
||||||
|
margin: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.callToAction {
|
||||||
|
grid-row: 3;
|
||||||
|
grid-column: span 4;
|
||||||
|
justify-content: flex-end;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
& > a {
|
||||||
|
justify-content: flex-end;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.informations {
|
||||||
|
color: var(--fg-0);
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column;
|
||||||
|
margin-left: 1rem;
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-family: var(--font-details);
|
||||||
|
margin: -0.5rem 0px;
|
||||||
|
font-size: 2rem;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin: 0px;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
opacity: 0.75;
|
||||||
|
font-weight: 700;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: 70rem) {
|
||||||
|
.editionSection {
|
||||||
|
grid-template-columns: repeat(3, max-content);
|
||||||
|
|
||||||
|
.callToAction {
|
||||||
|
grid-column: span 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
article:nth-of-type(n+5) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: 60rem) {
|
||||||
|
.editionSection {
|
||||||
|
grid-template-columns: repeat(2, max-content);
|
||||||
|
|
||||||
|
.callToAction {
|
||||||
|
grid-column: span 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
article:nth-of-type(n+3) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: 40rem) and (min-width: 30rem) {
|
||||||
|
.editionSection {
|
||||||
|
grid-template-columns: repeat(1, max-content);
|
||||||
|
|
||||||
|
.callToAction {
|
||||||
|
grid-column: span 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
article {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: 30rem) {
|
||||||
|
.editionSection {
|
||||||
|
--cell-width: calc(50vw - 5rem);
|
||||||
|
grid-template-columns: repeat(2, max-content);
|
||||||
|
grid-template-rows: repeat(5, max-content);
|
||||||
|
|
||||||
|
&.fullViewport {
|
||||||
|
height: calc(3.5 * var(--cell-width) + 10rem + 4 * var(--border-width));
|
||||||
|
min-height: calc(100vh - 2 * var(--border-width) - 4rem - 2rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.image {
|
||||||
|
grid-row: 1;
|
||||||
|
grid-column: span 2;
|
||||||
|
img {
|
||||||
|
height: auto;
|
||||||
|
width: calc(2 * var(--cell-width) + 1rem + 4 * var(--border-width));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
img.image {
|
||||||
|
height: auto;
|
||||||
|
width: calc(2 * var(--cell-width) + 1rem + 4 * var(--border-width));
|
||||||
|
}
|
||||||
|
|
||||||
|
.callToAction {
|
||||||
|
grid-row: 5;
|
||||||
|
grid-column: span 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
article:nth-of-type(n+3) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
17
next/components/editionGallery/index.jsx
Normal file
17
next/components/editionGallery/index.jsx
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import ImageBlock from "../imageBlock";
|
||||||
|
import styles from "./style.module.scss";
|
||||||
|
|
||||||
|
export default function EditionGallery({ images }) {
|
||||||
|
return (
|
||||||
|
<section className={styles.gallery}>
|
||||||
|
{images.map(({ id, attributes: attr }) => (
|
||||||
|
<div className={styles.galleryImage} key={id}>
|
||||||
|
<ImageBlock
|
||||||
|
alt={attr.alternativeText}
|
||||||
|
src={`${process.env.NEXT_PUBLIC_IMG_URI}${attr.url}`}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
34
next/components/editionGallery/style.module.scss
Normal file
34
next/components/editionGallery/style.module.scss
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
.gallery {
|
||||||
|
--gallery-width: 80rem;
|
||||||
|
--gallery-columns: 3;
|
||||||
|
grid-template-columns: repeat(var(--gallery-columns), calc(var(--gallery-width) / var(--gallery-columns)));
|
||||||
|
grid-auto-rows: max-content;
|
||||||
|
align-items: center;
|
||||||
|
justify-items: center;
|
||||||
|
display: grid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.galleryImage {
|
||||||
|
grid-column: span 1;
|
||||||
|
margin: 0.5rem;
|
||||||
|
max-height: calc(((6.5 / 10) * (var(--gallery-width) / var(--gallery-columns))) - 1rem);
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
& > img {
|
||||||
|
width: calc((var(--gallery-width) / var(--gallery-columns)) - 1rem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: 80rem) {
|
||||||
|
.gallery {
|
||||||
|
--gallery-width: 90vw;
|
||||||
|
--gallery-columns: 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: 50rem) {
|
||||||
|
.gallery {
|
||||||
|
--gallery-width: 90vw;
|
||||||
|
--gallery-columns: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
63
next/components/email/index.jsx
Normal file
63
next/components/email/index.jsx
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import qs from 'qs';
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import Button from "../button";
|
||||||
|
import styles from "./style.module.scss";
|
||||||
|
|
||||||
|
function fetchEmail(query) {
|
||||||
|
const queryString = qs.stringify(query);
|
||||||
|
return fetch(`${process.env.NEXT_PUBLIC_CONTENT_URI}/site?${queryString}`).then((data) => data.json());
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Email() {
|
||||||
|
const [email, setEmail] = useState("");
|
||||||
|
const [displayEmail, setDisplayEmail] = useState(false);
|
||||||
|
const [copied, setCopied] = useState(false);
|
||||||
|
|
||||||
|
const toggleEmail = () => {
|
||||||
|
if (email === "") {
|
||||||
|
fetchEmail({
|
||||||
|
fields: ['contact_mail']
|
||||||
|
}).then((res) => {
|
||||||
|
setEmail(res.data.attributes.contact_mail);
|
||||||
|
setDisplayEmail(true)
|
||||||
|
})
|
||||||
|
return;
|
||||||
|
} else if (displayEmail === false) {
|
||||||
|
setDisplayEmail(true)
|
||||||
|
} else {
|
||||||
|
navigator.clipboard.writeText(email);
|
||||||
|
setCopied(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
setDisplayEmail(false);
|
||||||
|
}, 5000);
|
||||||
|
}, [displayEmail])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
setCopied(false);
|
||||||
|
}, 2000);
|
||||||
|
}, [copied])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
as="button"
|
||||||
|
type="secondary"
|
||||||
|
onClick={toggleEmail}
|
||||||
|
className={styles.emailButton}
|
||||||
|
title={displayEmail ? "Cliquez pour copier le mail" : null}
|
||||||
|
>
|
||||||
|
<label className={displayEmail ? styles.copy : null}>
|
||||||
|
{displayEmail ? email : "Cliquez pour afficher le mail"}
|
||||||
|
</label>
|
||||||
|
<label className={copied ? styles.showLabel : styles.hideLabel}>
|
||||||
|
{copied ? "copié" : null}
|
||||||
|
</label>
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
20
next/components/email/style.module.scss
Normal file
20
next/components/email/style.module.scss
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
.emailButton {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
label {
|
||||||
|
transition: all ease-in-out var(--transition-time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy {
|
||||||
|
cursor: copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
.showLabel {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hideLabel {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
15
next/components/empty/index.jsx
Normal file
15
next/components/empty/index.jsx
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import Button from "../button";
|
||||||
|
import styles from "./style.module.scss";
|
||||||
|
|
||||||
|
export default function Empty({message, ...props}) {
|
||||||
|
return <section className={styles.empty}>
|
||||||
|
<h2 {...props}>{message ?? "Il n'y a rien ici."}</h2>
|
||||||
|
<Button
|
||||||
|
as="a"
|
||||||
|
type="secondary"
|
||||||
|
href="/"
|
||||||
|
>
|
||||||
|
Retour à la page d'accueil
|
||||||
|
</Button>
|
||||||
|
</section>
|
||||||
|
}
|
||||||
9
next/components/empty/style.module.scss
Normal file
9
next/components/empty/style.module.scss
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
.empty {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-family: var(--font-details);
|
||||||
|
}
|
||||||
|
}
|
||||||
10
next/components/fanfare/index.jsx
Normal file
10
next/components/fanfare/index.jsx
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import styles from './style.module.scss';
|
||||||
|
|
||||||
|
export default async function Fanfare(props) {
|
||||||
|
return (
|
||||||
|
<div className={styles.band}>
|
||||||
|
<h4>{props.name}</h4>
|
||||||
|
<h5>{props.location}</h5>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
22
next/components/fanfare/style.module.scss
Normal file
22
next/components/fanfare/style.module.scss
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
.band {
|
||||||
|
padding: 0.75rem 0.25rem;
|
||||||
|
max-width: 30vw;
|
||||||
|
h4, h5 {
|
||||||
|
margin: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
font-family: var(--font-body);
|
||||||
|
font-weight: 500;
|
||||||
|
margin: 0px;
|
||||||
|
font-size: 1rem;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
h5 {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
opacity: 0.75;
|
||||||
|
font-weight: 700;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
}
|
||||||
14
next/components/footer/index.jsx
Normal file
14
next/components/footer/index.jsx
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import Link from "next/link";
|
||||||
|
import styles from "./style.module.scss";
|
||||||
|
|
||||||
|
export default async function Footer() {
|
||||||
|
return (
|
||||||
|
<footer className={styles.footer}>
|
||||||
|
<Link href="/legal">Mentions légales</Link>
|
||||||
|
<span className={styles.separator}> · </span>
|
||||||
|
<Link href="/sponsor">Ils nous soutiennent</Link>
|
||||||
|
<span className={styles.separator}> · </span>
|
||||||
|
<Link href="/contact">Contact</Link>
|
||||||
|
</footer>
|
||||||
|
);
|
||||||
|
}
|
||||||
13
next/components/footer/style.module.scss
Normal file
13
next/components/footer/style.module.scss
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
.footer {
|
||||||
|
border-top: var(--fg-3) solid var(--border-width);
|
||||||
|
text-align: center;
|
||||||
|
padding: 0.25rem;
|
||||||
|
|
||||||
|
span {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
34
next/components/header/index.jsx
Normal file
34
next/components/header/index.jsx
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import getData from '@/api';
|
||||||
|
import NavigationDrawer from './navigationDrawer';
|
||||||
|
|
||||||
|
export default async function Header() {
|
||||||
|
const site = await getData('site', {
|
||||||
|
populate: {
|
||||||
|
edition: {
|
||||||
|
fields: [],
|
||||||
|
populate: {
|
||||||
|
programs: {
|
||||||
|
fields: ['type', 'title'],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const editionId = site.data?.attributes.edition.data?.id;
|
||||||
|
const programs = editionId ? site.data.attributes.edition.data.attributes.programs.data : null
|
||||||
|
const socialLinkQuery = editionId ? {
|
||||||
|
filters: {
|
||||||
|
$or: [
|
||||||
|
{ edition: { id: { $null: true } } },
|
||||||
|
{ edition: { id: { $eq: editionId } } },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
} : {
|
||||||
|
filters: { edition: { id: { $null: true } } },
|
||||||
|
}
|
||||||
|
const socialLinks = await getData('social-links', socialLinkQuery)
|
||||||
|
const links = socialLinks.data ?? []
|
||||||
|
return (
|
||||||
|
<NavigationDrawer programs={programs} links={links}/>
|
||||||
|
);
|
||||||
|
}
|
||||||
46
next/components/header/navigationDrawer/index.jsx
Normal file
46
next/components/header/navigationDrawer/index.jsx
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
"use client";
|
||||||
|
import Link from "next/link";
|
||||||
|
import SocialIcon from "../social-icon";
|
||||||
|
import { useState } from "react";
|
||||||
|
import styles from "./style.module.scss";
|
||||||
|
|
||||||
|
export default function NavigationDrawer({programs, links , ...props}) {
|
||||||
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
|
||||||
|
const toggleDrawer = () => {
|
||||||
|
setIsOpen(prev => !prev);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<header className={`${styles.header} ${isOpen ? styles.drawerOpen : ""}`}>
|
||||||
|
<div className={`${styles.backdrop}`} onClick={toggleDrawer}/>
|
||||||
|
<button className={`${styles.social} ${styles.burgerIcon}`} onClick={toggleDrawer}>
|
||||||
|
<svg className={`feather feather-menu`} xmlns="http://www.w3.org/2000/svg" width="36" height="36" viewBox="0 0 24 24" fill="none" stroke="#1B519E" strokeWidth="1" strokeLinecap="round" strokeLinejoin="round"><line x1="3" y1="12" x2="21" y2="12"></line><line x1="3" y1="6" x2="21" y2="6"></line><line x1="3" y1="18" x2="21" y2="18"></line></svg>
|
||||||
|
</button>
|
||||||
|
<Link href="/" className={`not-a-link ${styles.logo}`}>
|
||||||
|
<img src="/fefan.png" alt="Fefan" />
|
||||||
|
<h1>Festival de fanfare</h1>
|
||||||
|
</Link>
|
||||||
|
<a className={`${styles.social} ${styles.programIcon}`} href="/prog/city-wide">
|
||||||
|
<svg className={`feather feather-calendar`} xmlns="http://www.w3.org/2000/svg" width="36" height="36" viewBox="0 0 24 24" fill="none" stroke="#1B519E" strokeWidth="1" strokeLinecap="round" strokeLinejoin="round"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect><line x1="16" y1="2" x2="16" y2="6"></line><line x1="8" y1="2" x2="8" y2="6"></line><line x1="3" y1="10" x2="21" y2="10"></line></svg>
|
||||||
|
</a>
|
||||||
|
<nav className={`${styles.navigation}`}>
|
||||||
|
<div className={styles.spacer} />
|
||||||
|
<Link href="/">Accueil</Link>
|
||||||
|
{programs ? programs.map(({ id, attributes: attr }) => (
|
||||||
|
<Link href={`/prog/${attr.type}`} key={id}>{attr.title}</Link>
|
||||||
|
)): null}
|
||||||
|
<Link href="/editions">Éditions précédentes</Link>
|
||||||
|
<Link href="/contact">Contact</Link>
|
||||||
|
<div className={styles.spacer} />
|
||||||
|
<nav className={styles.socialLinks}>
|
||||||
|
{links.map(({ id, attributes: attr }) => (
|
||||||
|
<Link aria-label={attr.type} target="_blank" href={attr.uri} key={id} className={`${styles.social} not-a-link`}>
|
||||||
|
<SocialIcon name={attr.type} />
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
|
</nav>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
);
|
||||||
|
}
|
||||||
156
next/components/header/navigationDrawer/style.module.scss
Normal file
156
next/components/header/navigationDrawer/style.module.scss
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
z-index: 200;
|
||||||
|
flex-flow: row;
|
||||||
|
border-bottom: var(--fg-3) solid var(--border-width);
|
||||||
|
background: var(--bg-0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.header + * {
|
||||||
|
margin-top: calc(5rem + var(--border-width));
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
display: flex;
|
||||||
|
margin: 0.5rem 1rem;
|
||||||
|
flex-flow: column;
|
||||||
|
text-decoration: none;
|
||||||
|
text-align: right;
|
||||||
|
color: var(--fefan-4);
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-family: var(--font-details);
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
margin: -0.10rem 0.35rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
img:first-child {
|
||||||
|
height: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.social {
|
||||||
|
appearance: none;
|
||||||
|
border: none;
|
||||||
|
background: unset;
|
||||||
|
font-size: unset;
|
||||||
|
margin: 0;
|
||||||
|
border-radius: 100%;
|
||||||
|
padding: 0.5rem;
|
||||||
|
text-decoration: none;
|
||||||
|
color: var(--fefan-1);
|
||||||
|
display: flex;
|
||||||
|
align-self: center;
|
||||||
|
justify-content: center;
|
||||||
|
box-shadow: inset 0 0 6px 3px var(--background);
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spacer {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spacer:first-of-type {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navigation {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex: 1;
|
||||||
|
a {
|
||||||
|
margin: 0 0.35rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.socialLinks {
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
margin-right: 1rem;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.backdrop {
|
||||||
|
position: fixed;
|
||||||
|
content: '';
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 100vh;
|
||||||
|
z-index: 250;
|
||||||
|
background: rgba(0, 0, 0, 0);
|
||||||
|
transition: background-color 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.burgerIcon {
|
||||||
|
display: none;
|
||||||
|
align-self: center;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.drawerOpen .navigation {
|
||||||
|
left: 0%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.drawerOpen .backdrop {
|
||||||
|
bottom: 0vh;
|
||||||
|
background: rgba(0, 0, 0, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.programIcon {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: 992px) {
|
||||||
|
.header {
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 0 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.burgerIcon {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.programIcon {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navigation {
|
||||||
|
padding: 1rem 0;
|
||||||
|
flex-flow: column;
|
||||||
|
position: fixed;
|
||||||
|
z-index: 300;
|
||||||
|
align-items: stretch;
|
||||||
|
top: 0;
|
||||||
|
max-width: 80vw;
|
||||||
|
width: 20rem;
|
||||||
|
left: -100%;
|
||||||
|
bottom: 0;
|
||||||
|
background: var(--bg-0);
|
||||||
|
box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.25);
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
|
||||||
|
a {
|
||||||
|
padding: 0.5rem;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spacer:first-of-type {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.socialLinks {
|
||||||
|
justify-content: space-around;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.drawerOpen {
|
||||||
|
left: 0%;
|
||||||
|
}
|
||||||
|
}
|
||||||
38
next/components/header/social-icon/index.jsx
Normal file
38
next/components/header/social-icon/index.jsx
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
export default function SocialIcon(props) {
|
||||||
|
if (props.name === 'youtube')
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="36" height="36" viewBox="0 0 24 24"
|
||||||
|
fill="none" stroke="currentColor"
|
||||||
|
strokeWidth="1" strokeLinecap="round" strokeLinejoin="round"
|
||||||
|
>
|
||||||
|
<path d="M22.54 6.42a2.78 2.78 0 0 0-1.94-2C18.88 4 12 4 12 4s-6.88 0-8.6.46a2.78 2.78 0 0 0-1.94 2A29 29 0 0 0 1 11.75a29 29 0 0 0 .46 5.33A2.78 2.78 0 0 0 3.4 19c1.72.46 8.6.46 8.6.46s6.88 0 8.6-.46a2.78 2.78 0 0 0 1.94-2 29 29 0 0 0 .46-5.25 29 29 0 0 0-.46-5.33z" />
|
||||||
|
<polygon points="9.75 15.02 15.5 11.75 9.75 8.48 9.75 15.02" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
else if (props.name === 'instagram')
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="36" height="36" viewBox="0 0 24 24"
|
||||||
|
fill="none" stroke="currentColor"
|
||||||
|
strokeWidth="1" strokeLinecap="round" strokeLinejoin="round"
|
||||||
|
>
|
||||||
|
<rect x="2" y="2" width="20" height="20" rx="5" ry="5" />
|
||||||
|
<path d="M16 11.37A4 4 0 1 1 12.63 8 4 4 0 0 1 16 11.37z" />
|
||||||
|
<line x1="17.5" y1="6.5" x2="17.51" y2="6.5" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
else if (props.name === 'facebook')
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="36" height="36" viewBox="0 0 24 24"
|
||||||
|
fill="none" stroke="currentColor"
|
||||||
|
strokeWidth="1" strokeLinecap="round" strokeLinejoin="round"
|
||||||
|
>
|
||||||
|
<path d="M18 2h-3a5 5 0 0 0-5 5v3H7v4h3v8h4v-8h3l1-4h-4V7a1 1 0 0 1 1-1h3z" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
0
next/components/header/style.module.scss
Normal file
0
next/components/header/style.module.scss
Normal file
33
next/components/imageBlock/index.jsx
Normal file
33
next/components/imageBlock/index.jsx
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
'use client';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import styles from './style.module.scss';
|
||||||
|
import ImagePopup from '../imagePopup';
|
||||||
|
|
||||||
|
export default function ImageBlock({alt, src, ...props}) {
|
||||||
|
const [showPopup, setShowPopup] = useState(false);
|
||||||
|
|
||||||
|
const toggleShowPopup = () => {
|
||||||
|
setShowPopup(prev => !prev);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<img
|
||||||
|
{...props}
|
||||||
|
onClick={toggleShowPopup}
|
||||||
|
src={src}
|
||||||
|
alt={alt}
|
||||||
|
className={`${styles.imageBlock} ${props.className}`}
|
||||||
|
/>
|
||||||
|
{
|
||||||
|
showPopup ?
|
||||||
|
<ImagePopup
|
||||||
|
toggleShowPopup={toggleShowPopup}
|
||||||
|
alt={alt}
|
||||||
|
src={src}
|
||||||
|
/>
|
||||||
|
: null
|
||||||
|
}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
4
next/components/imageBlock/style.module.scss
Normal file
4
next/components/imageBlock/style.module.scss
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
.imageBlock {
|
||||||
|
cursor: pointer;
|
||||||
|
max-height: 100%;
|
||||||
|
}
|
||||||
32
next/components/imagePopup/index.jsx
Normal file
32
next/components/imagePopup/index.jsx
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import styles from './style.module.scss';
|
||||||
|
|
||||||
|
export default function ImagePopup({toggleShowPopup, src, alt, ...props}) {
|
||||||
|
return (
|
||||||
|
<div className={styles.modal}>
|
||||||
|
<button onClick={toggleShowPopup} className={styles.close}>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="36"
|
||||||
|
height="36"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="1"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
className="feather feather-x"
|
||||||
|
>
|
||||||
|
<line x1="18" y1="6" x2="6" y2="18" />
|
||||||
|
<line x1="6" y1="6" x2="18" y2="18" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<div onClick={toggleShowPopup} className={styles.imgModal} />
|
||||||
|
<img
|
||||||
|
{...props}
|
||||||
|
src={src}
|
||||||
|
alt={alt}
|
||||||
|
className={styles.popupImage}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
92
next/components/imagePopup/style.module.scss
Normal file
92
next/components/imagePopup/style.module.scss
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
.modal {
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 220;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
overflow: scroll;
|
||||||
|
overscroll-behavior: contain;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
& > div {
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
|
||||||
|
background-color: var(--fg-0);
|
||||||
|
opacity: 0.65;
|
||||||
|
z-index: 220;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.popupImage {
|
||||||
|
margin: auto;
|
||||||
|
display: block;
|
||||||
|
max-width: 80vw;
|
||||||
|
max-height: 80vh;
|
||||||
|
animation-name: zoom;
|
||||||
|
animation-duration: 0.6s;
|
||||||
|
animation-fill-mode: both;
|
||||||
|
z-index: 230;
|
||||||
|
cursor: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close {
|
||||||
|
position: fixed;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
top: 3.6rem;
|
||||||
|
right: 4rem;
|
||||||
|
height: 3rem;
|
||||||
|
width: 3rem;
|
||||||
|
padding: 0.25rem;
|
||||||
|
|
||||||
|
appearance: none;
|
||||||
|
border: unset;
|
||||||
|
font-size: unset;
|
||||||
|
font-family: unset;
|
||||||
|
|
||||||
|
color: var(--bg-0);
|
||||||
|
background: var(--fg-0-translucid);
|
||||||
|
font-weight: bold;
|
||||||
|
transition: 0.3s;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 100%;
|
||||||
|
z-index: 230;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
height: 3rem;
|
||||||
|
width: 3rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.close:hover {
|
||||||
|
top: 3.2rem;
|
||||||
|
right: 3.6rem;
|
||||||
|
height: 3.8rem;
|
||||||
|
width: 3.8rem;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
height: 3.8rem;
|
||||||
|
width: 3.8rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes zoom {
|
||||||
|
from {
|
||||||
|
transform:scale(0);
|
||||||
|
box-shadow: 0 0 0.2rem rgba(0,0,0,0.5);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform:scale(1);
|
||||||
|
box-shadow: 0 0 1rem rgba(0,0,0,0.5);
|
||||||
|
}
|
||||||
|
}
|
||||||
25
next/components/pressBlock/index.jsx
Normal file
25
next/components/pressBlock/index.jsx
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import ArticleBlock from "../articleBlock";
|
||||||
|
import ImageBlock from "../imageBlock";
|
||||||
|
import styles from "./style.module.scss";
|
||||||
|
|
||||||
|
export default function PressBlock({
|
||||||
|
left: {type: leftType, ...left},
|
||||||
|
right: {type: rightType, ...right},
|
||||||
|
...props
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<section {...props} className={styles.pressBlock}>
|
||||||
|
{
|
||||||
|
leftType ? leftType === 'article' ?
|
||||||
|
<ArticleBlock {...left} className={styles.pressArticle}/> :
|
||||||
|
<ImageBlock {...left} className={styles.pressImage}/> : null
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
rightType ? rightType === 'article' ?
|
||||||
|
<ArticleBlock {...right} className={styles.pressArticle}/> :
|
||||||
|
<ImageBlock {...right} className={styles.pressImage}/> : null
|
||||||
|
}
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
36
next/components/pressBlock/style.module.scss
Normal file
36
next/components/pressBlock/style.module.scss
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
.pressBlock {
|
||||||
|
--element-width: 30vw;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
.pressArticle, .pressImage {
|
||||||
|
width: var(--element-width);
|
||||||
|
margin: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pressImage {
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:nth-of-type(2n+1) {
|
||||||
|
flex-flow: row-reverse;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: 80rem) {
|
||||||
|
.pressBlock {
|
||||||
|
--element-width: 45vw;
|
||||||
|
|
||||||
|
.pressImage {
|
||||||
|
width: calc(var(--element-width) - 10vw);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: 60rem) {
|
||||||
|
.pressBlock {
|
||||||
|
--element-width: 90vw;
|
||||||
|
&, &:nth-child(2n) {
|
||||||
|
flex-flow: column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
10
next/components/statBlock/index.jsx
Normal file
10
next/components/statBlock/index.jsx
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import styles from './style.module.scss';
|
||||||
|
|
||||||
|
export default async function StatBlock(props) {
|
||||||
|
return (
|
||||||
|
<a href={props.href} className={styles.statBox}>
|
||||||
|
<span>{props.value}</span>
|
||||||
|
<h3>{props.title}</h3>
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
}
|
||||||
33
next/components/statBlock/style.module.scss
Normal file
33
next/components/statBlock/style.module.scss
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
.statBox {
|
||||||
|
width: 100%;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: none;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
font-size: 4rem;
|
||||||
|
color: var(--fg-0);
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
padding: 0px;
|
||||||
|
margin: 0px;
|
||||||
|
color: var(--fg-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: 30rem) {
|
||||||
|
.statBox {
|
||||||
|
span {
|
||||||
|
font-size: 2.2rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
23
next/components/timeSlot/index.jsx
Normal file
23
next/components/timeSlot/index.jsx
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { BlocksRenderer } from '@strapi/blocks-react-renderer';
|
||||||
|
import styles from './style.module.scss';
|
||||||
|
|
||||||
|
export default async function TimeSlot(props) {
|
||||||
|
const date_begin = new Date(props.start)
|
||||||
|
const date_end = new Date(props.end)
|
||||||
|
const date_format = new Intl.DateTimeFormat('fr', {
|
||||||
|
weekday: 'long',
|
||||||
|
month: "long",
|
||||||
|
day: "numeric"
|
||||||
|
}).format(date_begin)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<article className={styles.timeSlot}>
|
||||||
|
<h4>
|
||||||
|
{date_format} - {date_begin.getHours()}h
|
||||||
|
{date_begin.getMinutes() !== 0 ? date_begin.getMinutes() : ""} à
|
||||||
|
{date_end.getHours()}h{date_end.getMinutes() !== 0 ? date_end.getMinutes() : ""}
|
||||||
|
</h4>
|
||||||
|
<BlocksRenderer content={props.content}/>
|
||||||
|
</article>
|
||||||
|
);
|
||||||
|
}
|
||||||
17
next/components/timeSlot/style.module.scss
Normal file
17
next/components/timeSlot/style.module.scss
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
.timeSlot {
|
||||||
|
h4::first-letter {
|
||||||
|
text-transform: capitalize;
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
font-family: var(--font-details);
|
||||||
|
margin: 0px;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: 400;
|
||||||
|
color: var(--fefan-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
padding-right: 1rem;
|
||||||
|
padding-left: 1rem;
|
||||||
|
}
|
||||||
15
next/components/videoBlock/index.jsx
Normal file
15
next/components/videoBlock/index.jsx
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import styles from "./style.module.scss";
|
||||||
|
|
||||||
|
export default async function VideoBlock(props) {
|
||||||
|
return (
|
||||||
|
<iframe
|
||||||
|
className={`${props.className} ${styles.videoBlock}`}
|
||||||
|
width={ props.mode ==='thumbnail' ? "220" : "560" }
|
||||||
|
height={props.mode ==='thumbnail' ? "220" : "315"}
|
||||||
|
src={props.src}
|
||||||
|
title={props.title}
|
||||||
|
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
|
||||||
|
allowFullScreen
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
3
next/components/videoBlock/style.module.scss
Normal file
3
next/components/videoBlock/style.module.scss
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
.videoBlock {
|
||||||
|
max-width: 90vw;
|
||||||
|
}
|
||||||
7
next/jsconfig.json
Normal file
7
next/jsconfig.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["./*"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
6
next/next.config.js
Normal file
6
next/next.config.js
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
/** @type {import('next').NextConfig} */
|
||||||
|
const nextConfig = {
|
||||||
|
output: 'standalone',
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = nextConfig
|
||||||
23
next/package.json
Normal file
23
next/package.json
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "fefan-next",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"dev": "next dev",
|
||||||
|
"build": "next build",
|
||||||
|
"start": "next start",
|
||||||
|
"lint": "next lint"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@strapi/blocks-react-renderer": "^1.0.0",
|
||||||
|
"next": "14.0.0",
|
||||||
|
"qs": "^6.11.2",
|
||||||
|
"react": "^18",
|
||||||
|
"react-dom": "^18",
|
||||||
|
"sass": "^1.69.5"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"eslint": "^8",
|
||||||
|
"eslint-config-next": "14.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
next/public/fefan.png
Normal file
BIN
next/public/fefan.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 81 KiB |
2191
next/yarn.lock
Normal file
2191
next/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
113
strapi/.dockerignore
Normal file
113
strapi/.dockerignore
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
############################
|
||||||
|
# OS X
|
||||||
|
############################
|
||||||
|
|
||||||
|
.DS_Store
|
||||||
|
.AppleDouble
|
||||||
|
.LSOverride
|
||||||
|
Icon
|
||||||
|
.Spotlight-V100
|
||||||
|
.Trashes
|
||||||
|
._*
|
||||||
|
|
||||||
|
|
||||||
|
############################
|
||||||
|
# Linux
|
||||||
|
############################
|
||||||
|
|
||||||
|
*~
|
||||||
|
|
||||||
|
|
||||||
|
############################
|
||||||
|
# Windows
|
||||||
|
############################
|
||||||
|
|
||||||
|
Thumbs.db
|
||||||
|
ehthumbs.db
|
||||||
|
Desktop.ini
|
||||||
|
$RECYCLE.BIN/
|
||||||
|
*.cab
|
||||||
|
*.msi
|
||||||
|
*.msm
|
||||||
|
*.msp
|
||||||
|
|
||||||
|
|
||||||
|
############################
|
||||||
|
# Packages
|
||||||
|
############################
|
||||||
|
|
||||||
|
*.7z
|
||||||
|
*.csv
|
||||||
|
*.dat
|
||||||
|
*.dmg
|
||||||
|
*.gz
|
||||||
|
*.iso
|
||||||
|
*.jar
|
||||||
|
*.rar
|
||||||
|
*.tar
|
||||||
|
*.zip
|
||||||
|
*.com
|
||||||
|
*.class
|
||||||
|
*.dll
|
||||||
|
*.exe
|
||||||
|
*.o
|
||||||
|
*.seed
|
||||||
|
*.so
|
||||||
|
*.swo
|
||||||
|
*.swp
|
||||||
|
*.swn
|
||||||
|
*.swm
|
||||||
|
*.out
|
||||||
|
*.pid
|
||||||
|
|
||||||
|
|
||||||
|
############################
|
||||||
|
# Logs and databases
|
||||||
|
############################
|
||||||
|
|
||||||
|
.tmp
|
||||||
|
*.log
|
||||||
|
*.sql
|
||||||
|
*.sqlite
|
||||||
|
*.sqlite3
|
||||||
|
|
||||||
|
|
||||||
|
############################
|
||||||
|
# Misc.
|
||||||
|
############################
|
||||||
|
|
||||||
|
*#
|
||||||
|
ssl
|
||||||
|
.idea
|
||||||
|
nbproject
|
||||||
|
public/uploads/*
|
||||||
|
!public/uploads/.gitkeep
|
||||||
|
|
||||||
|
############################
|
||||||
|
# Node.js
|
||||||
|
############################
|
||||||
|
|
||||||
|
lib-cov
|
||||||
|
lcov.info
|
||||||
|
pids
|
||||||
|
logs
|
||||||
|
results
|
||||||
|
node_modules
|
||||||
|
.node_history
|
||||||
|
|
||||||
|
############################
|
||||||
|
# Tests
|
||||||
|
############################
|
||||||
|
|
||||||
|
coverage
|
||||||
|
|
||||||
|
############################
|
||||||
|
# Strapi
|
||||||
|
############################
|
||||||
|
|
||||||
|
license.txt
|
||||||
|
exports
|
||||||
|
*.cache
|
||||||
|
dist
|
||||||
|
build
|
||||||
|
.strapi-updater.json
|
||||||
16
strapi/.editorconfig
Normal file
16
strapi/.editorconfig
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
end_of_line = lf
|
||||||
|
charset = utf-8
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
|
[{package.json,*.yml}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
[*.md]
|
||||||
|
trim_trailing_whitespace = false
|
||||||
7
strapi/.env.example
Normal file
7
strapi/.env.example
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
HOST=0.0.0.0
|
||||||
|
PORT=1337
|
||||||
|
APP_KEYS="toBeModified1,toBeModified2"
|
||||||
|
API_TOKEN_SALT=tobemodified
|
||||||
|
ADMIN_JWT_SECRET=tobemodified
|
||||||
|
TRANSFER_TOKEN_SALT=tobemodified
|
||||||
|
JWT_SECRET=tobemodified
|
||||||
3
strapi/.eslintignore
Normal file
3
strapi/.eslintignore
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
.cache
|
||||||
|
build
|
||||||
|
**/node_modules/**
|
||||||
27
strapi/.eslintrc
Normal file
27
strapi/.eslintrc
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"parser": "babel-eslint",
|
||||||
|
"extends": "eslint:recommended",
|
||||||
|
"env": {
|
||||||
|
"commonjs": true,
|
||||||
|
"es6": true,
|
||||||
|
"node": true,
|
||||||
|
"browser": false
|
||||||
|
},
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaFeatures": {
|
||||||
|
"experimentalObjectRestSpread": true,
|
||||||
|
"jsx": false
|
||||||
|
},
|
||||||
|
"sourceType": "module"
|
||||||
|
},
|
||||||
|
"globals": {
|
||||||
|
"strapi": true
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"indent": ["error", 2, { "SwitchCase": 1 }],
|
||||||
|
"linebreak-style": ["error", "unix"],
|
||||||
|
"no-console": 0,
|
||||||
|
"quotes": ["error", "single"],
|
||||||
|
"semi": ["error", "always"]
|
||||||
|
}
|
||||||
|
}
|
||||||
114
strapi/.gitignore
vendored
Normal file
114
strapi/.gitignore
vendored
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
############################
|
||||||
|
# OS X
|
||||||
|
############################
|
||||||
|
|
||||||
|
.DS_Store
|
||||||
|
.AppleDouble
|
||||||
|
.LSOverride
|
||||||
|
Icon
|
||||||
|
.Spotlight-V100
|
||||||
|
.Trashes
|
||||||
|
._*
|
||||||
|
|
||||||
|
|
||||||
|
############################
|
||||||
|
# Linux
|
||||||
|
############################
|
||||||
|
|
||||||
|
*~
|
||||||
|
|
||||||
|
|
||||||
|
############################
|
||||||
|
# Windows
|
||||||
|
############################
|
||||||
|
|
||||||
|
Thumbs.db
|
||||||
|
ehthumbs.db
|
||||||
|
Desktop.ini
|
||||||
|
$RECYCLE.BIN/
|
||||||
|
*.cab
|
||||||
|
*.msi
|
||||||
|
*.msm
|
||||||
|
*.msp
|
||||||
|
|
||||||
|
|
||||||
|
############################
|
||||||
|
# Packages
|
||||||
|
############################
|
||||||
|
|
||||||
|
*.7z
|
||||||
|
*.csv
|
||||||
|
*.dat
|
||||||
|
*.dmg
|
||||||
|
*.gz
|
||||||
|
*.iso
|
||||||
|
*.jar
|
||||||
|
*.rar
|
||||||
|
*.tar
|
||||||
|
*.zip
|
||||||
|
*.com
|
||||||
|
*.class
|
||||||
|
*.dll
|
||||||
|
*.exe
|
||||||
|
*.o
|
||||||
|
*.seed
|
||||||
|
*.so
|
||||||
|
*.swo
|
||||||
|
*.swp
|
||||||
|
*.swn
|
||||||
|
*.swm
|
||||||
|
*.out
|
||||||
|
*.pid
|
||||||
|
|
||||||
|
|
||||||
|
############################
|
||||||
|
# Logs and databases
|
||||||
|
############################
|
||||||
|
|
||||||
|
.tmp
|
||||||
|
*.log
|
||||||
|
*.sql
|
||||||
|
*.sqlite
|
||||||
|
*.sqlite3
|
||||||
|
|
||||||
|
|
||||||
|
############################
|
||||||
|
# Misc.
|
||||||
|
############################
|
||||||
|
|
||||||
|
*#
|
||||||
|
ssl
|
||||||
|
.idea
|
||||||
|
nbproject
|
||||||
|
public/uploads/*
|
||||||
|
!public/uploads/.gitkeep
|
||||||
|
|
||||||
|
############################
|
||||||
|
# Node.js
|
||||||
|
############################
|
||||||
|
|
||||||
|
lib-cov
|
||||||
|
lcov.info
|
||||||
|
pids
|
||||||
|
logs
|
||||||
|
results
|
||||||
|
node_modules
|
||||||
|
.node_history
|
||||||
|
|
||||||
|
############################
|
||||||
|
# Tests
|
||||||
|
############################
|
||||||
|
|
||||||
|
coverage
|
||||||
|
|
||||||
|
############################
|
||||||
|
# Strapi
|
||||||
|
############################
|
||||||
|
|
||||||
|
.env
|
||||||
|
license.txt
|
||||||
|
exports
|
||||||
|
*.cache
|
||||||
|
dist
|
||||||
|
build
|
||||||
|
.strapi-updater.json
|
||||||
11
strapi/Dockerfile
Normal file
11
strapi/Dockerfile
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
FROM node:20.10-alpine AS builder
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
COPY package.json yarn.lock ./
|
||||||
|
RUN yarn
|
||||||
|
|
||||||
|
COPY ./ ./
|
||||||
|
RUN yarn build
|
||||||
|
|
||||||
|
ENTRYPOINT ["yarn", "strapi"]
|
||||||
|
CMD ["start"]
|
||||||
57
strapi/README.md
Normal file
57
strapi/README.md
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
# 🚀 Getting started with Strapi
|
||||||
|
|
||||||
|
Strapi comes with a full featured [Command Line Interface](https://docs.strapi.io/dev-docs/cli) (CLI) which lets you scaffold and manage your project in seconds.
|
||||||
|
|
||||||
|
### `develop`
|
||||||
|
|
||||||
|
Start your Strapi application with autoReload enabled. [Learn more](https://docs.strapi.io/dev-docs/cli#strapi-develop)
|
||||||
|
|
||||||
|
```
|
||||||
|
npm run develop
|
||||||
|
# or
|
||||||
|
yarn develop
|
||||||
|
```
|
||||||
|
|
||||||
|
### `start`
|
||||||
|
|
||||||
|
Start your Strapi application with autoReload disabled. [Learn more](https://docs.strapi.io/dev-docs/cli#strapi-start)
|
||||||
|
|
||||||
|
```
|
||||||
|
npm run start
|
||||||
|
# or
|
||||||
|
yarn start
|
||||||
|
```
|
||||||
|
|
||||||
|
### `build`
|
||||||
|
|
||||||
|
Build your admin panel. [Learn more](https://docs.strapi.io/dev-docs/cli#strapi-build)
|
||||||
|
|
||||||
|
```
|
||||||
|
npm run build
|
||||||
|
# or
|
||||||
|
yarn build
|
||||||
|
```
|
||||||
|
|
||||||
|
## ⚙️ Deployment
|
||||||
|
|
||||||
|
Strapi gives you many possible deployment options for your project including [Strapi Cloud](https://cloud.strapi.io). Browse the [deployment section of the documentation](https://docs.strapi.io/dev-docs/deployment) to find the best solution for your use case.
|
||||||
|
|
||||||
|
## 📚 Learn more
|
||||||
|
|
||||||
|
- [Resource center](https://strapi.io/resource-center) - Strapi resource center.
|
||||||
|
- [Strapi documentation](https://docs.strapi.io) - Official Strapi documentation.
|
||||||
|
- [Strapi tutorials](https://strapi.io/tutorials) - List of tutorials made by the core team and the community.
|
||||||
|
- [Strapi blog](https://strapi.io/blog) - Official Strapi blog containing articles made by the Strapi team and the community.
|
||||||
|
- [Changelog](https://strapi.io/changelog) - Find out about the Strapi product updates, new features and general improvements.
|
||||||
|
|
||||||
|
Feel free to check out the [Strapi GitHub repository](https://github.com/strapi/strapi). Your feedback and contributions are welcome!
|
||||||
|
|
||||||
|
## ✨ Community
|
||||||
|
|
||||||
|
- [Discord](https://discord.strapi.io) - Come chat with the Strapi community including the core team.
|
||||||
|
- [Forum](https://forum.strapi.io/) - Place to discuss, ask questions and find answers, show your Strapi project and get feedback or just talk with other Community members.
|
||||||
|
- [Awesome Strapi](https://github.com/strapi/awesome-strapi) - A curated list of awesome things related to Strapi.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<sub>🤫 Psst! [Strapi is hiring](https://strapi.io/careers).</sub>
|
||||||
17
strapi/config/admin.js
Normal file
17
strapi/config/admin.js
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
module.exports = ({ env }) => ({
|
||||||
|
auth: {
|
||||||
|
secret: env('ADMIN_JWT_SECRET'),
|
||||||
|
},
|
||||||
|
apiToken: {
|
||||||
|
salt: env('API_TOKEN_SALT'),
|
||||||
|
},
|
||||||
|
transfer: {
|
||||||
|
token: {
|
||||||
|
salt: env('TRANSFER_TOKEN_SALT'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
nps: env.bool('FLAG_NPS', true),
|
||||||
|
promoteEE: env.bool('FLAG_PROMOTE_EE', true),
|
||||||
|
},
|
||||||
|
});
|
||||||
7
strapi/config/api.js
Normal file
7
strapi/config/api.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
module.exports = {
|
||||||
|
rest: {
|
||||||
|
defaultLimit: 25,
|
||||||
|
maxLimit: 100,
|
||||||
|
withCount: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
92
strapi/config/database.js
Normal file
92
strapi/config/database.js
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
module.exports = ({ env }) => {
|
||||||
|
const client = env('DATABASE_CLIENT', 'sqlite');
|
||||||
|
|
||||||
|
const connections = {
|
||||||
|
mysql: {
|
||||||
|
connection: {
|
||||||
|
connectionString: env('DATABASE_URL'),
|
||||||
|
host: env('DATABASE_HOST', 'localhost'),
|
||||||
|
port: env.int('DATABASE_PORT', 3306),
|
||||||
|
database: env('DATABASE_NAME', 'strapi'),
|
||||||
|
user: env('DATABASE_USERNAME', 'strapi'),
|
||||||
|
password: env('DATABASE_PASSWORD', 'strapi'),
|
||||||
|
ssl: env.bool('DATABASE_SSL', false) && {
|
||||||
|
key: env('DATABASE_SSL_KEY', undefined),
|
||||||
|
cert: env('DATABASE_SSL_CERT', undefined),
|
||||||
|
ca: env('DATABASE_SSL_CA', undefined),
|
||||||
|
capath: env('DATABASE_SSL_CAPATH', undefined),
|
||||||
|
cipher: env('DATABASE_SSL_CIPHER', undefined),
|
||||||
|
rejectUnauthorized: env.bool(
|
||||||
|
'DATABASE_SSL_REJECT_UNAUTHORIZED',
|
||||||
|
true
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pool: { min: env.int('DATABASE_POOL_MIN', 2), max: env.int('DATABASE_POOL_MAX', 10) },
|
||||||
|
},
|
||||||
|
mysql2: {
|
||||||
|
connection: {
|
||||||
|
host: env('DATABASE_HOST', 'localhost'),
|
||||||
|
port: env.int('DATABASE_PORT', 3306),
|
||||||
|
database: env('DATABASE_NAME', 'strapi'),
|
||||||
|
user: env('DATABASE_USERNAME', 'strapi'),
|
||||||
|
password: env('DATABASE_PASSWORD', 'strapi'),
|
||||||
|
ssl: env.bool('DATABASE_SSL', false) && {
|
||||||
|
key: env('DATABASE_SSL_KEY', undefined),
|
||||||
|
cert: env('DATABASE_SSL_CERT', undefined),
|
||||||
|
ca: env('DATABASE_SSL_CA', undefined),
|
||||||
|
capath: env('DATABASE_SSL_CAPATH', undefined),
|
||||||
|
cipher: env('DATABASE_SSL_CIPHER', undefined),
|
||||||
|
rejectUnauthorized: env.bool(
|
||||||
|
'DATABASE_SSL_REJECT_UNAUTHORIZED',
|
||||||
|
true
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pool: { min: env.int('DATABASE_POOL_MIN', 2), max: env.int('DATABASE_POOL_MAX', 10) },
|
||||||
|
},
|
||||||
|
postgres: {
|
||||||
|
connection: {
|
||||||
|
connectionString: env('DATABASE_URL'),
|
||||||
|
host: env('DATABASE_HOST', 'localhost'),
|
||||||
|
port: env.int('DATABASE_PORT', 5432),
|
||||||
|
database: env('DATABASE_NAME', 'strapi'),
|
||||||
|
user: env('DATABASE_USERNAME', 'strapi'),
|
||||||
|
password: env('DATABASE_PASSWORD', 'strapi'),
|
||||||
|
ssl: env.bool('DATABASE_SSL', false) && {
|
||||||
|
key: env('DATABASE_SSL_KEY', undefined),
|
||||||
|
cert: env('DATABASE_SSL_CERT', undefined),
|
||||||
|
ca: env('DATABASE_SSL_CA', undefined),
|
||||||
|
capath: env('DATABASE_SSL_CAPATH', undefined),
|
||||||
|
cipher: env('DATABASE_SSL_CIPHER', undefined),
|
||||||
|
rejectUnauthorized: env.bool(
|
||||||
|
'DATABASE_SSL_REJECT_UNAUTHORIZED',
|
||||||
|
true
|
||||||
|
),
|
||||||
|
},
|
||||||
|
schema: env('DATABASE_SCHEMA', 'public'),
|
||||||
|
},
|
||||||
|
pool: { min: env.int('DATABASE_POOL_MIN', 2), max: env.int('DATABASE_POOL_MAX', 10) },
|
||||||
|
},
|
||||||
|
sqlite: {
|
||||||
|
connection: {
|
||||||
|
filename: path.join(
|
||||||
|
__dirname,
|
||||||
|
'..',
|
||||||
|
env('DATABASE_FILENAME', '.tmp/data.db')
|
||||||
|
),
|
||||||
|
},
|
||||||
|
useNullAsDefault: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
connection: {
|
||||||
|
client,
|
||||||
|
...connections[client],
|
||||||
|
acquireConnectionTimeout: env.int('DATABASE_CONNECTION_TIMEOUT', 60000),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
12
strapi/config/middlewares.js
Normal file
12
strapi/config/middlewares.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
module.exports = [
|
||||||
|
'strapi::errors',
|
||||||
|
'strapi::security',
|
||||||
|
'strapi::cors',
|
||||||
|
'strapi::poweredBy',
|
||||||
|
'strapi::logger',
|
||||||
|
'strapi::query',
|
||||||
|
'strapi::body',
|
||||||
|
'strapi::session',
|
||||||
|
'strapi::favicon',
|
||||||
|
'strapi::public',
|
||||||
|
];
|
||||||
10
strapi/config/server.js
Normal file
10
strapi/config/server.js
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
module.exports = ({ env }) => ({
|
||||||
|
host: env('HOST', '0.0.0.0'),
|
||||||
|
port: env.int('PORT', 1337),
|
||||||
|
app: {
|
||||||
|
keys: env.array('APP_KEYS'),
|
||||||
|
},
|
||||||
|
webhooks: {
|
||||||
|
populateRelations: env.bool('WEBHOOKS_POPULATE_RELATIONS', false),
|
||||||
|
},
|
||||||
|
});
|
||||||
0
strapi/database/migrations/.gitkeep
Normal file
0
strapi/database/migrations/.gitkeep
Normal file
BIN
strapi/favicon.png
Normal file
BIN
strapi/favicon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 497 B |
8
strapi/jsconfig.json
Normal file
8
strapi/jsconfig.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"moduleResolution": "nodenext",
|
||||||
|
"target": "ES2021",
|
||||||
|
"checkJs": true,
|
||||||
|
"allowJs": true
|
||||||
|
}
|
||||||
|
}
|
||||||
31
strapi/package.json
Normal file
31
strapi/package.json
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"name": "fefan-strapi",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "A Strapi application",
|
||||||
|
"scripts": {
|
||||||
|
"develop": "strapi develop",
|
||||||
|
"start": "strapi start",
|
||||||
|
"build": "strapi build",
|
||||||
|
"strapi": "strapi"
|
||||||
|
},
|
||||||
|
"devDependencies": {},
|
||||||
|
"dependencies": {
|
||||||
|
"@strapi/plugin-i18n": "4.15.0",
|
||||||
|
"@strapi/plugin-users-permissions": "4.15.0",
|
||||||
|
"@strapi/strapi": "4.15.0",
|
||||||
|
"better-sqlite3": "8.6.0",
|
||||||
|
"pg": "^8.11.3"
|
||||||
|
},
|
||||||
|
"author": {
|
||||||
|
"name": "A Strapi developer"
|
||||||
|
},
|
||||||
|
"strapi": {
|
||||||
|
"uuid": "3e9d2b31-e51d-464f-94f1-ab73e28fd712"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.0.0 <=20.x.x",
|
||||||
|
"npm": ">=6.0.0"
|
||||||
|
},
|
||||||
|
"license": "MIT"
|
||||||
|
}
|
||||||
3
strapi/public/robots.txt
Normal file
3
strapi/public/robots.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# To prevent search engines from seeing the site altogether, uncomment the next two lines:
|
||||||
|
# User-Agent: *
|
||||||
|
# Disallow: /
|
||||||
0
strapi/public/uploads/.gitkeep
Normal file
0
strapi/public/uploads/.gitkeep
Normal file
2
strapi/server.js
Normal file
2
strapi/server.js
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
import strapi from '@strapi/strapi';
|
||||||
|
strapi().start();
|
||||||
39
strapi/src/admin/app.example.js
Normal file
39
strapi/src/admin/app.example.js
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
const config = {
|
||||||
|
locales: [
|
||||||
|
// 'ar',
|
||||||
|
// 'fr',
|
||||||
|
// 'cs',
|
||||||
|
// 'de',
|
||||||
|
// 'dk',
|
||||||
|
// 'es',
|
||||||
|
// 'he',
|
||||||
|
// 'id',
|
||||||
|
// 'it',
|
||||||
|
// 'ja',
|
||||||
|
// 'ko',
|
||||||
|
// 'ms',
|
||||||
|
// 'nl',
|
||||||
|
// 'no',
|
||||||
|
// 'pl',
|
||||||
|
// 'pt-BR',
|
||||||
|
// 'pt',
|
||||||
|
// 'ru',
|
||||||
|
// 'sk',
|
||||||
|
// 'sv',
|
||||||
|
// 'th',
|
||||||
|
// 'tr',
|
||||||
|
// 'uk',
|
||||||
|
// 'vi',
|
||||||
|
// 'zh-Hans',
|
||||||
|
// 'zh',
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const bootstrap = (app) => {
|
||||||
|
console.log(app);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
config,
|
||||||
|
bootstrap,
|
||||||
|
};
|
||||||
9
strapi/src/admin/webpack.config.example.js
Normal file
9
strapi/src/admin/webpack.config.example.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
/* eslint-disable no-unused-vars */
|
||||||
|
module.exports = (config, webpack) => {
|
||||||
|
// Note: we provide webpack above so you should not `require` it
|
||||||
|
// Perform customizations to webpack config
|
||||||
|
// Important: return the modified config
|
||||||
|
return config;
|
||||||
|
};
|
||||||
0
strapi/src/api/.gitkeep
Normal file
0
strapi/src/api/.gitkeep
Normal file
32
strapi/src/api/article/content-types/article/schema.json
Normal file
32
strapi/src/api/article/content-types/article/schema.json
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"kind": "collectionType",
|
||||||
|
"collectionName": "articles",
|
||||||
|
"info": {
|
||||||
|
"singularName": "article",
|
||||||
|
"pluralName": "articles",
|
||||||
|
"displayName": "Article",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"draftAndPublish": true
|
||||||
|
},
|
||||||
|
"pluginOptions": {},
|
||||||
|
"attributes": {
|
||||||
|
"title": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"link": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"edition": {
|
||||||
|
"type": "relation",
|
||||||
|
"relation": "manyToOne",
|
||||||
|
"target": "api::edition.edition",
|
||||||
|
"inversedBy": "articles"
|
||||||
|
},
|
||||||
|
"excerpt": {
|
||||||
|
"type": "blocks",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
9
strapi/src/api/article/controllers/article.js
Normal file
9
strapi/src/api/article/controllers/article.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* article controller
|
||||||
|
*/
|
||||||
|
|
||||||
|
const { createCoreController } = require('@strapi/strapi').factories;
|
||||||
|
|
||||||
|
module.exports = createCoreController('api::article.article');
|
||||||
9
strapi/src/api/article/routes/article.js
Normal file
9
strapi/src/api/article/routes/article.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* article router
|
||||||
|
*/
|
||||||
|
|
||||||
|
const { createCoreRouter } = require('@strapi/strapi').factories;
|
||||||
|
|
||||||
|
module.exports = createCoreRouter('api::article.article');
|
||||||
9
strapi/src/api/article/services/article.js
Normal file
9
strapi/src/api/article/services/article.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* article service
|
||||||
|
*/
|
||||||
|
|
||||||
|
const { createCoreService } = require('@strapi/strapi').factories;
|
||||||
|
|
||||||
|
module.exports = createCoreService('api::article.article');
|
||||||
37
strapi/src/api/band/content-types/band/schema.json
Normal file
37
strapi/src/api/band/content-types/band/schema.json
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
{
|
||||||
|
"kind": "collectionType",
|
||||||
|
"collectionName": "bands",
|
||||||
|
"info": {
|
||||||
|
"singularName": "band",
|
||||||
|
"pluralName": "bands",
|
||||||
|
"displayName": "Band"
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"draftAndPublish": true
|
||||||
|
},
|
||||||
|
"pluginOptions": {},
|
||||||
|
"attributes": {
|
||||||
|
"name": {
|
||||||
|
"type": "string",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"location": {
|
||||||
|
"type": "string",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"profile_picture": {
|
||||||
|
"allowedTypes": [
|
||||||
|
"images"
|
||||||
|
],
|
||||||
|
"type": "media",
|
||||||
|
"multiple": true,
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"programs": {
|
||||||
|
"type": "relation",
|
||||||
|
"relation": "manyToMany",
|
||||||
|
"target": "api::program.program",
|
||||||
|
"mappedBy": "bands"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
9
strapi/src/api/band/controllers/band.js
Normal file
9
strapi/src/api/band/controllers/band.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* band controller
|
||||||
|
*/
|
||||||
|
|
||||||
|
const { createCoreController } = require('@strapi/strapi').factories;
|
||||||
|
|
||||||
|
module.exports = createCoreController('api::band.band');
|
||||||
9
strapi/src/api/band/routes/band.js
Normal file
9
strapi/src/api/band/routes/band.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* band router
|
||||||
|
*/
|
||||||
|
|
||||||
|
const { createCoreRouter } = require('@strapi/strapi').factories;
|
||||||
|
|
||||||
|
module.exports = createCoreRouter('api::band.band');
|
||||||
9
strapi/src/api/band/services/band.js
Normal file
9
strapi/src/api/band/services/band.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* band service
|
||||||
|
*/
|
||||||
|
|
||||||
|
const { createCoreService } = require('@strapi/strapi').factories;
|
||||||
|
|
||||||
|
module.exports = createCoreService('api::band.band');
|
||||||
91
strapi/src/api/edition/content-types/edition/schema.json
Normal file
91
strapi/src/api/edition/content-types/edition/schema.json
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
{
|
||||||
|
"kind": "collectionType",
|
||||||
|
"collectionName": "editions",
|
||||||
|
"info": {
|
||||||
|
"singularName": "edition",
|
||||||
|
"pluralName": "editions",
|
||||||
|
"displayName": "Edition",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"draftAndPublish": true
|
||||||
|
},
|
||||||
|
"pluginOptions": {},
|
||||||
|
"attributes": {
|
||||||
|
"title": {
|
||||||
|
"type": "string",
|
||||||
|
"required": true,
|
||||||
|
"default": "Le FeFan"
|
||||||
|
},
|
||||||
|
"subtitle": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"introduction": {
|
||||||
|
"type": "blocks",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"flyer": {
|
||||||
|
"type": "media",
|
||||||
|
"multiple": false,
|
||||||
|
"required": true,
|
||||||
|
"allowedTypes": [
|
||||||
|
"images"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"gallery": {
|
||||||
|
"type": "media",
|
||||||
|
"multiple": true,
|
||||||
|
"required": true,
|
||||||
|
"allowedTypes": [
|
||||||
|
"images",
|
||||||
|
"videos"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"start": {
|
||||||
|
"type": "date",
|
||||||
|
"required": true,
|
||||||
|
"unique": true
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"type": "date",
|
||||||
|
"required": true,
|
||||||
|
"unique": true
|
||||||
|
},
|
||||||
|
"social_links": {
|
||||||
|
"type": "relation",
|
||||||
|
"relation": "oneToMany",
|
||||||
|
"target": "api::social-link.social-link",
|
||||||
|
"mappedBy": "edition"
|
||||||
|
},
|
||||||
|
"statistics": {
|
||||||
|
"type": "relation",
|
||||||
|
"relation": "oneToMany",
|
||||||
|
"target": "api::statistic.statistic",
|
||||||
|
"mappedBy": "edition"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"type": "text",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"programs": {
|
||||||
|
"type": "relation",
|
||||||
|
"relation": "oneToMany",
|
||||||
|
"target": "api::program.program",
|
||||||
|
"mappedBy": "edition"
|
||||||
|
},
|
||||||
|
"sponsors": {
|
||||||
|
"type": "relation",
|
||||||
|
"relation": "oneToMany",
|
||||||
|
"target": "api::sponsor.sponsor"
|
||||||
|
},
|
||||||
|
"articles": {
|
||||||
|
"type": "relation",
|
||||||
|
"relation": "oneToMany",
|
||||||
|
"target": "api::article.article",
|
||||||
|
"mappedBy": "edition"
|
||||||
|
},
|
||||||
|
"movie": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
9
strapi/src/api/edition/controllers/edition.js
Normal file
9
strapi/src/api/edition/controllers/edition.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* edition controller
|
||||||
|
*/
|
||||||
|
|
||||||
|
const { createCoreController } = require('@strapi/strapi').factories;
|
||||||
|
|
||||||
|
module.exports = createCoreController('api::edition.edition');
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user