initial commit
2
.env
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
VITE_DIRECTUS_API_KEY="SLT_e4auGG1g2gGTl9dLjEBRHW-b7Dgl"
|
||||||
|
VITE_DIRECTUS_URL="https://api.mrsh.online"
|
||||||
24
.gitignore
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
*.local
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
16
README.md
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# React + Vite
|
||||||
|
|
||||||
|
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
|
||||||
|
|
||||||
|
Currently, two official plugins are available:
|
||||||
|
|
||||||
|
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) (or [oxc](https://oxc.rs) when used in [rolldown-vite](https://vite.dev/guide/rolldown)) for Fast Refresh
|
||||||
|
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
|
||||||
|
|
||||||
|
## React Compiler
|
||||||
|
|
||||||
|
The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation).
|
||||||
|
|
||||||
|
## Expanding the ESLint configuration
|
||||||
|
|
||||||
|
If you are developing a production application, we recommend using TypeScript with type-aware lint rules enabled. Check out the [TS template](https://github.com/vitejs/vite/tree/main/packages/create-vite/template-react-ts) for information on how to integrate TypeScript and [`typescript-eslint`](https://typescript-eslint.io) in your project.
|
||||||
5
automate.sh
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# Rebuild and restart
|
||||||
|
docker stop mrsh
|
||||||
|
docker rm mrsh
|
||||||
|
docker build -t your-image-name .
|
||||||
|
docker run --name mrsh -p 5173:5173 your-image-name
|
||||||
12
dockerfile
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
FROM oven/bun:1 AS build
|
||||||
|
WORKDIR /app
|
||||||
|
COPY package.json bun.lockb ./
|
||||||
|
RUN bun install
|
||||||
|
COPY . .
|
||||||
|
RUN bun run build
|
||||||
|
|
||||||
|
FROM nginx:alpine
|
||||||
|
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||||
|
COPY --from=build /app/dist /usr/share/nginx/html
|
||||||
|
EXPOSE 80
|
||||||
|
CMD ["nginx", "-g", "daemon off;"]
|
||||||
29
eslint.config.js
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import js from '@eslint/js'
|
||||||
|
import globals from 'globals'
|
||||||
|
import reactHooks from 'eslint-plugin-react-hooks'
|
||||||
|
import reactRefresh from 'eslint-plugin-react-refresh'
|
||||||
|
import { defineConfig, globalIgnores } from 'eslint/config'
|
||||||
|
|
||||||
|
export default defineConfig([
|
||||||
|
globalIgnores(['dist']),
|
||||||
|
{
|
||||||
|
files: ['**/*.{js,jsx}'],
|
||||||
|
extends: [
|
||||||
|
js.configs.recommended,
|
||||||
|
reactHooks.configs.flat.recommended,
|
||||||
|
reactRefresh.configs.vite,
|
||||||
|
],
|
||||||
|
languageOptions: {
|
||||||
|
ecmaVersion: 2020,
|
||||||
|
globals: globals.browser,
|
||||||
|
parserOptions: {
|
||||||
|
ecmaVersion: 'latest',
|
||||||
|
ecmaFeatures: { jsx: true },
|
||||||
|
sourceType: 'module',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
'no-unused-vars': ['error', { varsIgnorePattern: '^[A-Z_]' }],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
])
|
||||||
13
index.html
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/pp.png" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>[Mrsh]</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
<script type="module" src="/src/main.jsx"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
12
nginx.conf
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name localhost;
|
||||||
|
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
index index.html;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ /index.html;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
33
package.json
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"name": "client",
|
||||||
|
"proxy": "https://mrsh.online",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "vite build",
|
||||||
|
"lint": "eslint .",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"hamburger-react": "^2.5.2",
|
||||||
|
"initialize-css": "^1.4.0",
|
||||||
|
"react": "^19.2.0",
|
||||||
|
"react-dom": "^19.2.0",
|
||||||
|
"react-markdown": "^10.1.0",
|
||||||
|
"react-router": "^7.10.1",
|
||||||
|
"sass": "^1.96.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@eslint/js": "^9.39.1",
|
||||||
|
"@types/react": "^19.2.5",
|
||||||
|
"@types/react-dom": "^19.2.3",
|
||||||
|
"@vitejs/plugin-react": "^5.1.1",
|
||||||
|
"eslint": "^9.39.1",
|
||||||
|
"eslint-plugin-react-hooks": "^7.0.1",
|
||||||
|
"eslint-plugin-react-refresh": "^0.4.24",
|
||||||
|
"globals": "^16.5.0",
|
||||||
|
"vite": "^7.2.4"
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
public/fonts/04b_21/04B_21__.TTF
Normal file
BIN
public/fonts/04b_21/about.gif
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
public/fonts/Alata/Alata-Regular.ttf
Normal file
93
public/fonts/Alata/OFL.txt
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
Copyright 2016 The Alata Project Authors (https://github.com/SorkinType/Alata)
|
||||||
|
|
||||||
|
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||||
|
This license is copied below, and is also available with a FAQ at:
|
||||||
|
https://openfontlicense.org
|
||||||
|
|
||||||
|
|
||||||
|
-----------------------------------------------------------
|
||||||
|
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||||
|
-----------------------------------------------------------
|
||||||
|
|
||||||
|
PREAMBLE
|
||||||
|
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||||
|
development of collaborative font projects, to support the font creation
|
||||||
|
efforts of academic and linguistic communities, and to provide a free and
|
||||||
|
open framework in which fonts may be shared and improved in partnership
|
||||||
|
with others.
|
||||||
|
|
||||||
|
The OFL allows the licensed fonts to be used, studied, modified and
|
||||||
|
redistributed freely as long as they are not sold by themselves. The
|
||||||
|
fonts, including any derivative works, can be bundled, embedded,
|
||||||
|
redistributed and/or sold with any software provided that any reserved
|
||||||
|
names are not used by derivative works. The fonts and derivatives,
|
||||||
|
however, cannot be released under any other type of license. The
|
||||||
|
requirement for fonts to remain under this license does not apply
|
||||||
|
to any document created using the fonts or their derivatives.
|
||||||
|
|
||||||
|
DEFINITIONS
|
||||||
|
"Font Software" refers to the set of files released by the Copyright
|
||||||
|
Holder(s) under this license and clearly marked as such. This may
|
||||||
|
include source files, build scripts and documentation.
|
||||||
|
|
||||||
|
"Reserved Font Name" refers to any names specified as such after the
|
||||||
|
copyright statement(s).
|
||||||
|
|
||||||
|
"Original Version" refers to the collection of Font Software components as
|
||||||
|
distributed by the Copyright Holder(s).
|
||||||
|
|
||||||
|
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||||
|
or substituting -- in part or in whole -- any of the components of the
|
||||||
|
Original Version, by changing formats or by porting the Font Software to a
|
||||||
|
new environment.
|
||||||
|
|
||||||
|
"Author" refers to any designer, engineer, programmer, technical
|
||||||
|
writer or other person who contributed to the Font Software.
|
||||||
|
|
||||||
|
PERMISSION & CONDITIONS
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||||
|
redistribute, and sell modified and unmodified copies of the Font
|
||||||
|
Software, subject to the following conditions:
|
||||||
|
|
||||||
|
1) Neither the Font Software nor any of its individual components,
|
||||||
|
in Original or Modified Versions, may be sold by itself.
|
||||||
|
|
||||||
|
2) Original or Modified Versions of the Font Software may be bundled,
|
||||||
|
redistributed and/or sold with any software, provided that each copy
|
||||||
|
contains the above copyright notice and this license. These can be
|
||||||
|
included either as stand-alone text files, human-readable headers or
|
||||||
|
in the appropriate machine-readable metadata fields within text or
|
||||||
|
binary files as long as those fields can be easily viewed by the user.
|
||||||
|
|
||||||
|
3) No Modified Version of the Font Software may use the Reserved Font
|
||||||
|
Name(s) unless explicit written permission is granted by the corresponding
|
||||||
|
Copyright Holder. This restriction only applies to the primary font name as
|
||||||
|
presented to the users.
|
||||||
|
|
||||||
|
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||||
|
Software shall not be used to promote, endorse or advertise any
|
||||||
|
Modified Version, except to acknowledge the contribution(s) of the
|
||||||
|
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||||
|
permission.
|
||||||
|
|
||||||
|
5) The Font Software, modified or unmodified, in part or in whole,
|
||||||
|
must be distributed entirely under this license, and must not be
|
||||||
|
distributed under any other license. The requirement for fonts to
|
||||||
|
remain under this license does not apply to any document created
|
||||||
|
using the Font Software.
|
||||||
|
|
||||||
|
TERMINATION
|
||||||
|
This license becomes null and void if any of the above conditions are
|
||||||
|
not met.
|
||||||
|
|
||||||
|
DISCLAIMER
|
||||||
|
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||||
|
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||||
|
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||||
|
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||||
|
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||||
BIN
public/fonts/Vanitas/vanitas.ttf
Normal file
3
public/links/bluesky.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="200" height="200" viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M100 94.8998C92.45 80.2249 71.9 52.8665 52.7916 39.3832C34.4833 26.4665 27.5083 28.6915 22.9333 30.7749C17.6333 33.1499 16.6666 41.2915 16.6666 46.0665C16.6666 50.8582 19.2916 85.3082 21 91.0665C26.6666 110.067 46.7833 116.483 65.3333 114.425C38.1666 118.45 14.025 128.342 45.6666 163.575C80.4833 199.617 93.3833 155.85 100 133.658C106.617 155.85 114.233 198.042 153.7 163.575C183.333 133.658 161.842 118.45 134.675 114.425C153.217 116.483 173.342 110.067 179 91.0665C180.708 85.3165 183.333 50.8582 183.333 46.0749C183.333 41.2832 182.367 33.1582 177.067 30.7582C172.492 28.6915 165.517 26.4499 147.208 39.3665C128.1 52.8749 107.542 80.2332 100 94.8998Z" fill="#F1F1F1"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 789 B |
3
public/links/gitea.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="200" height="200" viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M193.25 40.075C193.25 40.075 195 40.0375 196.312 41.35L196.437 41.475C197.237 42.225 199.662 44.525 200 54.9C200 91.175 182.437 128.425 182.437 128.425C181.074 131.461 179.577 134.435 177.95 137.338C172.225 147.363 168.125 151.75 168.125 151.75C168.125 151.75 164.15 156.488 159.687 159.188C154.5 162.5 150.687 162.475 150.687 162.475L90.5872 162.5C82.6372 161.513 74.4622 153.3 66.4997 142.775C60.6247 134.313 56.7622 125.113 56.7622 125.113C56.7622 125.113 25.3872 125.538 10.8247 107.688C2.93718 98.6875 1.28718 88.3375 0.74968 84.6125C0.74968 84.5125 0.733013 84.3917 0.69968 84.25C0.0746797 80.2 -2.86282 60.8375 10.9622 48.5C17.4247 42.3 25.3122 40.525 28.0872 39.95C32.7247 38.9375 36.4247 39.2 39.3747 39.4L40.4997 39.475C45.3872 39.9125 90.3747 42.175 90.3747 42.175C90.3747 42.175 109.525 43 118.75 42.875C118.75 42.875 118.787 66.0375 118.787 77.625C119.662 78.025 120.541 78.4417 121.425 78.875L124.075 80.125V42.8375C128.191 42.7958 132.341 42.725 136.525 42.625H136.662C155.975 42.175 193.262 40.075 193.262 40.075H193.25ZM26.4122 100.325C26.4122 100.325 29.9122 103.575 38.1622 106.288C43.5372 108.188 51.8372 109.175 51.8372 109.175C51.8372 109.175 46.2372 93.75 43.9497 84.4625C41.1997 73.3875 38.9497 54.4875 38.9497 54.4875C38.9497 54.4875 33.4747 54.3 29.0872 55.475C19.5122 57.85 16.8372 65.0125 16.8372 65.0125C16.8372 65.0125 12.0372 73.6125 17.2872 87.675C20.3372 96.075 26.4122 100.338 26.4122 100.338V100.325ZM127.462 145.413C131.762 145.125 133.7 140.513 133.7 140.513C133.7 140.513 149.2 109.438 151.2 104.538C151.975 102.822 152.177 100.902 151.775 99.0625C150.9 95.725 146.9 93.9125 146.9 93.9125L122.825 82.225L120.762 86.4625L118.512 91.075C119.055 91.6392 119.477 92.3082 119.752 93.0412C120.027 93.7741 120.15 94.5555 120.112 95.3375C120.112 95.3375 125.525 97.6625 129.4 100.175C129.4 100.175 132.612 101.863 133.4 105.488C134.337 108.9 132.9 111.588 132.575 112.225L132.55 112.263C132.55 112.263 129.85 118.638 128.262 121.938L128.212 122.025C127.629 123.225 127.05 124.392 126.475 125.525C127.351 126.498 127.863 127.745 127.925 129.053C127.986 130.361 127.594 131.65 126.814 132.702C126.034 133.754 124.914 134.503 123.644 134.823C122.374 135.144 121.033 135.015 119.847 134.459C118.662 133.903 117.705 132.955 117.139 131.774C116.573 130.593 116.432 129.253 116.742 127.98C117.051 126.708 117.791 125.582 118.836 124.792C119.88 124.003 121.166 123.6 122.475 123.65C122.475 123.65 127.6 113.15 128.325 110.738C128.325 110.738 129.525 107.738 128.925 105.988C128.377 104.991 127.552 104.174 126.55 103.638C123.817 102.044 120.986 100.624 118.075 99.3875C118.075 99.3875 117.125 100.238 115.825 100.513C115.18 100.676 114.513 100.735 113.85 100.688L106.212 116.313C107.151 117.275 107.722 118.537 107.827 119.877C107.933 121.218 107.564 122.553 106.787 123.65C105.981 124.746 104.813 125.52 103.491 125.836C102.168 126.152 100.776 125.988 99.5622 125.375C98.4442 124.78 97.554 123.833 97.0296 122.68C96.5052 121.527 96.3761 120.234 96.6622 119C96.9661 117.765 97.6708 116.666 98.666 115.874C99.6612 115.083 100.891 114.643 102.162 114.625L110 98.575C109.319 97.7316 108.892 96.7119 108.769 95.635C108.646 94.5582 108.832 93.4685 109.305 92.4934C109.779 91.5182 110.519 90.6978 111.441 90.1279C112.363 89.5581 113.428 89.2622 114.512 89.275L116.825 84.5875L118.912 80.325L111.675 76.8125C111.675 76.8125 108.537 75.25 105.95 75.9125C105.46 76.0268 104.982 76.1901 104.525 76.4C102.162 77.45 100.65 80.525 100.65 80.525L83.3497 116.163C83.3497 116.163 81.7997 119.338 82.4997 121.913C83.0997 125.063 86.5622 126.875 86.5622 126.875L122.487 144.375L124.175 145.05C124.175 145.05 125.6 145.55 127.45 145.425L127.462 145.413Z" fill="#F1F1F1"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 3.7 KiB |
3
public/links/linkedin.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="200" height="200" viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M158.333 25C162.754 25 166.993 26.7559 170.118 29.8816C173.244 33.0072 175 37.2464 175 41.6667V158.333C175 162.754 173.244 166.993 170.118 170.118C166.993 173.244 162.754 175 158.333 175H41.6667C37.2464 175 33.0072 173.244 29.8816 170.118C26.7559 166.993 25 162.754 25 158.333V41.6667C25 37.2464 26.7559 33.0072 29.8816 29.8816C33.0072 26.7559 37.2464 25 41.6667 25H158.333ZM154.167 154.167V110C154.167 102.795 151.304 95.885 146.21 90.7903C141.115 85.6955 134.205 82.8333 127 82.8333C119.917 82.8333 111.667 87.1667 107.667 93.6667V84.4167H84.4167V154.167H107.667V113.083C107.667 106.667 112.833 101.417 119.25 101.417C122.344 101.417 125.312 102.646 127.5 104.834C129.688 107.022 130.917 109.989 130.917 113.083V154.167H154.167ZM57.3333 71.3333C61.0464 71.3333 64.6073 69.8583 67.2328 67.2328C69.8583 64.6073 71.3333 61.0464 71.3333 57.3333C71.3333 49.5833 65.0833 43.25 57.3333 43.25C53.5982 43.25 50.016 44.7338 47.3749 47.3749C44.7338 50.016 43.25 53.5982 43.25 57.3333C43.25 65.0833 49.5833 71.3333 57.3333 71.3333ZM68.9167 154.167V84.4167H45.8333V154.167H68.9167Z" fill="#F1F1F1"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.2 KiB |
3
public/links/mail.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="200" height="200" viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M33.3334 166.667C28.75 166.667 24.8278 165.036 21.5667 161.775C18.3056 158.514 16.6722 154.589 16.6667 150V50C16.6667 45.4167 18.3 41.4945 21.5667 38.2334C24.8334 34.9723 28.7556 33.3389 33.3334 33.3334H166.667C171.25 33.3334 175.175 34.9667 178.442 38.2334C181.708 41.5 183.339 45.4223 183.333 50V150C183.333 154.583 181.703 158.508 178.442 161.775C175.181 165.042 171.256 166.672 166.667 166.667H33.3334ZM100 108.333L166.667 66.6667V50L100 91.6667L33.3334 50V66.6667L100 108.333Z" fill="#F1F1F1"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 616 B |
3
public/links/work.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="200" height="200" viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M33.3334 175C28.75 175 24.8278 173.369 21.5667 170.108C18.3056 166.847 16.6722 162.922 16.6667 158.333V66.6666C16.6667 62.0833 18.3 58.1611 21.5667 54.9C24.8334 51.6388 28.7556 50.0055 33.3334 50H66.6667V33.3333C66.6667 28.75 68.3 24.8277 71.5667 21.5666C74.8334 18.3055 78.7556 16.6722 83.3334 16.6666H116.667C121.25 16.6666 125.175 18.3 128.442 21.5666C131.708 24.8333 133.339 28.7555 133.333 33.3333V50H166.667C171.25 50 175.175 51.6333 178.442 54.9C181.708 58.1666 183.339 62.0888 183.333 66.6666V158.333C183.333 162.917 181.703 166.842 178.442 170.108C175.181 173.375 171.256 175.006 166.667 175H33.3334ZM83.3334 50H116.667V33.3333H83.3334V50Z" fill="#F1F1F1"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 783 B |
BIN
public/pp.ico
Normal file
|
After Width: | Height: | Size: 217 KiB |
BIN
public/pp.png
Normal file
|
After Width: | Height: | Size: 158 KiB |
40
src/App.jsx
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import { useState } from "react"
|
||||||
|
import Header from "./Components/Header"
|
||||||
|
import { BrowserRouter, Routes, Route } from "react-router"
|
||||||
|
import Home from './Pages/Home'
|
||||||
|
import Blog from "./Pages/Blog/Blog"
|
||||||
|
import BlogArticle from "./Pages/Blog/BlogArticle"
|
||||||
|
import Event from "./Pages/Event"
|
||||||
|
import Walkthroughs from "./Pages/Walkthroughs/Walkthroughs"
|
||||||
|
import WalkthroughsArticle from "./Pages/Walkthroughs/WalkthroughsArticles"
|
||||||
|
import Music from "./Pages/Music"
|
||||||
|
import Hamburger from 'hamburger-react'
|
||||||
|
|
||||||
|
function App() {
|
||||||
|
|
||||||
|
const [lang, setlang] = useState('fr')
|
||||||
|
const [isOpen, setOpen] = useState(false)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<BrowserRouter>
|
||||||
|
<Header setLang={setlang} setOpen={setOpen} isOpen={isOpen}/>
|
||||||
|
<Hamburger toggled={isOpen} toggle={setOpen}/>
|
||||||
|
|
||||||
|
|
||||||
|
<Routes>
|
||||||
|
<Route path="/" element={<Home lang={lang}/>} />
|
||||||
|
<Route path="/blog" element={<Blog lang={lang}/>} />
|
||||||
|
<Route path="/blog/article" element={<BlogArticle lang={lang}/>} />
|
||||||
|
<Route path="/event" element={<Event lang={lang}/>} />
|
||||||
|
<Route path="/walkthroughs" element={<Walkthroughs lang={lang}/>} />
|
||||||
|
<Route path="/walkthroughs/article" element={<WalkthroughsArticle lang={lang}/>} />
|
||||||
|
<Route path="/music" element={<Music />} />
|
||||||
|
</Routes>
|
||||||
|
</BrowserRouter>
|
||||||
|
</>
|
||||||
|
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default App
|
||||||
25
src/Components/Header.jsx
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { Link } from "react-router"
|
||||||
|
import {useState} from "react"
|
||||||
|
|
||||||
|
export default function Header({setLang, isOpen, setOpen}){
|
||||||
|
return(
|
||||||
|
<header onClick={()=>{setOpen(false)}} className={isOpen == true ?`Header` : `HeaderActive`} >
|
||||||
|
<div className={`HeaderLogo`} ><button><Link to="/">[Mrsh]</Link></button></div>
|
||||||
|
<div className="HeaderLang">
|
||||||
|
<button onClick={()=>{setLang('fr')}}>fr</button>
|
||||||
|
<span>/</span>
|
||||||
|
<button onClick={()=>{setLang('en')}}>en</button>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<ul className="Headernav">
|
||||||
|
<li><Link to="/blog">Blog</Link></li>
|
||||||
|
<li><Link to="/event">Event</Link></li>
|
||||||
|
<li><Link to="/music">Music</Link></li>
|
||||||
|
<li><Link to="walkthroughs">Walkthroughs</Link></li>
|
||||||
|
<li><Link to="/#Links">Links</Link></li>
|
||||||
|
<li><a target="blank" href="http://mrsh.online">Gallery</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
)
|
||||||
|
}
|
||||||
50
src/Pages/Blog/Blog.jsx
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
import { Link } from "react-router"
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
|
export default function Blog({lang}){
|
||||||
|
const [articles, setArticles] = useState([])
|
||||||
|
const [loading, setLoading] = useState(true)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
|
||||||
|
fetch(`${import.meta.env.VITE_DIRECTUS_URL}/items/Blog`)
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(data => {setArticles(data.data), setLoading(false), console.log(data.data)})
|
||||||
|
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
if(loading === true){
|
||||||
|
return(
|
||||||
|
<span className="Loading">
|
||||||
|
Loading
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return(
|
||||||
|
<section>
|
||||||
|
<div className="breadcrumb">
|
||||||
|
<Link to="/">Home</Link>
|
||||||
|
<span>/</span>
|
||||||
|
<h1>Blog</h1>
|
||||||
|
</div>
|
||||||
|
<div className="separator"></div>
|
||||||
|
{articles?.map((e,i)=>{return(
|
||||||
|
<Link to={`/blog/article?id=${e.id}`} key={`article${i}${lang}`} className="BlogElement">
|
||||||
|
<h2>Titre</h2>
|
||||||
|
<div className="separator"></div>
|
||||||
|
<div className="HomeImg">
|
||||||
|
<img src={`${import.meta.env.VITE_DIRECTUS_URL + "/assets/" + e?.presentation }`} alt="" />
|
||||||
|
|
||||||
|
</div>
|
||||||
|
{lang === "fr" ?
|
||||||
|
<h3>{e.title}</h3>
|
||||||
|
: <h3>{e.title_en}</h3>}
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
)})}
|
||||||
|
|
||||||
|
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
}
|
||||||
56
src/Pages/Blog/BlogArticle.jsx
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
import { Link, useSearchParams } from "react-router"
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import Markdown from 'react-markdown'
|
||||||
|
|
||||||
|
export default function BlogArticle({lang}){
|
||||||
|
|
||||||
|
const [article, setArticle] = useState({});
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
const [searchParams] = useSearchParams();
|
||||||
|
const id = searchParams.get("id")
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetch(`${import.meta.env.VITE_DIRECTUS_URL}/items/Blog/${id}`)
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(data => {setArticle(data.data), setLoading(false), console.log(data.data)})
|
||||||
|
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
if (!id) return <p>No ID provided</p>
|
||||||
|
|
||||||
|
if(loading === true){
|
||||||
|
return(
|
||||||
|
<span className="Loading">
|
||||||
|
Loading
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return(
|
||||||
|
<section>
|
||||||
|
<div className="breadcrumb">
|
||||||
|
<Link to="/">Home</Link>
|
||||||
|
<span>/</span>
|
||||||
|
<Link to="/blog">Blog</Link>
|
||||||
|
<span>/</span>
|
||||||
|
<Link key={`title ${lang}`} to="/blog">{lang == "fr" ? article.title : article.title_en}</Link>
|
||||||
|
</div>
|
||||||
|
<div className="separator"></div>
|
||||||
|
|
||||||
|
<div key={`article ${lang}`} className="BlogElement">
|
||||||
|
<h2>{article?.title}</h2>
|
||||||
|
<div className="separator"></div>
|
||||||
|
<div className="HomeImg">
|
||||||
|
<img src={`${import.meta.env.VITE_DIRECTUS_URL + "/assets/" + article?.presentation }`} alt="" />
|
||||||
|
</div>
|
||||||
|
<Markdown>
|
||||||
|
{lang == "fr" ? article.content : article.content_en}
|
||||||
|
</Markdown>
|
||||||
|
<div className="HomeLink">
|
||||||
|
<Link to="/blog">Retour</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
}
|
||||||
48
src/Pages/Event.jsx
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import { Link } from "react-router"
|
||||||
|
import {useState, useEffect} from 'react'
|
||||||
|
|
||||||
|
export default function Event({lang}){
|
||||||
|
|
||||||
|
const [events, setEvents] = useState([])
|
||||||
|
const [past, setPast] = useState([])
|
||||||
|
const [future, setFuture] = useState([])
|
||||||
|
|
||||||
|
//for today date
|
||||||
|
const today = new Date();
|
||||||
|
const yyyy = today.getFullYear();
|
||||||
|
const mm = String(today.getMonth() + 1).padStart(2, '0'); // Months are zero-based
|
||||||
|
const dd = String(today.getDate()).padStart(2, '0');
|
||||||
|
const formattedDate = `${yyyy}-${mm}-${dd}`;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetch(`${import.meta.env.VITE_DIRECTUS_URL}/items/Event`)
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(data => setEvents(data.data))
|
||||||
|
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
console.log(future)
|
||||||
|
console.log(formattedDate)
|
||||||
|
|
||||||
|
}, [future])
|
||||||
|
|
||||||
|
return(
|
||||||
|
<section>
|
||||||
|
<div className="breadcrumb">
|
||||||
|
<span>Home</span>
|
||||||
|
<span>/</span>
|
||||||
|
<span>Event</span>
|
||||||
|
</div>
|
||||||
|
<div className="separator"></div>
|
||||||
|
|
||||||
|
{events?.map((e,i)=>{return(
|
||||||
|
|
||||||
|
<div key={`past${i}`} className="BlogElement">
|
||||||
|
<p>{e.date} {e.title} @ {e.place} | <a href={`${e.link}`}>{lang == "fr" ? "Lien" : "Link"}</a></p>
|
||||||
|
</div>
|
||||||
|
)})}
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
}
|
||||||
192
src/Pages/Home.jsx
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { Link } from "react-router"
|
||||||
|
|
||||||
|
export default function Home({lang}){
|
||||||
|
|
||||||
|
const [blogHome, setBlogHome] = useState({})
|
||||||
|
const [eventHome, setEventHome] = useState({})
|
||||||
|
const [friendHome, setFriendHome] = useState([])
|
||||||
|
const [walkthroughtHome, setWalkthroughtHome] = useState({})
|
||||||
|
const [loading, setLoading] = useState(false)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetch(`${import.meta.env.VITE_DIRECTUS_URL}/items/Blog`)
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(data => setBlogHome(data.data[0]))
|
||||||
|
|
||||||
|
fetch(`${import.meta.env.VITE_DIRECTUS_URL}/items/Event`)
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(data => setEventHome(data.data[0]))
|
||||||
|
|
||||||
|
fetch(`${import.meta.env.VITE_DIRECTUS_URL}/items/Walkthrought`)
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(data => setWalkthroughtHome(data.data[0]))
|
||||||
|
|
||||||
|
fetch(`${import.meta.env.VITE_DIRECTUS_URL}/items/Friend`)
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(data => setFriendHome(data.data))
|
||||||
|
|
||||||
|
setLoading(false)
|
||||||
|
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
console.log(walkthroughtHome,"hello")
|
||||||
|
|
||||||
|
}, [walkthroughtHome])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if(loading === true){
|
||||||
|
return(
|
||||||
|
<span className="Loading">
|
||||||
|
Loading
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="Home" key={`Home${lang}`}>
|
||||||
|
<div className="HomePresentation">
|
||||||
|
<div className="HomePresentationText">
|
||||||
|
<h1>[Mrsh]</h1>
|
||||||
|
<h2>TECH / MUSIC</h2>
|
||||||
|
{lang === "fr" ?
|
||||||
|
<p key={`hello${lang}`}>Salut 🙌🏿 , moi c'est Marsha. Je suis développeuse web et apprentie admin sys. Je suis fan de trop de trucs et j'en fais un blog, mais surtout des ordinateurs et la musique. On peut me retrouver ici ou en train de faire un moshpit.</p>
|
||||||
|
: <p>Hi, 🙌🏿 I'm Marsha. I'm a web developer and a trainee system administrator. I'm a fan of so many things, and I write a blog about them, but mainly about computers and music. You can find me here or in the middle of a mosh pit.</p> }
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="HomePresentationProfile">
|
||||||
|
<img src="/pp.png" alt="" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="HomeElement">
|
||||||
|
<h2>BLOG</h2>
|
||||||
|
<div className="separator"></div>
|
||||||
|
<Link to={`blog/article?id=${blogHome.id}`} className="HomeElement">
|
||||||
|
<div className="HomeImg">
|
||||||
|
<img src={`${import.meta.env.VITE_DIRECTUS_URL + "/assets/" + blogHome?.presentation }`} alt="" />
|
||||||
|
</div>
|
||||||
|
{lang === "fr" ?
|
||||||
|
<h3>{blogHome.title}</h3>
|
||||||
|
: <h3>{blogHome.title_en}</h3>}
|
||||||
|
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<div className="HomeLink">
|
||||||
|
<Link to="/blog">{lang == "fr" ? "Voir plus" : "See more" }</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="HomeElement">
|
||||||
|
<h2>EVENT</h2>
|
||||||
|
<div className="separator"></div>
|
||||||
|
<div className="HomeImg">
|
||||||
|
<img src={`${import.meta.env.VITE_DIRECTUS_URL + "/assets/" + eventHome?.image }`} alt="" />
|
||||||
|
</div>
|
||||||
|
<a href={`${eventHome.link}`} target="_blank">
|
||||||
|
<h3>
|
||||||
|
{eventHome.date} | {eventHome.title} @ {eventHome.place}
|
||||||
|
</h3>
|
||||||
|
</a>
|
||||||
|
<div className="HomeLink">
|
||||||
|
<Link to="/event">{lang == "fr" ? "Voir plus" : "See more" }</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="HomeElement">
|
||||||
|
<h2>Music</h2>
|
||||||
|
<div className="separator"></div>
|
||||||
|
<iframe margin="50px" width="100%" height="300" scrolling="no" frameborder="no" allow="autoplay" src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/soundcloud%253Atracks%253A2202724747&color=%23fb74e0&auto_play=false&hide_related=false&show_comments=true&show_user=true&show_reposts=false&show_teaser=true&visual=true"></iframe>
|
||||||
|
<h3>Opps Canem - 2025 </h3>
|
||||||
|
|
||||||
|
<div className="HomeLink">
|
||||||
|
<Link to="/music">{lang == "fr" ? "Voir plus" : "See more" }</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="HomeElement">
|
||||||
|
<h2>WALKTHROUGHTS</h2>
|
||||||
|
<div className="separator"></div>
|
||||||
|
{walkthroughtHome == null ?
|
||||||
|
<span className="Loading">
|
||||||
|
Under construction...
|
||||||
|
</span>
|
||||||
|
:
|
||||||
|
<Link to={`walkthroughs/article?id=${walkthroughtHome.id}`} className="HomeElement">
|
||||||
|
<div className="HomeImg">
|
||||||
|
<img src={`${import.meta.env.VITE_DIRECTUS_URL + "/assets/" + walkthroughtHome?.image }`} alt="" />
|
||||||
|
</div>
|
||||||
|
{lang === "fr" ?
|
||||||
|
<>
|
||||||
|
<h3>{walkthroughtHome.title}</h3>
|
||||||
|
<p>{walkthroughtHome.summary}</p>
|
||||||
|
</>
|
||||||
|
:
|
||||||
|
<>
|
||||||
|
<h3>{walkthroughtHome.title_en}</h3>
|
||||||
|
<p>{walkthroughtHome.summary_en}</p>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
|
||||||
|
<div className="HomeLink">
|
||||||
|
<Link to="/walkthroughs">{lang == "fr" ? "Voir plus" : "See more" }</Link>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div className="HomeElement">
|
||||||
|
<h2>FRIENDS</h2>
|
||||||
|
<div className="separator"></div>
|
||||||
|
<div className="HomeElementFriend">
|
||||||
|
{friendHome?.map((e,i)=>{return(
|
||||||
|
<a href={`${e.link}`} target="blank" key={`friend${i}`} className="HomeElementFriendContainer">
|
||||||
|
<div className="HomeElementFriendContainerImage">
|
||||||
|
<img src={`${import.meta.env.VITE_DIRECTUS_URL + "/assets/" + e?.image }`} alt="" />
|
||||||
|
</div>
|
||||||
|
<h4>{e.name}</h4>
|
||||||
|
{lang === "fr" ?
|
||||||
|
<p>{e.summary}</p>
|
||||||
|
:
|
||||||
|
<p>{e.summary_en}</p>
|
||||||
|
}
|
||||||
|
</a>
|
||||||
|
)})}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="HomeElement" id="Links">
|
||||||
|
<h2>LINKS</h2>
|
||||||
|
<div className="separator"></div>
|
||||||
|
<div className="Links">
|
||||||
|
<a href="www.linkedin.com/in/marsha-walungua-b53295383" target="_blank" className="LinksContainer">
|
||||||
|
<img src="/assets/links/linkedin.svg"/>
|
||||||
|
<p>Linkedin</p>
|
||||||
|
</a>
|
||||||
|
<a href="http://gitea.mrsh.online/mrsh" target="_blank" className="LinksContainer">
|
||||||
|
<img src="/assets/links/gitea.svg"/>
|
||||||
|
<p>Gitea</p>
|
||||||
|
</a>
|
||||||
|
<a href="mailto:@mrsh-online@proton.me" className="LinksContainer">
|
||||||
|
<img src="/assets/links/mail.svg"/>
|
||||||
|
<p>Mail</p>
|
||||||
|
</a>
|
||||||
|
<a href="https://mrsh-online.github.io/portfolio-marsha-walungua/" target="_blank" className="LinksContainer">
|
||||||
|
<img src="/assets/links/work.svg"/>
|
||||||
|
<p>Portfolio</p>
|
||||||
|
</a>
|
||||||
|
<a href="https://bsky.app/profile/mrsh-online.bsky.social" target="_blank" className="LinksContainer">
|
||||||
|
<img src="/assets/links/bluesky.svg"/>
|
||||||
|
<p>Bluesky</p>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
26
src/Pages/Music.jsx
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import { Link } from "react-router"
|
||||||
|
|
||||||
|
export default function Music (){
|
||||||
|
return(
|
||||||
|
<div>
|
||||||
|
<div className="breadcrumb">
|
||||||
|
<Link to="/">Home</Link>
|
||||||
|
<span>/</span>
|
||||||
|
<h1>Music</h1>
|
||||||
|
</div>
|
||||||
|
<div className="separator"></div>
|
||||||
|
|
||||||
|
<h2>Opps Canem - 2025 - (Production)</h2>
|
||||||
|
<div className="separator"></div>
|
||||||
|
<iframe margin="50px" width="100%" height="300" scrolling="no" frameborder="no" allow="autoplay" src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/soundcloud%253Atracks%253A2202724747&color=%23fb74e0&auto_play=false&hide_related=false&show_comments=true&show_user=true&show_reposts=false&show_teaser=true&visual=true"></iframe>
|
||||||
|
|
||||||
|
<h2>Sooooo Dysfurric - 2025 - (Production)</h2>
|
||||||
|
<div className="separator"></div>
|
||||||
|
<iframe width="100%" height="300" scrolling="no" frameborder="no" allow="autoplay" src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/soundcloud%253Atracks%253A2202724767&color=%23fb74e0&auto_play=false&hide_related=false&show_comments=true&show_user=true&show_reposts=false&show_teaser=true&visual=true"></iframe>
|
||||||
|
|
||||||
|
<h2>Aïda - 2025 - (Bass)</h2>
|
||||||
|
<div className="separator"></div>
|
||||||
|
<iframe width="100%" height="315" src="https://www.youtube.com/embed/1_-DJ1sD1vY?si=idRHKs6aQYMfnIHA&controls=0" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
48
src/Pages/Walkthroughs/Walkthroughs.jsx
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import { Link } from "react-router"
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
|
export default function Walkthroughs({lang}){
|
||||||
|
const [articles, setArticles] = useState([])
|
||||||
|
const [loading, setLoading] = useState(true)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetch(`${import.meta.env.VITE_DIRECTUS_URL}/items/Walkthrought`)
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(data => {setArticles(data.data), setLoading(false), console.log(data.data)})
|
||||||
|
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
if(loading === true){
|
||||||
|
return(
|
||||||
|
<span className="Loading">
|
||||||
|
Loading
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return(
|
||||||
|
<section>
|
||||||
|
<div className="breadcrumb">
|
||||||
|
<Link to="/">Home</Link>
|
||||||
|
<span>/</span>
|
||||||
|
<h1>Walkthroughs</h1>
|
||||||
|
</div>
|
||||||
|
<div className="separator"></div>
|
||||||
|
{articles?.map((e,i)=>{return(
|
||||||
|
<Link to={`/walkthroughs/article?id=${e.id}`} key={`article${i}${lang}`} className="BlogElement">
|
||||||
|
<h2>Titre</h2>
|
||||||
|
<div className="separator"></div>
|
||||||
|
<div className="HomeImg">
|
||||||
|
<img src={`${import.meta.env.VITE_DIRECTUS_URL + "/assets/" + e?.image }`} alt="" />
|
||||||
|
</div>
|
||||||
|
{lang === "fr" ?
|
||||||
|
<h3>{e.title}</h3>
|
||||||
|
: <h3>{e.title_en}</h3>}
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
)})}
|
||||||
|
|
||||||
|
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
}
|
||||||
58
src/Pages/Walkthroughs/WalkthroughsArticles.jsx
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
import { Link, useSearchParams } from "react-router"
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import Markdown from 'react-markdown'
|
||||||
|
|
||||||
|
export default function WalkthroughsArticles({lang}){
|
||||||
|
|
||||||
|
const [article, setArticle] = useState({});
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
const [searchParams] = useSearchParams();
|
||||||
|
const id = searchParams.get("id")
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetch(`${import.meta.env.VITE_DIRECTUS_URL}/items/Walkthrought/${id}`)
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(data => {setArticle(data.data), setLoading(false), console.log(data.data)})
|
||||||
|
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
if (!id) return <p>No ID provided</p>
|
||||||
|
|
||||||
|
if(loading === true){
|
||||||
|
return(
|
||||||
|
<span className="Loading">
|
||||||
|
Loading
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return(
|
||||||
|
<section>
|
||||||
|
<div className="breadcrumb">
|
||||||
|
<Link to="/">Home</Link>
|
||||||
|
<span>/</span>
|
||||||
|
<Link to="/blog">Blog</Link>
|
||||||
|
<span>/</span>
|
||||||
|
<Link to="/blog">{article.title}</Link>
|
||||||
|
</div>
|
||||||
|
<div className="separator"></div>
|
||||||
|
|
||||||
|
<div className="BlogElement">
|
||||||
|
<h2>{article?.title}</h2>
|
||||||
|
<div className="separator"></div>
|
||||||
|
<div className="HomeImg">
|
||||||
|
<img src={`${import.meta.env.VITE_DIRECTUS_URL + "/assets/" + article?.image }`} alt="" />
|
||||||
|
</div>
|
||||||
|
<div className="prose">
|
||||||
|
<Markdown>
|
||||||
|
{lang == "fr" ? article.content : article.content_en}
|
||||||
|
</Markdown>
|
||||||
|
</div>
|
||||||
|
<div className="HomeLink">
|
||||||
|
<Link to="/blog">Retour</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
}
|
||||||
1
src/assets/react.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>
|
||||||
|
After Width: | Height: | Size: 4.0 KiB |
10
src/main.jsx
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { StrictMode } from 'react'
|
||||||
|
import { createRoot } from 'react-dom/client'
|
||||||
|
import App from './App.jsx'
|
||||||
|
import './main.sass'
|
||||||
|
|
||||||
|
createRoot(document.getElementById('root')).render(
|
||||||
|
<StrictMode>
|
||||||
|
<App />
|
||||||
|
</StrictMode>,
|
||||||
|
)
|
||||||
244
src/main.sass
Normal file
@@ -0,0 +1,244 @@
|
|||||||
|
@use "../node_modules/initialize-css/dist/initialize"
|
||||||
|
|
||||||
|
@font-face
|
||||||
|
font-family: 'alata'
|
||||||
|
src: url('/fonts/Alata/Alata-Regular.ttf')
|
||||||
|
|
||||||
|
@font-face
|
||||||
|
font-family: 'Vanitas'
|
||||||
|
src: url('/fonts/Vanitas/vanitas.ttf')
|
||||||
|
|
||||||
|
$background: #0E0E0E
|
||||||
|
$foreground: #F1F1F1
|
||||||
|
$accent: #FB74E0
|
||||||
|
|
||||||
|
@keyframes fadeIn
|
||||||
|
from
|
||||||
|
opacity: 0
|
||||||
|
transform: translateY(10px)
|
||||||
|
|
||||||
|
to
|
||||||
|
opacity: 1
|
||||||
|
transform: translateY(0px)
|
||||||
|
|
||||||
|
@mixin animation-fadeIn($duration: 1s, $timing: ease-in)
|
||||||
|
animation-name: fadeIn
|
||||||
|
animation-duration: $duration
|
||||||
|
animation-timing-function: $timing
|
||||||
|
animation-fill-mode: forwards
|
||||||
|
|
||||||
|
body
|
||||||
|
background-color: $background
|
||||||
|
|
||||||
|
#root
|
||||||
|
max-width: 1024px
|
||||||
|
margin: auto
|
||||||
|
|
||||||
|
.Loading
|
||||||
|
font-family: 'Vanitas'
|
||||||
|
font-size: 50px
|
||||||
|
margin: 150px auto
|
||||||
|
text-align: center
|
||||||
|
width: 100%
|
||||||
|
display: block
|
||||||
|
p, button
|
||||||
|
font-family: 'alata'
|
||||||
|
|
||||||
|
h1, h2, header button, header, h3
|
||||||
|
font-family: 'Vanitas'
|
||||||
|
margin: 0
|
||||||
|
padding: 0
|
||||||
|
h1
|
||||||
|
font-size: 128px
|
||||||
|
h2
|
||||||
|
font-size: 40px
|
||||||
|
|
||||||
|
.breadcrumb
|
||||||
|
margin-top: 200px
|
||||||
|
display: flex
|
||||||
|
gap: 10px
|
||||||
|
h1, span, a
|
||||||
|
font-size: 30px
|
||||||
|
font-family: 'Vanitas'
|
||||||
|
.separator
|
||||||
|
height: 1px
|
||||||
|
background-color: white
|
||||||
|
width: 100%
|
||||||
|
margin-bottom: 20px
|
||||||
|
|
||||||
|
button
|
||||||
|
all: unset
|
||||||
|
background: none
|
||||||
|
color: $foreground
|
||||||
|
border-radius: 5px
|
||||||
|
border: 1px solid $foreground
|
||||||
|
padding: 5px 10px
|
||||||
|
transition: 0.5s ease
|
||||||
|
&:hover
|
||||||
|
color: $accent
|
||||||
|
cursor: pointer
|
||||||
|
border: 1px solid $accent
|
||||||
|
|
||||||
|
a, p, span, h1, h2, h3, h4, h5
|
||||||
|
color: $foreground
|
||||||
|
text-decoration: none
|
||||||
|
@include animation-fadeIn(2s, ease)
|
||||||
|
|
||||||
|
a
|
||||||
|
transition: 0.5s ease-in-out
|
||||||
|
&:hover
|
||||||
|
color: $accent
|
||||||
|
.hamburger-react
|
||||||
|
position : fixed !important
|
||||||
|
top : 50px
|
||||||
|
z-index: 40
|
||||||
|
left: 5%
|
||||||
|
color: $foreground
|
||||||
|
|
||||||
|
|
||||||
|
.Header
|
||||||
|
&Active
|
||||||
|
opacity: 0
|
||||||
|
transform: translateY(-200%)
|
||||||
|
height: 0
|
||||||
|
position: fixed
|
||||||
|
top: 0
|
||||||
|
left: 0%
|
||||||
|
display: block
|
||||||
|
background-color: $background
|
||||||
|
z-index:5
|
||||||
|
font-size: 20px
|
||||||
|
height: 100%
|
||||||
|
display: flex
|
||||||
|
width: 100%
|
||||||
|
align-items: center
|
||||||
|
padding: 100px 0
|
||||||
|
transition: 0.6s ease
|
||||||
|
flex-direction: column
|
||||||
|
ul
|
||||||
|
display: flex
|
||||||
|
flex-direction: column
|
||||||
|
gap: 10px
|
||||||
|
padding: 0
|
||||||
|
li
|
||||||
|
text-align: center
|
||||||
|
list-style: none
|
||||||
|
button
|
||||||
|
background: none
|
||||||
|
border: none
|
||||||
|
&Logo
|
||||||
|
font-family: '04b_21'
|
||||||
|
font-size: 40px
|
||||||
|
|
||||||
|
.Home
|
||||||
|
&Presentation
|
||||||
|
display: flex
|
||||||
|
margin-top:100px
|
||||||
|
justify-content: space-between
|
||||||
|
align-items: center
|
||||||
|
flex-direction: column
|
||||||
|
&Profile
|
||||||
|
@include animation-fadeIn(2s, ease)
|
||||||
|
width: 40%
|
||||||
|
img
|
||||||
|
width: 100%
|
||||||
|
&Text
|
||||||
|
@include animation-fadeIn(2s, ease)
|
||||||
|
text-align: center
|
||||||
|
&Element
|
||||||
|
min-height: 40vh
|
||||||
|
margin: 50px 0
|
||||||
|
p
|
||||||
|
width: 80%
|
||||||
|
margin: 20px auto
|
||||||
|
h3
|
||||||
|
margin: 20px auto
|
||||||
|
transition: 0.6s ease
|
||||||
|
text-align: center
|
||||||
|
font-size: 40px
|
||||||
|
&:hover
|
||||||
|
h3
|
||||||
|
color: $accent
|
||||||
|
&Friend
|
||||||
|
&Container
|
||||||
|
width: 50%
|
||||||
|
padding: 20px
|
||||||
|
display: flex
|
||||||
|
flex-direction: column
|
||||||
|
gap: 10px
|
||||||
|
&:hover
|
||||||
|
h4
|
||||||
|
color: $accent
|
||||||
|
h4
|
||||||
|
transition: 0.6s ease
|
||||||
|
text-align: center
|
||||||
|
font-family: 'vanitas'
|
||||||
|
font-size: 30px
|
||||||
|
margin: 0
|
||||||
|
p
|
||||||
|
margin: 0
|
||||||
|
width: 100%
|
||||||
|
font-size: 20px
|
||||||
|
&Image
|
||||||
|
max-height: 50%
|
||||||
|
img
|
||||||
|
width: 100%
|
||||||
|
&Link
|
||||||
|
a
|
||||||
|
margin: 40px auto
|
||||||
|
border: 1px solid white
|
||||||
|
max-width: 200px
|
||||||
|
text-align: center
|
||||||
|
border-radius: 10px
|
||||||
|
padding: 10px
|
||||||
|
transition: 0.3 ease
|
||||||
|
cursor: pointer
|
||||||
|
display: block
|
||||||
|
&:hover
|
||||||
|
background-color: $foreground
|
||||||
|
&Img
|
||||||
|
object-fit: cover
|
||||||
|
max-width: 50%
|
||||||
|
margin: auto
|
||||||
|
img
|
||||||
|
width: 100%
|
||||||
|
|
||||||
|
.Links
|
||||||
|
display: flex
|
||||||
|
flex-direction: column
|
||||||
|
justify-content: center
|
||||||
|
align-items: center
|
||||||
|
text-align: center
|
||||||
|
p
|
||||||
|
font-family: 'Vanitas'
|
||||||
|
font-size: 35px
|
||||||
|
&Container
|
||||||
|
width: 200px
|
||||||
|
img
|
||||||
|
width: 100%
|
||||||
|
&:hover
|
||||||
|
cursor: pointer
|
||||||
|
img
|
||||||
|
color: $accent
|
||||||
|
transform: translate(0.05)
|
||||||
|
iframe
|
||||||
|
margin-bottom: 50px
|
||||||
|
|
||||||
|
.BlogElement
|
||||||
|
h2, h3, h4, h5
|
||||||
|
font-family: 'Vanitas'
|
||||||
|
text-align: center
|
||||||
|
img
|
||||||
|
display: block
|
||||||
|
max-width: 400px
|
||||||
|
margin: auto
|
||||||
|
|
||||||
|
//pour le text Markdown
|
||||||
|
.prose
|
||||||
|
img
|
||||||
|
max-height: 400px
|
||||||
|
display: block
|
||||||
|
margin: auto
|
||||||
|
|
||||||
|
|
||||||
|
//transform: translateX(-50%)
|
||||||
15
vite.config.js
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { defineConfig } from 'vite'
|
||||||
|
import react from '@vitejs/plugin-react'
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [react()],
|
||||||
|
server: {
|
||||||
|
host: '0.0.0.0',
|
||||||
|
port: 5173,
|
||||||
|
strictPort: true,
|
||||||
|
watch: {
|
||||||
|
usePolling: true, // Essential for Docker
|
||||||
|
interval: 1000
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||