Compare commits

..

No commits in common. "master" and "main" have entirely different histories.
master ... main

42 changed files with 7916 additions and 1 deletions

3
.eslintrc.json Normal file
View File

@ -0,0 +1,3 @@
{
"extends": "next/core-web-vitals"
}

36
.gitignore vendored Normal file
View File

@ -0,0 +1,36 @@
# 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

339
LICENSE.txt Normal file
View File

@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

View File

@ -1,2 +1,42 @@
# website
# OpenFest Website
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.tsx`. 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.
## License
GPL v2 or later, see LICENSE.txt

12
dictionaries.ts Normal file
View File

@ -0,0 +1,12 @@
import "server-only";
import type { Locale } from "./i18n-config";
// We enumerate all dictionaries here for better linting and typescript support
// We also get the default import for cleaner types
const dictionaries = {
en: () => import("./dictionaries/en.json").then((module) => module.default),
bg: () => import("./dictionaries/bg.json").then((module) => module.default),
};
export const getDictionary = async (locale: Locale) =>
dictionaries[locale]?.() ?? dictionaries.en();

5
dictionaries/bg.json Normal file
View File

@ -0,0 +1,5 @@
{
"main": {
"hello": "Добре дошли"
}
}

5
dictionaries/en.json Normal file
View File

@ -0,0 +1,5 @@
{
"main": {
"hello": "Welcome"
}
}

6
i18n-config.ts Normal file
View File

@ -0,0 +1,6 @@
export const i18n = {
defaultLocale: "en",
locales: ["en", "bg"],
} as const;
export type Locale = (typeof i18n)["locales"][number];

19
next.config.mjs Normal file
View File

@ -0,0 +1,19 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
async redirects() {
return [
{
source: '/news',
destination: '/news/1',
permanent: true,
},
{
source: '/news/post',
destination: '/news/1',
permanent: true,
},
]
},
}
export default nextConfig

6095
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

35
package.json Normal file
View File

@ -0,0 +1,35 @@
{
"name": "openfest",
"version": "0.1.0",
"license": "GPL-2.0-or-later",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"@formatjs/intl-localematcher": "^0.5.4",
"gray-matter": "^4.0.3",
"negotiator": "^0.6.3",
"next": "14.2.3",
"react": "^18",
"react-dom": "^18",
"remark": "^15.0.1",
"remark-html": "^16.0.1",
"remark-parse": "^11.0.0",
"strip-markdown": "^6.0.0"
},
"devDependencies": {
"@types/negotiator": "^0.6.3",
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
"eslint": "^8",
"eslint-config-next": "14.2.3",
"postcss": "^8",
"tailwindcss": "^3.4.1",
"typescript": "^5"
}
}

8
postcss.config.mjs Normal file
View File

@ -0,0 +1,8 @@
/** @type {import('postcss-load-config').Config} */
const config = {
plugins: {
tailwindcss: {},
},
};
export default config;

1
public/next.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

1
public/vercel.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 283 64"><path fill="black" d="M141 16c-11 0-19 7-19 18s9 18 20 18c7 0 13-3 16-7l-7-5c-2 3-6 4-9 4-5 0-9-3-10-7h28v-3c0-11-8-18-19-18zm-9 15c1-4 4-7 9-7s8 3 9 7h-18zm117-15c-11 0-19 7-19 18s9 18 20 18c6 0 12-3 16-7l-8-5c-2 3-5 4-8 4-5 0-9-3-11-7h28l1-3c0-11-8-18-19-18zm-10 15c2-4 5-7 10-7s8 3 9 7h-19zm-39 3c0 6 4 10 10 10 4 0 7-2 9-5l8 5c-3 5-9 8-17 8-11 0-19-7-19-18s8-18 19-18c8 0 14 3 17 8l-8 5c-2-3-5-5-9-5-6 0-10 4-10 10zm83-29v46h-9V5h9zM37 0l37 64H0L37 0zm92 5-27 48L74 5h10l18 30 17-30h10zm59 12v10l-3-1c-6 0-10 4-10 10v15h-9V17h9v9c0-5 6-9 13-9z"/></svg>

After

Width:  |  Height:  |  Size: 629 B

BIN
src/app/[lang]/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View File

@ -0,0 +1,8 @@
@import url('https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css');
@tailwind base;
@tailwind components;
@tailwind utilities;
.container {
@apply mx-auto px-6;
}

37
src/app/[lang]/layout.tsx Normal file
View File

@ -0,0 +1,37 @@
import type { Metadata } from 'next'
import { Inter } from 'next/font/google'
import './globals.css'
import { i18n, type Locale } from '../../../i18n-config'
import NavBar from '../../components/NavBar'
import Footer from '../../components/Footer'
export async function generateStaticParams() {
return i18n.locales.map((locale) => ({ lang: locale }))
}
const inter = Inter({ subsets: ['latin'] })
export const metadata: Metadata = {
title: 'Create Next App',
description: 'Generated by create next app',
}
export default function RootLayout({
children,
params,
}: Readonly<{
children: React.ReactNode
params: { lang: Locale }
}>) {
return (
<html lang={params.lang}>
<body
className={`${inter.className}, flex flex-col text-white md:text-lg lg:text-xl min-h-screen`}
>
<NavBar lang={params.lang} />
{children}
<Footer />
</body>
</html>
)
}

View File

@ -0,0 +1,49 @@
import { getSortedPostsData, getAllPosts } from '../../../../lib/posts'
import News from '../../../../components/News'
import Social from '../../../../components/Social'
import Pagination from '../../../../components/Pagination'
const POSTS_PER_PAGE = 4
export default async function AllNews({ params }) {
const { lang } = params
const { posts } = await getSortedPostsData(lang)
const totalPages = Math.max(1, Math.floor(posts.length / POSTS_PER_PAGE))
const page = parseInt(params.page)
const startPost = (page - 1) * POSTS_PER_PAGE
const endPost = page * POSTS_PER_PAGE
const paginatedPosts = posts.slice(startPost, endPost)
return (
<>
<div className='bg-deep-blue py-16 text-center'>
<div className='container'>
<h1 className='text-4xl font-extrabold'>Новини от OpenFest</h1>
</div>
</div>
<div className='container py-10 text-black'>
<News posts={paginatedPosts} />
<Pagination baseUrl='/news' page={page} totalPages={totalPages} />
</div>
<Social />
</>
)
}
export async function generateStaticParams() {
const paths = []
const allPosts = await getAllPosts()
for (const [locale, currentPosts] of Object.entries(allPosts)) {
const totalPages = Math.max(
1,
Math.floor(currentPosts.length / POSTS_PER_PAGE)
)
Array.from({ length: totalPages }, (_, i) => {
paths.push({ params: { page: (i + 1).toString() }, locale })
})
}
return paths
}

View File

@ -0,0 +1,35 @@
import Social from '../../../../../components/Social'
import { getSortedPostsData, getAllPosts } from '../../../../../lib/posts'
import { notFound } from 'next/navigation'
export default async function NewsDetails({ params }) {
const { id, lang } = params
const { posts } = await getSortedPostsData(lang)
const post = posts.filter((post) => post.id === id).pop()
if (!post) notFound()
return (
<>
<div className='flex flex-col gap-4 container py-6 text-black'>
<p className='text-4xl font-extrabold'>{post.title}</p>
<p>{post.date}</p>
<div dangerouslySetInnerHTML={{ __html: post.content }}></div>
</div>
<Social />
</>
)
}
export async function generateStaticParams() {
const paths = []
const allPosts = await getAllPosts()
for (const [locale, currentPosts] of Object.entries(allPosts)) {
currentPosts.forEach((post) => {
paths.push({ id: post.id, lang: locale })
})
}
return paths
}

View File

@ -0,0 +1,3 @@
export default function NotFound() {
return <h1 className='text-black text-3xl text-center py-6'>404 Not Found</h1>
}

36
src/app/[lang]/page.tsx Normal file
View File

@ -0,0 +1,36 @@
import Header from '../../components/Header'
import Info from '../../components/Info'
import JoinUs from '../../components/JoinUs'
import Social from '../../components/Social'
import News from '../../components/News'
import { getSortedPostsData } from '../../lib/posts'
import { getDictionary } from '../../../dictionaries'
import { Locale } from '../../../i18n-config'
export default async function Home({
params: { lang },
}: {
params: { lang: Locale }
}) {
const dictionary = await getDictionary(lang)
const { posts } = await getSortedPostsData(lang)
return (
<>
<Header />
<main>
<Info />
<JoinUs />
<div className='container flex flex-col py-8 lg:py-16 gap-8 lg:gap-16 text-black'>
<div className='flex flex-col'>
<h2 className='text-xl lg:text-3xl font-extrabold text-center mb-6 lg:mb-10'>
Новини
</h2>
<News posts={posts} />
</div>
</div>
<Social />
</main>
</>
)
}

155
src/components/Footer.jsx Normal file
View File

@ -0,0 +1,155 @@
import Link from 'next/link'
const Footer = () => {
return (
<footer className='mt-auto'>
<div className='bg-dark-blue pt-8 lg:pt-16 pb-6'>
<div className='container'>
<div className='flex items-center gap-4 mb-3'>
<div className='bg-blue w-14 h-14 lg:w-16 lg:h-16 p-1 rounded-full'>
<svg
className='fill-deep-blue'
width='100%'
height='100%'
viewBox='0 0 293 182'
fill='none'
xmlns='http://www.w3.org/2000/svg'
>
<path
fillRule='evenodd'
clipRule='evenodd'
d='M0 86.1918L18.5717 65.0594L66.4078 111.895C70.0214 108.957 74.1682 105.87 78.7001 102.753L48.1916 61.7055L65.8746 49.4475L93.4804 93.6119C101.863 89.0114 111.163 84.7374 121.353 81.4429L98.1307 8.48858L133.29 0L147.833 75.4772C153.401 74.8539 159 74.5274 164.627 74.5571L177.601 13.1484L203.104 19.0845L184.917 76.4566C204.2 79.9886 220.283 88.2397 233.168 98.5982L282.663 37.9315L293 47.7557L241.876 106.256C252.095 116.317 259.796 127.566 264.98 138.221L233.257 156.594C233.257 156.594 220.402 126.349 196.202 108.126C198.986 114.537 200.497 121.63 200.497 129.05C200.497 158.196 176.772 182 147.714 182C117.117 182 92.888 155.703 95.0206 125.34L122.004 128.605C118.361 142.05 128.55 155.941 142.916 155.941C154.764 155.941 164.598 146.205 164.598 134.215C164.598 102.308 128.402 94.5023 102.544 105.484C86.4605 112.34 73.5166 122.402 55.8039 136.084C37.7061 119.107 18.8679 102.783 0 86.1918Z'
/>
</svg>
</div>
<div>
<p className='text-xl lg:text-3xl font-extrabold uppercase'>
OpenFest
</p>
<p>2024</p>
</div>
</div>
<div className='flex flex-col lg:flex-row gap-6'>
<div className='basis-1/2'>
<p>
OpenFest е конференция, посветена на свободната култура,
свободния софтуер и софтуера с отворен код, свободното споделяне
на знания.
</p>
<div className='flex flex-col gap-4 my-4'>
<div className='flex items-center'>
<div className='w-10 h-10 bg-blue flex items-center justify-center rounded-full mr-3'>
<i className='fa-solid fa-calendar-days'></i>
</div>
2 и 3 ноември 2024 (събота и неделя)
</div>
<div className='flex items-center'>
<div className='w-10 h-10 bg-blue flex items-center justify-center rounded-full mr-3'>
<i className='fa-solid fa-location-dot'></i>
</div>
Sofia Tech Park
</div>
<div className='flex items-center'>
<div className='w-10 h-10 bg-blue flex items-center justify-center rounded-full mr-3'>
<i className='fa-solid fa-ticket'></i>
</div>
Вход свободен, без предварителна регистрация
</div>
</div>
</div>
<div className='basis-1/4'>
<p className='font-extrabold'>Свържете се</p>
<p>
Ако имате въпроси, предложения или желаете да се включите в
OpenFest 2024, пишете ни:
</p>
<div className='flex flex-col'>
<div className='flex items-center my-4'>
<div className='w-10 h-10 bg-neon-green text-black flex items-center justify-center rounded-full mr-3'>
<i className='fa-solid fa-envelope'></i>
</div>
<Link href='mailto:info@openfest.org'>info@openfest.org</Link>
</div>
</div>
</div>
<div className='flex flex-col gap-4 basis-1/4'>
<Link href='https://cfp.openfest.org/'>
<div className='flex items-center'>
<div className='w-10 h-10 bg-neon-green text-black flex items-center justify-center rounded-full mr-3'>
<i className='fa-solid fa-lightbulb'></i>
</div>
Call for Participation
</div>
</Link>
<Link href='https://cfp.openfest.org/'>
<div className='flex items-center'>
<div className='w-10 h-10 bg-neon-green text-black flex items-center justify-center rounded-full mr-3'>
<i className='fa-solid fa-helmet-safety'></i>
</div>
Call for Volunteers
</div>
</Link>
<Link href='https://cfp.openfest.org/'>
<div className='flex items-center'>
<div className='w-10 h-10 bg-neon-green text-black flex items-center justify-center rounded-full mr-3'>
<i className='fa-solid fa-handshake-angle'></i>
</div>
Партньорство и спонсорство
</div>
</Link>
</div>
</div>
<div className='flex justify-center gap-4 mt-4'>
<div className='w-10 h-10 bg-blue flex items-center justify-center rounded-full'>
<a
href='https://www.youtube.com/@openfestbulgaria'
target='_blank'
>
<i className='fa-brands fa-youtube'></i>
</a>
</div>
<div className='text-blue text-4xl flex items-center justify-center rounded-full'>
<a
href='https://www.facebook.com/groups/6360369433'
target='_blank'
>
<i className='fa-brands fa-facebook'></i>
</a>
</div>
<div className='w-10 h-10 bg-blue flex items-center justify-center rounded-full'>
<a href='https://twitter.com/openfestbg' target='_blank'>
<i className='fa-brands fa-x-twitter'></i>
</a>
</div>
<div className='w-10 h-10 bg-blue flex items-center justify-center rounded-full'>
<a href='https://mastodon.social/@openfest' target='_blank'>
<i className='fa-brands fa-mastodon'></i>
</a>
</div>
<div className='w-10 h-10 bg-blue flex items-center justify-center rounded-full'>
<a
href='https://www.instagram.com/openfestbulgaria'
target='_blank'
>
<i className='fa-brands fa-instagram'></i>
</a>
</div>
<div className='w-10 h-10 bg-blue flex items-center justify-center rounded-full'>
<a
href='https://www.linkedin.com/company/openfest-bulgaria/'
target='_blank'
>
<i className='fa-brands fa-linkedin-in'></i>
</a>
</div>
</div>
<p className='text-center mt-5'>
&copy; 2024 OpenFest. Някои права са запазени.
</p>
</div>
</div>
</footer>
)
}
export default Footer

18
src/components/Header.jsx Normal file
View File

@ -0,0 +1,18 @@
import Chicken from './icons/Chicken'
const Header = () => {
return (
<header className='relative overflow-hidden pt-10 pb-16 sm:pt-16 sm:pb-24 lg:pt-16 lg:pb-44 bg-deep-blue'>
<div className='container flex flex-col gap-2 lg:gap-4 text-center'>
<p className='uppercase tracking-widest lg:tracking-max text-xl lg:text3xl z-10'>
2 - 3 Ноември 2024
</p>
<p className='font-extrabold text-4xl lg:text-8xl z-10'>OpenFest</p>
<p className='font-extrabold text-4xl lg:text-8xl z-10'>2024</p>
</div>
<Chicken className='fill-blue absolute inset-0 scale-1.3 -translate-y-10 sm:scale-125 lg:-translate-y-16' />
</header>
)
}
export default Header

59
src/components/Info.jsx Normal file
View File

@ -0,0 +1,59 @@
import Circle from './icons/Circle'
import Eye from './icons/Eye'
const Info = () => {
return (
<div className='bg-deep-blue'>
<div className='container'>
<div className='flex flex-col-reverse lg:flex-row gap-6 items-center py-8 lg:py-16'>
<div className='flex justify-center relative flex-1'>
<Circle className='w-2/5 md:w-1/2' />
<Eye className='absolute inset-0 translate-x-full w-2/5 md:translate-x-3/4 md:w-1/2' />
</div>
<div className='flex-1'>
<h2 className='font-extrabold text-xl lg:text-3xl'>
{' '}
Повече за OpenFest
</h2>
<p className='mt-4 mb-2'>
OpenFest e най-голямата българска конференция, посветена на
свободната култура, свободния софтуер и софтуера с отворен код,
свободното споделяне на знания фестивал на свободното
творчество.
</p>
<p>
Организаторите всякога се стараят да дадат поле за изява и на
свободното изкуство и свободното разпространение на знания.
</p>
</div>
</div>
</div>
<div className='bg-blue'>
<div className='container flex flex-col gap-2 items-center lg:gap-0 lg:flex-row lg:justify-between p-2'>
<p>
<i className='fa-solid fa-angle-right text-neon-pink'></i>
1500+ посетители годишно
</p>
<p>
<i className='fa-solid fa-angle-right text-neon-pink'></i>
40+ Лектора
</p>
<p>
<i className='fa-solid fa-angle-right text-neon-pink'></i>
20+ Лекции
</p>
<p>
<i className='fa-solid fa-angle-right text-neon-pink'></i>
20+ Работилници
</p>
<p>
<i className='fa-solid fa-angle-right text-neon-pink'></i>
70+ Доброволеца
</p>
</div>
</div>
</div>
)
}
export default Info

155
src/components/JoinUs.jsx Normal file
View File

@ -0,0 +1,155 @@
import Link from 'next/link'
import BackToTheSource from './icons/BackToTheSource'
export default function JoinUs() {
return (
<div className='container flex flex-col pt-8 lg:pt-16 gap-8 lg:gap-16'>
<div className='flex flex-col-reverse text-black lg:flex-row gap-6 items-center'>
<div className='flex-1'>
<h2 className='font-extrabold text-xl lg:text-3xl'>
Станете част от OpenFest 2024
</h2>
<p className='mt-4 mb-2'>
Фестът е оживен двудневен празник на всичко с отворен код, който
събира ентусиасти, разработчици и новатори. Независимо дали сте
опитен професионалист или начинаещ, OpenFest предлага уникална
възможност да се потопите в света на отворените технологии и
съвместното творчество във всички измерения.
</p>
<div className='flex flex-col gap-2 mt-4'>
<div className='flex items-center'>
<div className='w-10 h-10 bg-blue flex items-center justify-center rounded-full mr-3 text-white min-w-10'>
<i className='fa-solid fa-calendar-days'></i>
</div>
2 и 3 ноември 2024 (събота и неделя)
</div>
<div className='flex items-center'>
<div className='w-10 h-10 bg-blue flex items-center justify-center rounded-full mr-3 text-white min-w-10'>
<i className='fa-solid fa-location-dot'></i>
</div>
Sofia Tech Park
</div>
<div className='flex items-center'>
<div className='w-10 h-10 bg-blue flex items-center justify-center rounded-full mr-3 min-w-10 text-blue'>
<i className='fa-solid fa-location-dot'></i>
</div>
Вход свободен, без предварителна регистрация
</div>
</div>
</div>
<div className='flex justify-center relative flex-1'>
<BackToTheSource />
</div>
</div>
<div className='flex flex-col text-black lg:flex-row gap-6 items-center'>
<div className='flex flex-col gap-4 items-center center relative flex-1'>
<div className='flex items-center justify-center bg-blue rounded-full p-4 text-5xl w-24 h-24 lg:text-7xl lg:w-36 lg:h-36'>
<i className='fa-regular fa-lightbulb text-neon-pink'></i>
</div>
<h2 className='font-extrabold text-3xl text-center'>
Предложете
<br />
идея
</h2>
</div>
<div className='flex-1'>
<p className='mb-2'>
Представете лекция, уъркшоп или щанд. Приемаме предложения до 1
септември 2024 г
</p>
<p>Тази година направленията са:</p>
<ul>
<li>
<strong>Advanced Technical</strong> За технически лекции с
по-голяма сложност.
</li>
<li>
<strong>Technical</strong> Това е мястото да разкажете какво
ново сте открили през годината, което би било полезно и разбираемо
и за начинаещи.
</li>
<li>
<strong>OpenArt</strong> Свободата да твориш под Creative
Commons и свободното изкуство във всичките му форми.
</li>
<li>
<strong>Общност и социален</strong> Отворени общности,
образование и управление, свободни лицензи, как човек може да се
изхранва с хобито си и др.
</li>
</ul>
<div className='flex justify-center mt-4'>
<Link
href='https://cfp.openfest.org/'
className='bg-neon-green px-16 py-4 rounded-md'
>
Предложете идея
</Link>
</div>
</div>
</div>
<div className='flex flex-col-reverse text-black lg:flex-row gap-6 items-center'>
<div className='flex-1'>
<p>
OpenFest се организира и провежда изцяло от доброволци. Тази година
отново се надяваме да срещнем нови съмишленици и приятели, да
създадем вълнуващо събитие. Можете да се включите в екипите: Мрежа;
Логистика; Аудио и видео; Медия, уебсайт и дизайн; Хералди;
Рецепция.
</p>
<p className='mt-4'>
Повече за тях можете да прочетете във формата за кандидатстване.
</p>
<div className='flex justify-center mt-4'>
<Link
href='https://cfp.openfest.org/'
className='bg-neon-green px-16 py-4 rounded-md'
>
Кандидатствайте
</Link>
</div>
</div>
<div className='flex flex-col gap-4 items-center center relative flex-1'>
<div className='flex items-center justify-center bg-blue rounded-full p-4 text-5xl w-24 h-24 lg:text-7xl lg:w-36 lg:h-36'>
<i className='fa-solid fa-helmet-safety text-neon-pink'></i>
</div>
<h2 className='font-extrabold text-3xl text-center'>
Станете
<br />
доброволец
</h2>
</div>
</div>
<div className='flex flex-col text-black lg:flex-row gap-6 items-center'>
<div className='flex flex-col gap-4 items-center center relative flex-1'>
<div className='flex items-center justify-center bg-blue rounded-full p-4 text-5xl w-24 h-24 lg:text-7xl lg:w-36 lg:h-36'>
<i className='fa-solid fa-handshake-angle text-neon-pink'></i>
</div>
<h2 className='font-extrabold text-3xl text-center'>
Подкрепете <br /> събитието като <br /> партньор
</h2>
</div>
<div className='flex-1'>
<p>
OpenFest се организира и провежда изцяло от доброволци. Тази година
отново се надяваме да срещнем нови съмишленици и приятели, да
създадем вълнуващо събитие. Можете да се включите в екипите: Мрежа;
Логистика; Аудио и видео; Медия, уебсайт и дизайн; Хералди;
Рецепция.
</p>
<p className='mt-4'>
Повече за тях можете да прочетете във формата за кандидатстване.
</p>
<div className='flex justify-center mt-4'>
<Link
href='https://cfp.openfest.org/'
className='bg-neon-green px-16 py-4 rounded-md'
>
Кандидатствайте
</Link>
</div>
</div>
</div>
</div>
)
}

View File

@ -0,0 +1,30 @@
'use client'
import Link from 'next/link'
import { i18n } from '../../i18n-config'
import { usePathname } from 'next/navigation'
export default function LocaleSwitcher({ lang }) {
const pathName = usePathname()
const { locales } = i18n
const otherLocale = locales?.find((cur) => cur !== lang)
function getRedirectPathName() {
if (!pathName) return '/'
const segments = pathName.split('/')
segments[1] = otherLocale
return segments.join('/')
}
return (
<Link
href={getRedirectPathName()}
locale={otherLocale}
className='flex justify-center items-center'
>
<i className='fa-solid fa-globe'></i>
</Link>
)
}

31
src/components/NavBar.jsx Normal file
View File

@ -0,0 +1,31 @@
import Logo from './icons/Logo'
import LanguageSwitcher from './LanguageSwitcher'
import Link from 'next/link'
const Navbar = ({ lang }) => {
return (
<nav className='bg-deep-blue'>
<div className='container flex flex-col items-center gap-6 lg:flex-row lg:justify-between py-6'>
<Link href='/' className='flex items-center gap-4'>
<div className='bg-blue w-14 h-14 lg:w-16 lg:h-16 p-1 rounded-full'>
<Logo className='fill-deep-blue' />
</div>
<div>
<p className='text-xl lg:text-3xl font-extrabold uppercase'>
OpenFest
</p>
<p>2024</p>
</div>
</Link>
<div className='flex w-full flex-wrap justify-center sm:justify-around lg:justify-end gap-6 gap-y-4 items-center font-semibold'>
<Link href='/news'>Новини</Link>
<Link href='https://cfp.openfest.org/'>Call for participation</Link>
<Link href='mailto:info@openfest.org'>Пишете ни</Link>
<LanguageSwitcher lang={lang} />
</div>
</div>
</nav>
)
}
export default Navbar

21
src/components/News.jsx Normal file
View File

@ -0,0 +1,21 @@
import Link from 'next/link'
export default function News({ posts }) {
return (
<div className='flex flex-wrap text-center gap-6 lg:text-left lg:gap-4 lg:gap-y-8'>
{posts.map((post) => (
<div key={post.id} className='flex flex-col gap-2 basis-24 flex-1'>
<p className='text-xl font-extrabold'>{post.title}</p>
<p>{post.date}</p>
<p>{post.truncatedContent}</p>
<Link href={`/news/post/${post.id}`}>
<p className='underline decoration-2 underline-offset-4 decoration-neon-green mt-auto'>
Прочетете
<i className='fa-solid fa-angle-right text-neon-green ml-1'></i>
</p>
</Link>
</div>
))}
</div>
)
}

View File

@ -0,0 +1,52 @@
import Link from 'next/link'
export default function Pagination({ baseUrl, page, totalPages }) {
return (
<div className='flex justify-center gap-6 text-black pt-6'>
<Link
href={`${baseUrl}/1`}
className='text-center border border-neon-green w-8 h-8 rounded-2xl hover:bg-neon-green'
>
<i className='fa-solid fa-angles-left'></i>
</Link>
{page > 1 ? (
<Link
href={`${baseUrl}/${page - 1}`}
className='text-center border border-neon-green w-8 h-8 rounded-2xl hover:bg-neon-green'
>
<i className='fa-solid fa-angle-left'></i>
</Link>
) : (
<></>
)}
{Array.from(Array(totalPages), (_, i) => {
const pageNumber = i + 1
return (
<Link
href={`${baseUrl}/${pageNumber}`}
key={pageNumber}
className={`text-center border border-neon-green w-8 h-8 rounded-2xl hover:bg-neon-green ${pageNumber === page ? 'bg-neon-green' : ''}`}
>
{pageNumber}
</Link>
)
})}
{page < totalPages ? (
<Link
href={`${baseUrl}/${page + 1}`}
className='text-center border border-neon-green w-8 h-8 rounded-2xl hover:bg-neon-green'
>
<i className='fa-solid fa-angle-right'></i>
</Link>
) : (
<></>
)}
<Link
href={`${baseUrl}/${totalPages}`}
className='text-center border border-neon-green w-8 h-8 rounded-2xl hover:bg-neon-green'
>
<i className='fa-solid fa-angles-right'></i>
</Link>
</div>
)
}

57
src/components/Social.jsx Normal file
View File

@ -0,0 +1,57 @@
const Social = () => {
return (
<div id='social' className='py-8 lg:pb-16 bg-deep-blue'>
<div className='container'>
<h2 className='font-extrabold text-center text-xl lg:text-3xl mb-6 lg:mb-12'>
Продължете разговора
</h2>
<div className='flex flex-wrap justify-center gap-10 md:gap-20 md:gap-y-12'>
<div className='flex items-center gap-4'>
<div className='flex justify-center items-center bg-neon-green w-12 h-12 md:w-16 md:h-16 rounded-full text-black text-3xl'>
<i className='fa-brands fa-youtube'></i>
</div>
@openfestbulgaria
</div>
<div className='flex items-center gap-4'>
<div className='flex justify-center items-center bg-black w-12 h-12 md:w-16 md:h-16 rounded-full text-neon-green text-5xl md:text-6xl'>
<i className='fa-brands fa-facebook '></i>
</div>
@openfestbulgaria
</div>
<div className='flex items-center gap-4'>
<div className='flex justify-center items-center bg-neon-green w-12 h-12 md:w-16 md:h-16 rounded-full text-black text-3xl'>
<i className='fa-brands fa-x-twitter'></i>
</div>
OpenFestBG
</div>
<div className='flex items-center gap-4'>
<div className='flex justify-center items-center bg-neon-green w-12 h-12 md:w-16 md:h-16 rounded-full text-black text-3xl'>
<i className='fa-brands fa-instagram'></i>
</div>
openfestbulgaria
</div>
<div className='flex items-center gap-4'>
<div className='flex justify-center items-center bg-neon-green w-12 h-12 md:w-16 md:h-16 rounded-full text-black text-3xl'>
<i className='fa-brands fa-mastodon'></i>
</div>
@openfest
</div>
<div className='flex items-center gap-4'>
<div className='flex justify-center items-center bg-neon-green w-12 h-12 md:w-16 md:h-16 rounded-full text-black text-3xl'>
<i className='fa-brands fa-facebook-f'></i>
</div>
Facebook група: OpenFest
</div>
<div className='flex items-center gap-4'>
<div className='flex justify-center items-center bg-neon-green w-12 h-12 md:w-16 md:h-16 rounded-full text-black text-3xl'>
<i className='fa-brands fa-linkedin-in'></i>
</div>
openfest-bulgaria
</div>
</div>
</div>
</div>
)
}
export default Social

View File

@ -0,0 +1,196 @@
export default function BackToTheSource({ className }) {
return (
<svg
className={className}
width='100%'
fill='none'
viewBox='0 0 710 319'
xmlns='http://www.w3.org/2000/svg'
>
<path
d='M563.902 292.433C502.395 305.069 425.252 313.559 342.093 313.559C144.143 313.559 5.02502 265.455 5.02502 227.837'
stroke='#D92ACE'
strokeLinecap='round'
strokeWidth='10'
/>
<path
d='M703.912 227.837C703.912 241.271 683.012 256.043 647.124 269.484'
stroke='#D92ACE'
strokeLinecap='round'
strokeWidth='10'
/>
<path
d='M636.889 268.756L626.209 281.882C628.085 283.785 629.533 285.587 630.453 286.816C630.536 286.931 630.628 287.061 630.725 287.198C630.867 287.402 631.022 287.626 631.178 287.853C631.332 288.078 631.486 288.307 631.634 288.526C631.649 288.55 631.661 288.573 631.674 288.596C631.679 288.609 631.685 288.621 631.688 288.633C631.732 288.747 631.734 288.87 631.699 288.982C631.692 289.001 631.686 289.022 631.678 289.042C631.67 289.054 631.664 289.067 631.656 289.08C631.648 289.096 631.638 289.111 631.628 289.126C631.591 289.179 631.543 289.227 631.485 289.265L629.694 290.449L629.68 290.458L623.253 294.709C623.008 294.871 622.679 294.804 622.518 294.56L621.658 293.266C621.494 293.021 621.561 292.691 621.806 292.528L624.068 291.031C623.285 289.698 620.987 286.121 617.506 283.534C617.438 283.483 617.347 283.543 617.361 283.623C618.161 288.135 616.542 292.934 612.724 296.014C607.14 300.52 598.966 299.641 594.462 294.054C593.781 293.214 593.222 292.314 592.785 291.375C592.638 291.056 592.841 290.684 593.188 290.631L599.924 289.594C600.099 289.566 600.276 289.631 600.396 289.761C602.406 291.942 605.793 292.222 608.138 290.33C610.576 288.355 610.96 284.781 608.994 282.333C608.985 282.324 608.979 282.316 608.972 282.307C607.981 281.103 606.463 280.598 604.898 280.627C601.804 280.687 597.733 281.872 593.885 283.917C590.477 285.727 587.219 288.392 585.177 290.21C584.548 290.772 584.031 291.256 583.668 291.607C583.45 291.817 583.099 291.802 582.901 291.574L578.791 286.866L571.092 279.166C570.953 279.026 570.903 278.821 570.961 278.634L572.776 272.738C572.895 272.356 573.375 272.235 573.658 272.518L583.049 281.913C583.961 281.215 584.974 280.482 586.069 279.752L582.143 273.801C582.056 273.67 582.032 273.505 582.08 273.354L583.627 268.323C583.759 267.895 584.328 267.812 584.576 268.185L590.523 277.102C591.092 276.804 591.672 276.518 592.262 276.244C593.543 275.651 594.802 275.152 596.03 274.734L593.265 262.643C593.227 262.474 593.274 262.298 593.388 262.17L599.412 255.488C599.699 255.169 600.226 255.306 600.323 255.726L604.268 272.975C605.441 272.869 606.542 272.833 607.558 272.847L611.097 259.71C611.204 259.309 611.715 259.182 611.998 259.487L615.478 263.24C615.601 263.374 615.648 263.561 615.6 263.738L612.982 273.504C612.998 273.508 613.015 273.513 613.031 273.517C617.189 274.63 620.878 277.071 623.851 279.661L635.523 265.422C635.807 265.076 636.364 265.213 636.454 265.652C636.682 266.758 636.997 268.315 636.997 268.315C637.028 268.471 636.989 268.633 636.889 268.756Z'
fill='#D92ACE'
/>
<path
d='M631.634 288.526C631.486 288.307 631.332 288.078 631.178 287.853C631.023 287.626 630.867 287.401 630.725 287.198C630.735 287.209 630.743 287.22 630.751 287.232C631.029 287.63 631.347 288.098 631.634 288.526Z'
fill='#D92ACE'
/>
<path
d='M631.634 288.526C631.65 288.55 631.662 288.573 631.674 288.596C631.661 288.573 631.649 288.55 631.634 288.526Z'
fill='#D92ACE'
/>
<path
d='M631.7 288.981C631.734 288.869 631.732 288.747 631.689 288.633C631.734 288.747 631.736 288.869 631.7 288.981Z'
fill='#D92ACE'
/>
<path
d='M4.26318 179.068V173.831L40.1095 198.998V209.139L33.1999 207.054L40.1095 214.671V224.813L8.75892 202.802V182.225L4.26318 179.068ZM16.6344 192.806L26.2898 199.584L32.2339 200.586V198.705L16.6339 187.753L16.6344 192.806ZM16.6344 203.094L32.2345 214.047V212.166L26.2903 204.822L16.635 198.043L16.6344 203.094Z'
fill='#2F1B5D'
/>
<path
d='M44.8984 226.151L53.3013 208.784L49.0308 207.152V201.915L72.9646 211.059L83.4333 240.874L74.1351 237.321L71.5177 229.867L56.8481 224.263L54.2306 229.716L44.8984 226.151ZM58.983 219.843L69.3827 223.816L65.7323 213.534L62.6329 212.35L58.983 219.843Z'
fill='#2F1B5D'
/>
<path
d='M83.4746 220.236V214.999L125.788 226.289V235.804L116.492 231.995V229.045L98.0778 224.132V239.473L116.492 244.386V241.436L125.788 242.589V252.104L88.7817 242.229V221.652L83.4746 220.236Z'
fill='#2F1B5D'
/>
<path
d='M130.03 248.834V242.233L135.427 241.779V228.281L144.88 230.069V240.949L163.389 239.361V233.57L172.842 235.359V245.169L158.644 246.391L166.794 254.498L173.06 255.683V261.215L161.179 258.967L149.263 247.162L144.88 247.55V255.884L135.427 254.095V248.38L130.03 248.834Z'
fill='#2F1B5D'
/>
<path
d='M190.567 248.341V238.016L231.526 242.899V253.224L221.973 250.758V246.996L215.823 246.263V266.841L206.27 265.702V245.125L200.12 244.391V248.153L190.567 248.341Z'
fill='#2F1B5D'
/>
<path
d='M236.663 269.16V243.346L274.856 246.205V272.02L236.663 269.16ZM246.257 264.642L265.262 266.064V250.723L246.257 249.3V264.642Z'
fill='#2F1B5D'
/>
<path
d='M292.153 257.404V247.077L333.389 248.083V258.409L323.771 256.847V253.085L317.58 252.934V273.512L307.962 273.277V252.7L301.771 252.549V256.31L292.153 257.404Z'
fill='#2F1B5D'
/>
<path
d='M336.884 253.391V248.154L351.995 247.926V258.142L371.014 257.854V247.639L380.634 247.494V273.308L371.014 273.453V263.09L351.995 263.378V273.74L342.376 273.886V253.308L336.884 253.391Z'
fill='#2F1B5D'
/>
<path
d='M385.287 252.636V247.399L429.052 245.016V254.53L419.445 253.725V250.775L400.414 251.812V256.864L416.058 256.012V261.249L400.414 262.101V267.153L419.445 266.116V263.166L429.052 261.315V270.829L390.809 272.912V252.334L385.287 252.636Z'
fill='#2F1B5D'
/>
<path
d='M449.991 269.131V260.649L459.559 261.036V262.88L478.108 260.916V257.67L449.991 256.187V243.316L487.675 239.325V248.287L478.107 247.825V245.575L459.559 247.54V251.044L487.675 252.528V265.14L449.991 269.131Z'
fill='#2F1B5D'
/>
<path
d='M493.776 264.387V238.573L531.666 232.986V258.802L493.776 264.387ZM503.293 257.748L522.148 254.967V239.626L503.293 242.406V257.748Z'
fill='#2F1B5D'
/>
<path
d='M536.277 237.581V232.345L551.098 229.383V247.453L555.725 249.035L569.75 246.232V225.655L579.185 223.77V249.584L553.194 254.778L541.663 250.813V236.505L536.277 237.581Z'
fill='#2F1B5D'
/>
<path
d='M583.769 242.965V237.248L589.071 234.891V221.468L626.041 211.457V224.143L615.295 228.933L627.5 236.876L614.655 240.354L604.514 233.734L598.358 236.47V244.767L589.071 247.282V240.608L583.769 242.965ZM598.358 230.68L616.754 222.49V219.207L598.358 224.19V230.68Z'
fill='#2F1B5D'
/>
<path
d='M630.358 215.744V210.508L671.111 194.471V203.985L662.157 206.181V203.231L644.422 210.209V225.55L662.157 218.572V215.621L671.111 210.77V220.285L635.469 234.31V213.732L630.358 215.744Z'
fill='#2F1B5D'
/>
<path
d='M676.633 199.679V194.443L709.429 165.354V174.868L702.23 179.926V176.976L687.969 189.624V194.677L699.692 184.279V189.515L687.969 199.913V204.966L702.23 192.317V189.367L709.429 181.653V191.168L680.77 216.587V196.009L676.633 199.679Z'
fill='#2F1B5D'
/>
<path
d='M9.02502 88.5303V62.7158L41.509 42.4174V68.2319L9.02502 88.5303ZM17.1851 78.195L33.3485 68.0942V52.7533L17.1851 62.8535V78.195Z'
fill='#2F1B5D'
/>
<path
d='M46.16 61.7082V55.9184L51.3779 53.2808V39.2672L87.7623 27.3013V40.6512L60.5178 54.4421V62.0759L51.3779 65.0817V59.0706L46.16 61.7082ZM60.5178 48.6522L78.6224 39.4899V35.5441L60.5178 41.4979V48.6522Z'
fill='#2F1B5D'
/>
<path
d='M91.6941 31.5751V26.3386L134.507 16.9479V26.4623L125.109 27.1961V24.246L106.494 28.329V33.3815L121.796 30.025V35.2616L106.494 38.6185V43.6705L125.109 39.5875V36.6374L134.507 33.2484V42.7629L97.0955 50.9691V30.3912L91.6941 31.5751Z'
fill='#2F1B5D'
/>
<path
d='M140.397 41.7753V14.9285L168.735 26.5561V16.7833L165.639 17.2655V12.0289L178.242 10.0658V35.8803L170.192 37.1344L149.905 28.494V35.0583L153 34.5757V39.8122L140.397 41.7753Z'
fill='#2F1B5D'
/>
<path
d='M182.864 14.6865V9.44997L223.359 5.04932V14.3792L213.794 14.0909V11.3249L197.89 13.0534V19.9127L213.794 18.1842V23.4208L197.89 25.1493V33.6314L188.325 34.6704V14.093L182.864 14.6865Z'
fill='#2F1B5D'
/>
<path
d='M226.822 9.93122V4.69468L270.551 1.72705V11.2415L260.952 10.5655V7.61539L241.938 8.90557V13.958L257.569 12.8968V18.1333L241.938 19.194V24.2465L260.952 22.9563V20.0062L270.551 18.0271V27.5416L232.339 30.1348V9.55685L226.822 9.93122Z'
fill='#2F1B5D'
/>
<path
d='M276.363 27.2867V18.8052L285.978 19.8886V21.7326L304.62 21.1158V17.8708L276.363 14.3431V1.47275L314.237 0.220215V9.18137L304.621 8.02423V5.7744L285.979 6.39113V9.89453L314.237 13.4222V26.0342L276.363 27.2867Z'
fill='#2F1B5D'
/>
<path
d='M319.207 10.4932V0.167588L360.456 0V10.3256L350.834 9.03696V5.27521L344.642 5.30048V25.8784L335.02 25.9176V5.33967L328.827 5.36493V9.12669L319.207 10.4932Z'
fill='#2F1B5D'
/>
<path
d='M379.62 26.2487V13.747L408.609 10.3076V6.80416L389.234 6.04666V8.29597L379.62 9.39536V0.434204L418.223 1.94354V14.8139L389.234 18.2534V21.3881L408.609 22.1456V20.3016L418.223 19.2759V27.7581L379.62 26.2487Z'
fill='#2F1B5D'
/>
<path
d='M424.366 28.0948V2.28027L462.202 4.9612V30.7757L424.366 28.0948ZM433.963 19.5188L448.598 9.23448L433.963 8.19749V19.5188ZM437.971 23.822L452.606 24.859V13.5377L437.971 23.822Z'
fill='#2F1B5D'
/>
<path
d='M468.333 31.2975V18.7959L497.185 17.2582V13.7548L477.901 11.7261V13.9755L468.333 14.4442V5.48303L506.753 9.52478V22.3952L477.901 23.9324V27.0671L497.184 29.0957V27.2517L506.753 26.8567V35.3383L468.333 31.2975Z'
fill='#2F1B5D'
/>
<path
d='M511.534 32.4187V27.1451L546.994 13.6234V32.3203L552.612 33.1345V38.371L546.994 37.5568V41.0236L537.472 39.6437V36.1774L511.534 32.4187ZM523.791 28.9581L537.472 30.9409V23.7128L523.791 28.9581Z'
fill='#2F1B5D'
/>
<path
d='M10.4181 143.275V130.774L34.7318 142.034V138.531L18.4813 127.949V130.198L10.4176 126.423V117.461L42.7944 138.545V151.416L18.4807 140.156V143.29L34.7313 153.872V152.029L42.7944 155.878V164.36L10.4181 143.275Z'
fill='#2F1B5D'
/>
<path
d='M61.6609 161.07V155.833L85.6112 164.178V169.415L61.6609 161.07Z'
fill='#2F1B5D'
/>
<path
d='M104.358 185.272V176.311L113.724 179.986V182.235L132.241 186.585V184.703L125.207 179.88L116.989 177.95V172.713L125.207 174.644L132.241 173.124V171.244L113.724 166.894V169.143L104.358 168.419V159.458L141.608 168.206V178.348L133.39 179.183L141.608 183.879V194.02L104.358 185.272Z'
fill='#2F1B5D'
/>
<path
d='M162.992 198.026V171.179L191.346 191.526V181.754L188.248 181.284V176.048L200.858 177.962V203.777L192.804 202.553L172.504 187.671V194.235L175.602 194.705V199.942L162.992 198.026Z'
fill='#2F1B5D'
/>
<path
d='M207.132 204.55V178.736L245.229 182.681V208.495L207.132 204.55ZM216.702 200.304L235.659 202.267V186.926L216.702 184.963V200.304Z'
fill='#2F1B5D'
/>
<path
d='M249.683 188.319V183.083L262.194 183.841L272.643 201.55L283.019 185.104L293.138 185.718L277.242 210.569L267.897 210.001L255.166 188.652L249.683 188.319Z'
fill='#2F1B5D'
/>
<path
d='M326.036 212.885L334.629 200.477L335.095 199.804L334.524 199.216L323.852 188.271L328.549 187.878L339.79 199.409L330.734 212.493L326.036 212.885ZM324.031 214.051L331.283 213.443L341.072 199.302L328.931 186.848L321.925 187.435L321.81 187.601L333.812 199.911L324.025 214.046L324.031 214.051Z'
fill='#D92ACE'
/>
<path
d='M352.882 213.922L361.474 201.513L361.939 200.839L361.368 200.253L350.697 189.307L355.394 188.914L366.637 200.446L357.579 213.529L352.882 213.922ZM350.875 215.088L358.128 214.478L367.919 200.338L355.777 187.885L348.771 188.473L348.657 188.638L360.656 200.946L350.872 215.083L350.875 215.088Z'
fill='#D92ACE'
/>
<path
d='M378.256 214.32L386.847 201.911L387.313 201.238L386.742 200.651L376.07 189.704L380.767 189.312L392.01 200.843L382.953 213.925L378.256 214.32ZM376.248 215.485L383.502 214.877L393.292 200.736L381.15 188.284L374.144 188.87L374.03 189.035L386.031 201.345L376.244 215.481L376.248 215.485Z'
fill='#D92ACE'
/>
<path
d='M395.038 188.216L394.924 188.381L406.925 200.691L397.138 214.827L397.144 214.831L404.395 214.224L414.185 200.083L402.044 187.629L395.038 188.216Z'
fill='#D92ACE'
/>
<path
d='M414.462 186.724L414.347 186.89L426.348 199.199L416.561 213.336L416.566 213.34L423.817 212.732L433.607 198.591L421.466 186.139L414.462 186.724Z'
fill='#D92ACE'
/>
<path
d='M432.272 184.561L432.159 184.726L444.16 197.036L434.373 211.171L434.378 211.176L441.631 210.568L451.422 196.428L439.279 183.974L432.272 184.561Z'
fill='#D92ACE'
/>
</svg>
)
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,59 @@
export default function Circle({ className }) {
return (
<svg
className={className}
height='100%'
viewBox='0 0 473 472'
fill='none'
xmlns='http://www.w3.org/2000/svg'
>
<g filter='url(#filter0_d_181_191)'>
<circle
cx='236.5'
cy='236'
r='166'
stroke='#2F1B5D'
strokeWidth='40'
shapeRendering='crispEdges'
/>
</g>
<defs>
<filter
id='filter0_d_181_191'
x='0.5'
y='0'
width='472'
height='472'
filterUnits='userSpaceOnUse'
colorInterpolationFilters='sRGB'
>
<feFlood floodOpacity='0' result='BackgroundImageFix' />
<feColorMatrix
in='SourceAlpha'
type='matrix'
values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0'
result='hardAlpha'
/>
<feOffset />
<feGaussianBlur stdDeviation='25' />
<feComposite in2='hardAlpha' operator='out' />
<feColorMatrix
type='matrix'
values='0 0 0 0 0.773556 0 0 0 0 0.327778 0 0 0 0 0.983333 0 0 0 0.2 0'
/>
<feBlend
mode='normal'
in2='BackgroundImageFix'
result='effect1_dropShadow_181_191'
/>
<feBlend
mode='normal'
in='SourceGraphic'
in2='effect1_dropShadow_181_191'
result='shape'
/>
</filter>
</defs>
</svg>
)
}

View File

@ -0,0 +1,57 @@
export default function Eye({ className }) {
return (
<svg
className={className}
height='100%'
viewBox='0 0 485 337'
fill='none'
xmlns='http://www.w3.org/2000/svg'
>
<g filter='url(#filter0_d_181_192)'>
<path
fillRule='evenodd'
clipRule='evenodd'
d='M50.5 142.239L74.8397 114.72L137.533 175.709C142.269 171.883 147.703 167.864 153.643 163.805L113.659 110.353L136.834 94.3904L173.014 151.901C183.999 145.91 196.189 140.345 209.542 136.055L179.108 41.0538L225.187 30L244.247 128.286C251.545 127.475 258.882 127.049 266.257 127.088L283.26 47.1218L316.684 54.8518L292.849 129.562C318.12 134.161 339.199 144.906 356.085 158.394L420.952 79.3943L434.5 92.1874L367.498 168.366C380.891 181.468 390.984 196.116 397.777 209.992L356.202 233.916C356.202 233.916 339.354 194.532 307.639 170.801C311.288 179.149 313.267 188.387 313.267 198.049C313.267 236.003 282.173 267 244.092 267C203.991 267 172.237 232.756 175.032 193.218L210.396 197.469C205.622 214.978 218.976 233.066 237.803 233.066C253.331 233.066 266.219 220.388 266.219 204.774C266.219 163.226 218.781 153.061 184.892 167.361C163.813 176.289 146.849 189.391 123.635 207.209C99.9169 185.101 75.2279 163.844 50.5 142.239Z'
fill='#5C46B2'
/>
</g>
<defs>
<filter
id='filter0_d_181_192'
x='0.5'
y='0'
width='484'
height='337'
filterUnits='userSpaceOnUse'
colorInterpolationFilters='sRGB'
>
<feFlood floodOpacity='0' result='BackgroundImageFix' />
<feColorMatrix
in='SourceAlpha'
type='matrix'
values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0'
result='hardAlpha'
/>
<feOffset dy='20' />
<feGaussianBlur stdDeviation='25' />
<feComposite in2='hardAlpha' operator='out' />
<feColorMatrix
type='matrix'
values='0 0 0 0 0.546065 0 0 0 0 0.5375 0 0 0 0 1 0 0 0 0.2 0'
/>
<feBlend
mode='normal'
in2='BackgroundImageFix'
result='effect1_dropShadow_181_192'
/>
<feBlend
mode='normal'
in='SourceGraphic'
in2='effect1_dropShadow_181_192'
result='shape'
/>
</filter>
</defs>
</svg>
)
}

View File

@ -0,0 +1,18 @@
export default function Logo({ className }) {
return (
<svg
className={className}
width='100%'
height='100%'
viewBox='0 0 293 182'
fill='none'
xmlns='http://www.w3.org/2000/svg'
>
<path
fillRule='evenodd'
clipRule='evenodd'
d='M0 86.1918L18.5717 65.0594L66.4078 111.895C70.0214 108.957 74.1682 105.87 78.7001 102.753L48.1916 61.7055L65.8746 49.4475L93.4804 93.6119C101.863 89.0114 111.163 84.7374 121.353 81.4429L98.1307 8.48858L133.29 0L147.833 75.4772C153.401 74.8539 159 74.5274 164.627 74.5571L177.601 13.1484L203.104 19.0845L184.917 76.4566C204.2 79.9886 220.283 88.2397 233.168 98.5982L282.663 37.9315L293 47.7557L241.876 106.256C252.095 116.317 259.796 127.566 264.98 138.221L233.257 156.594C233.257 156.594 220.402 126.349 196.202 108.126C198.986 114.537 200.497 121.63 200.497 129.05C200.497 158.196 176.772 182 147.714 182C117.117 182 92.888 155.703 95.0206 125.34L122.004 128.605C118.361 142.05 128.55 155.941 142.916 155.941C154.764 155.941 164.598 146.205 164.598 134.215C164.598 102.308 128.402 94.5023 102.544 105.484C86.4605 112.34 73.5166 122.402 55.8039 136.084C37.7061 119.107 18.8679 102.783 0 86.1918Z'
/>
</svg>
)
}

15
src/lib/markdown.js Normal file
View File

@ -0,0 +1,15 @@
import { remark } from 'remark'
import html from 'remark-html'
import strip from 'strip-markdown'
export async function markdownToHtml(markdown) {
const result = await remark().use(html).process(markdown)
return result.toString()
}
export async function markdownToText(markdown) {
const result = await remark().use(strip).process(markdown)
return result.toString()
}

66
src/lib/posts.js Normal file
View File

@ -0,0 +1,66 @@
import fs from 'fs'
import path from 'path'
import matter from 'gray-matter'
import { markdownToHtml, markdownToText } from './markdown'
import { cache } from 'react'
import { i18n } from '../../i18n-config'
async function getTruncatedPostContent(content) {
const words = content.split(' ')
const truncatedWords = 9
const truncatedContent =
words.length > truncatedWords
? words.slice(0, truncatedWords).join(' ') + '...'
: content
const truncatedContentAsText = await markdownToText(truncatedContent)
return truncatedContentAsText
}
export const getSortedPostsData = cache(async (locale) => {
const postsDirectory = path.join(process.cwd(), `src/posts/${locale}`)
const fileNames = fs.readdirSync(postsDirectory)
const allPostsDataPromises = fileNames.map(async (fileName) => {
const id = fileName.replace(/\.md$/, '')
const fullPath = path.join(postsDirectory, fileName)
const fileContents = fs.readFileSync(fullPath, 'utf8')
const matterResult = matter(fileContents)
const content = matterResult.content
const truncatedContent = await getTruncatedPostContent(content)
const contentAsHtml = await markdownToHtml(content)
return {
id,
content: contentAsHtml,
truncatedContent,
...matterResult.data,
}
})
const allPostsData = await Promise.all(allPostsDataPromises)
const sortedPostsData = allPostsData.sort((a, b) => {
if (a.date < b.date) {
return -1
} else {
return 1
}
})
return {
posts: sortedPostsData,
}
})
export const getAllPosts = cache(async () => {
const allPosts = {}
for (const locale of i18n.locales) {
const { posts } = await getSortedPostsData(locale)
allPosts[locale] = posts
}
return allPosts
})

65
src/middleware.ts Normal file
View File

@ -0,0 +1,65 @@
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
import { i18n } from "../i18n-config";
import { match as matchLocale } from "@formatjs/intl-localematcher";
import Negotiator from "negotiator";
function getLocale(request: NextRequest): string | undefined {
// Negotiator expects plain object so we need to transform headers
const negotiatorHeaders: Record<string, string> = {};
request.headers.forEach((value, key) => (negotiatorHeaders[key] = value));
// @ts-ignore locales are readonly
const locales: string[] = i18n.locales;
// Use negotiator and intl-localematcher to get best locale
let languages = new Negotiator({ headers: negotiatorHeaders }).languages(
locales
);
const locale = matchLocale(languages, locales, i18n.defaultLocale);
return locale;
}
export function middleware(request: NextRequest) {
const pathname = request.nextUrl.pathname;
// `/_next/` and `/api/` are ignored by the watcher, but we need to ignore files in `public` manually.
// If you have one
if (
[
'/manifest.json',
'/favicon.ico',
'/next.svg',
'/vercel.svg',
// Your other files in `public`
].includes(pathname)
)
return
// Check if there is any supported locale in the pathname
const pathnameIsMissingLocale = i18n.locales.every(
(locale) => !pathname.startsWith(`/${locale}/`) && pathname !== `/${locale}`
);
// Redirect if there is no locale
if (pathnameIsMissingLocale) {
const locale = getLocale(request);
// e.g. incoming request is /products
// The new URL is now /en-US/products
return NextResponse.redirect(
new URL(
`/${locale}${pathname.startsWith("/") ? "" : "/"}${pathname}`,
request.url
)
);
}
}
export const config = {
// Matcher ignoring `/_next/` and `/api/`
matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"],
};

6
src/posts/bg/first.md Normal file
View File

@ -0,0 +1,6 @@
---
title: 'Bulgarian'
date: '08-06-2024'
---
**Some** description

6
src/posts/en/first.md Normal file
View File

@ -0,0 +1,6 @@
---
title: 'English'
date: '08-06-2024'
---
**Some** description

36
tailwind.config.ts Normal file
View File

@ -0,0 +1,36 @@
import type { Config } from 'tailwindcss'
const config: Config = {
content: [
'./src/pages/**/*.{js,ts,jsx,tsx,mdx}',
'./src/components/**/*.{js,ts,jsx,tsx,mdx}',
'./src/app/**/*.{js,ts,jsx,tsx,mdx}',
],
theme: {
extend: {
backgroundImage: {
'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))',
'gradient-conic':
'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))',
},
colors: {
'dark-blue': '#09021B',
'deep-blue': '#0F0429',
blue: '#2F1B5D',
'neon-green': '#34EAD8',
'neon-pink': '#D92ACE',
},
letterSpacing: {
max: '0.3em',
},
scale: {
1.3: '1.3',
},
flexBasis: {
24: '24%',
},
},
},
plugins: [],
}
export default config

26
tsconfig.json Normal file
View File

@ -0,0 +1,26 @@
{
"compilerOptions": {
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}