Front-end panel #1
19 changed files with 4972 additions and 0 deletions
24
src/Guestbooky-admin/.gitignore
vendored
Normal file
24
src/Guestbooky-admin/.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?
|
38
src/Guestbooky-admin/eslint.config.js
Normal file
38
src/Guestbooky-admin/eslint.config.js
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
import js from '@eslint/js'
|
||||||
|
import globals from 'globals'
|
||||||
|
import react from 'eslint-plugin-react'
|
||||||
|
import reactHooks from 'eslint-plugin-react-hooks'
|
||||||
|
import reactRefresh from 'eslint-plugin-react-refresh'
|
||||||
|
|
||||||
|
export default [
|
||||||
|
{ ignores: ['dist'] },
|
||||||
|
{
|
||||||
|
files: ['**/*.{js,jsx}'],
|
||||||
|
languageOptions: {
|
||||||
|
ecmaVersion: 2020,
|
||||||
|
globals: globals.browser,
|
||||||
|
parserOptions: {
|
||||||
|
ecmaVersion: 'latest',
|
||||||
|
ecmaFeatures: { jsx: true },
|
||||||
|
sourceType: 'module',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
settings: { react: { version: '18.3' } },
|
||||||
|
plugins: {
|
||||||
|
react,
|
||||||
|
'react-hooks': reactHooks,
|
||||||
|
'react-refresh': reactRefresh,
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
...js.configs.recommended.rules,
|
||||||
|
...react.configs.recommended.rules,
|
||||||
|
...react.configs['jsx-runtime'].rules,
|
||||||
|
...reactHooks.configs.recommended.rules,
|
||||||
|
'react/jsx-no-target-blank': 'off',
|
||||||
|
'react-refresh/only-export-components': [
|
||||||
|
'warn',
|
||||||
|
{ allowConstantExport: true },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
13
src/Guestbooky-admin/index.html
Normal file
13
src/Guestbooky-admin/index.html
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" type="image/png" href="/src/assets/guestbooky.png" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Guestbooky Admin Panel</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
<script type="module" src="/src/main.jsx"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
4391
src/Guestbooky-admin/package-lock.json
generated
Normal file
4391
src/Guestbooky-admin/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
30
src/Guestbooky-admin/package.json
Normal file
30
src/Guestbooky-admin/package.json
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
{
|
||||||
|
"name": "guestbooky-admin",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "vite build",
|
||||||
|
"lint": "eslint .",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"axios": "^1.7.7",
|
||||||
|
"guestbooky-admin": "file:",
|
||||||
|
"react": "^18.3.1",
|
||||||
|
"react-dom": "^18.3.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@eslint/js": "^9.13.0",
|
||||||
|
"@types/react": "^18.3.11",
|
||||||
|
"@types/react-dom": "^18.3.1",
|
||||||
|
"@vitejs/plugin-react": "^4.3.3",
|
||||||
|
"eslint": "^9.13.0",
|
||||||
|
"eslint-plugin-react": "^7.37.1",
|
||||||
|
"eslint-plugin-react-hooks": "^5.0.0",
|
||||||
|
"eslint-plugin-react-refresh": "^0.4.13",
|
||||||
|
"globals": "^15.11.0",
|
||||||
|
"vite": "^5.4.9"
|
||||||
|
}
|
||||||
|
}
|
24
src/Guestbooky-admin/src/App.css
Normal file
24
src/Guestbooky-admin/src/App.css
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
.header{
|
||||||
|
font-size: 3em;
|
||||||
|
word-spacing: 0.3em;
|
||||||
|
letter-spacing: -0.05em;
|
||||||
|
background: linear-gradient(180deg, hsl(15, 80%, 10%) 0%, hsl(15, 50%, 60%) 100%);
|
||||||
|
width: 100%;
|
||||||
|
padding: 2ch;
|
||||||
|
border-bottom: 0.1em solid hsl(15, 80%, 20%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.header h1 {
|
||||||
|
margin-inline: auto;
|
||||||
|
width: 1200px;
|
||||||
|
color: hsl(15, 100%, 90%);
|
||||||
|
text-shadow: 0.01em 0.01em 0.01em hsl(15, 0%, 20%);
|
||||||
|
font-family: "Carattere", cursive;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
width: 1200px;
|
||||||
|
max-width: 1200px;
|
||||||
|
margin-inline: auto;
|
||||||
|
padding: 2ch;
|
||||||
|
}
|
24
src/Guestbooky-admin/src/App.jsx
Normal file
24
src/Guestbooky-admin/src/App.jsx
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import {useState} from 'react'
|
||||||
|
import AdminPage from './components/AdminPage'
|
||||||
|
import LoginPage from './components/LoginPage'
|
||||||
|
|
||||||
|
import './App.css'
|
||||||
|
|
||||||
|
function App() {
|
||||||
|
const [isLoggedIn, setIsLoggedIn] = useState(false)
|
||||||
|
|
||||||
|
const handleLoggedIn = (newValue) => {
|
||||||
|
setIsLoggedIn(newValue)
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="header"><h1>Guestbooky Admin</h1></div>
|
||||||
|
<div className="content">
|
||||||
|
{isLoggedIn ? <AdminPage className="content"/> :
|
||||||
|
<LoginPage onLoggedIn={handleLoggedIn} className="content"/>}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default App
|
BIN
src/Guestbooky-admin/src/assets/guestbooky.png
Normal file
BIN
src/Guestbooky-admin/src/assets/guestbooky.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
111
src/Guestbooky-admin/src/components/AdminPage.css
Normal file
111
src/Guestbooky-admin/src/components/AdminPage.css
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
@keyframes smallFadeIn {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(20px);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-item {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(20px);
|
||||||
|
animation: smallFadeIn 0.5s ease-out forwards;
|
||||||
|
animation-delay: calc(0.2s * var(--child-index));
|
||||||
|
}
|
||||||
|
|
||||||
|
.warning{
|
||||||
|
background-color: hsla(15, 100%, 90%, 0.75);
|
||||||
|
border: 0.2em solid hsla(15, 80%, 70%, 1);
|
||||||
|
border-radius: 0.3em;
|
||||||
|
padding: 2ch 1ch;
|
||||||
|
width: fit-content;
|
||||||
|
font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navigation{
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navigation button {
|
||||||
|
font-size: 1em;
|
||||||
|
margin: 2em;
|
||||||
|
background: hsla(15, 60%, 80%, 1);
|
||||||
|
border-radius: 0.2em;
|
||||||
|
width: fit-content;
|
||||||
|
padding: 0.8em 0.5em;
|
||||||
|
font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navigation button:hover {
|
||||||
|
background: hsla(15, 80%, 90%, 0.75);
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
list-style-type: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul li{
|
||||||
|
line-height: 1.6em;
|
||||||
|
display: grid;
|
||||||
|
grid-row-gap: 2em;
|
||||||
|
padding: 1em;
|
||||||
|
grid-template-rows: auto 1fr;
|
||||||
|
grid-template-columns: 1fr auto auto;
|
||||||
|
font-family: 'Solway', serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul li h2{
|
||||||
|
align-self: center;
|
||||||
|
grid-row: 1;
|
||||||
|
background: linear-gradient(to right, hsl(15, 40%, 85%), hsla(15, 40%, 85%, 0));
|
||||||
|
border-radius: 0.2em;
|
||||||
|
padding: 0.5em 0 0.5em 0.5em;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul li h3{
|
||||||
|
align-self: center;
|
||||||
|
grid-row: 1;
|
||||||
|
justify-self: right;
|
||||||
|
padding-right: 0.5em;
|
||||||
|
vertical-align: bottom;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ul li button{
|
||||||
|
grid-row: 1;
|
||||||
|
justify-self: right;
|
||||||
|
width: fit-content;
|
||||||
|
border-radius: 0.5em;
|
||||||
|
padding: 0.5em;
|
||||||
|
font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace;
|
||||||
|
background: hsla(15, 60%, 80%, 0.75);
|
||||||
|
}
|
||||||
|
ul li button:hover {
|
||||||
|
background: hsla(15, 80%, 90%, 0.75);
|
||||||
|
}
|
||||||
|
.message-text {
|
||||||
|
grid-row: 2;
|
||||||
|
grid-column: 1 / 4;
|
||||||
|
background-color: hsl(15, 50%, 90%);
|
||||||
|
margin: 1em 0.5em;
|
||||||
|
padding: 1em 0.5em;
|
||||||
|
font-weight: 300;
|
||||||
|
border-radius: 0.5em;
|
||||||
|
box-shadow: 0.05em 0.05em 0.05em hsla(15, 20%, 20%, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-text p{
|
||||||
|
line-height: 1.5em;
|
||||||
|
text-align: justify;
|
||||||
|
text-justify: inter-word;
|
||||||
|
text-align-last: left;
|
||||||
|
}
|
51
src/Guestbooky-admin/src/components/AdminPage.jsx
Normal file
51
src/Guestbooky-admin/src/components/AdminPage.jsx
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
import useMessages from "../hooks/useMessages.js";
|
||||||
|
|
||||||
|
import './AdminPage.css'
|
||||||
|
|
||||||
|
|
||||||
|
const AdminPage = () => {
|
||||||
|
const {messages, totalMessages, loading, error, removeMessage, page, nextPage, previousPage} = useMessages();
|
||||||
|
const localeOptions = {
|
||||||
|
weekday: 'long',
|
||||||
|
year: 'numeric',
|
||||||
|
month: 'long',
|
||||||
|
day: 'numeric',
|
||||||
|
hour: 'numeric',
|
||||||
|
minute: 'numeric',
|
||||||
|
second: 'numeric',
|
||||||
|
timeZoneName: 'short'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return (<div className='warning'><p>Loading...</p></div>);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (messages.length === 0 && page > 1) {
|
||||||
|
previousPage();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (messages.length === 0) {
|
||||||
|
return (<div className='warning'><p>No messages.</p></div>);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className='warning'><p>{error ? 'Error : ' + error : 'Total messages: ' + totalMessages }</p></div>
|
||||||
|
<ul>
|
||||||
|
{messages.length > 0 ? messages.map((message, index) => (
|
||||||
|
<li key={message.id} className='list-item' style={{ '--child-index': index}} >
|
||||||
|
<h2>{message.author}</h2>
|
||||||
|
<h3>{new Date(message.timestamp).toLocaleString(navigator.language, localeOptions)}</h3>
|
||||||
|
<button onClick={() => removeMessage(message.id)}>Delete</button>
|
||||||
|
<div className='message-text'><p>{message.message}</p></div>
|
||||||
|
</li>
|
||||||
|
)) : ''}
|
||||||
|
</ul>
|
||||||
|
<div className='navigation'>
|
||||||
|
<button onClick={() => previousPage()}>Previous Page</button>
|
||||||
|
<button onClick={() => nextPage()}>Next Page</button>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default AdminPage;
|
64
src/Guestbooky-admin/src/components/LoginPage.css
Normal file
64
src/Guestbooky-admin/src/components/LoginPage.css
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
.login-form{
|
||||||
|
display: flex;
|
||||||
|
margin-inline: auto;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
vertical-align: center;
|
||||||
|
margin-top: 20vh;
|
||||||
|
background-color: hsla(15, 100%, 93%);
|
||||||
|
width: fit-content;
|
||||||
|
padding: 2em;
|
||||||
|
border-radius: 1em;
|
||||||
|
box-shadow: 0.05em 0.05em 0.05em hsla(15, 20%, 20%, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.fade-in{
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(10vh);
|
||||||
|
animation: fadeIn 0.5s ease-out forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeIn {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(20vh);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
form {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: auto 20ch;
|
||||||
|
grid-column-gap: 2ch;
|
||||||
|
grid-row-gap: 2ch;
|
||||||
|
align-items: center;
|
||||||
|
font-family: 'Solway', serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
form label {
|
||||||
|
font-size: 1.4em;
|
||||||
|
text-align: right;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
form input[type=text], form input[type=password] {
|
||||||
|
font-size: 1.4em;
|
||||||
|
border: 0.2em hsl(15, 100%, 50%) outset;
|
||||||
|
border-radius: 0.2em;
|
||||||
|
padding: 0.2em 0.2em;
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
form button {
|
||||||
|
margin-top: 1em;
|
||||||
|
grid-column: 2 / 3;
|
||||||
|
padding: 1em 2em;
|
||||||
|
font-size: 1.4em;
|
||||||
|
border-radius: 0.2em;
|
||||||
|
background-color: hsl(15, 50%, 85%);
|
||||||
|
font-family: 'Solway', serif;
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
40
src/Guestbooky-admin/src/components/LoginPage.jsx
Normal file
40
src/Guestbooky-admin/src/components/LoginPage.jsx
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
import React, { useRef } from 'react';
|
||||||
|
import useAuth from '../hooks/useAuth.js';
|
||||||
|
|
||||||
|
import './LoginPage.css'
|
||||||
|
|
||||||
|
const LoginPage = ({onLoggedIn}) =>
|
||||||
|
{
|
||||||
|
const userInputRef = useRef(null);
|
||||||
|
const passInputRef = useRef(null);
|
||||||
|
const { authenticate, error } = useAuth();
|
||||||
|
|
||||||
|
const handleSubmit = (e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
const user = userInputRef.current.value;
|
||||||
|
const pass = passInputRef.current.value;
|
||||||
|
|
||||||
|
authenticate({user, pass})
|
||||||
|
.then(() => {
|
||||||
|
if (error === null) {
|
||||||
|
onLoggedIn(true)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return(
|
||||||
|
<>
|
||||||
|
<div className="login-form fade-in">
|
||||||
|
<form action="post" onSubmit={handleSubmit}>
|
||||||
|
<label>User: </label><input type='text' name='username' placeholder=''
|
||||||
|
ref={userInputRef}></input>
|
||||||
|
<label>Password: </label><input type='password' name='password' placeholder=''
|
||||||
|
ref={passInputRef}></input>
|
||||||
|
<button type='submit' className='login-button'>Login</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LoginPage;
|
1
src/Guestbooky-admin/src/environment/constants.js
Normal file
1
src/Guestbooky-admin/src/environment/constants.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export const API_URL = 'http://localhost:8080';
|
19
src/Guestbooky-admin/src/hooks/useAuth.js
Normal file
19
src/Guestbooky-admin/src/hooks/useAuth.js
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import { useState } from "react";
|
||||||
|
import { post } from '../services/httpService.js'
|
||||||
|
|
||||||
|
const useAuth = () => {
|
||||||
|
const [error, setError] = useState(null);
|
||||||
|
|
||||||
|
const authenticate = async ({user, pass}) => {
|
||||||
|
try {
|
||||||
|
await post('/auth/login', {username: user, password: pass});
|
||||||
|
}
|
||||||
|
catch(err){
|
||||||
|
setError(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { authenticate, error };
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useAuth;
|
67
src/Guestbooky-admin/src/hooks/useMessages.js
Normal file
67
src/Guestbooky-admin/src/hooks/useMessages.js
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
import { useState, useEffect } from "react";
|
||||||
|
import { get, del } from '../services/httpService.js'
|
||||||
|
|
||||||
|
const useMessages = () => {
|
||||||
|
const [messages, setMessages] = useState([]);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
const [error, setError] = useState(null);
|
||||||
|
const [totalMessages, setTotalMessages] = useState(0);
|
||||||
|
const [page, setPage] = useState(1);
|
||||||
|
const amountPerPage = 10;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchMessages = async () => {
|
||||||
|
try{
|
||||||
|
setLoading(true);
|
||||||
|
const data = await get('/message', {
|
||||||
|
headers: {
|
||||||
|
'Range': 'messages=' + ((page - 1) * amountPerPage) + '-' + (((page - 1) * amountPerPage) + amountPerPage),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const [, rangeInfo] = data.headers['content-range'].split(' ');
|
||||||
|
const [, total] = rangeInfo.split('/');
|
||||||
|
|
||||||
|
setTotalMessages(parseInt(total));
|
||||||
|
setMessages(data.data);
|
||||||
|
} catch(err) {
|
||||||
|
setError(err);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
fetchMessages();
|
||||||
|
}, [page]);
|
||||||
|
|
||||||
|
const removeMessage = async (id) => {
|
||||||
|
try{
|
||||||
|
await del('/message', {id: id});
|
||||||
|
setMessages((prevlist) => prevlist.filter(message => message.id !== id));
|
||||||
|
setTotalMessages(totalMessages - 1)
|
||||||
|
}catch(err){
|
||||||
|
setError(err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const nextPage = () => {
|
||||||
|
if(page * amountPerPage < totalMessages)
|
||||||
|
setPage((page + 1));
|
||||||
|
}
|
||||||
|
const previousPage = () => {
|
||||||
|
if (page > 1)
|
||||||
|
setPage((page - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
messages,
|
||||||
|
totalMessages,
|
||||||
|
loading,
|
||||||
|
error,
|
||||||
|
removeMessage,
|
||||||
|
page,
|
||||||
|
nextPage,
|
||||||
|
previousPage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useMessages;
|
40
src/Guestbooky-admin/src/index.css
Normal file
40
src/Guestbooky-admin/src/index.css
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
@import url(https://fonts.bunny.net/css?family=carattere:400|solway:300,500);
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: reduce){
|
||||||
|
html:focus-within {
|
||||||
|
scroll-behavior: auto;
|
||||||
|
}
|
||||||
|
*, *::before, *::after {
|
||||||
|
animation-duration: 0.01ms !important;
|
||||||
|
animation-iteration-count: 1 !important;
|
||||||
|
transition: none;
|
||||||
|
transition-duration: 0.01ms !important;
|
||||||
|
scroll-behavior: auto !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*, *::before, *::after{
|
||||||
|
box-sizing: border-box;
|
||||||
|
transition: all 0.05s ease;
|
||||||
|
animation-duration: initial;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
html:focus-within{
|
||||||
|
scroll-behavior: smooth;
|
||||||
|
}
|
||||||
|
|
||||||
|
img, picture, svg, video, canvas{
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background: linear-gradient(90deg, hsl(15, 80%, 90%) 0%, hsl(15, 80%, 98%) 5%, hsl(15, 80%, 98%) 95%, hsl(15, 80%, 90%) 100%);
|
||||||
|
color: hsl(15, 70%, 5%);
|
||||||
|
}
|
7
src/Guestbooky-admin/src/main.jsx
Normal file
7
src/Guestbooky-admin/src/main.jsx
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import { createRoot } from 'react-dom/client'
|
||||||
|
import './index.css'
|
||||||
|
import App from './App.jsx'
|
||||||
|
|
||||||
|
createRoot(document.getElementById('root')).render(
|
||||||
|
<App />
|
||||||
|
)
|
21
src/Guestbooky-admin/src/services/httpService.js
Normal file
21
src/Guestbooky-admin/src/services/httpService.js
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import axios from 'axios';
|
||||||
|
import { API_URL } from '../environment/constants.js'
|
||||||
|
|
||||||
|
const httpClient = axios.create({
|
||||||
|
baseURL: API_URL,
|
||||||
|
withCredentials: true,
|
||||||
|
timeout: 5000,
|
||||||
|
headers: {'Content-Type': 'application/json',}
|
||||||
|
});
|
||||||
|
|
||||||
|
export const get = (endpoint, params) => {
|
||||||
|
return httpClient.get(endpoint, { headers: params.headers, data: params.data});
|
||||||
|
}
|
||||||
|
|
||||||
|
export const post = (endpoint, data) => {
|
||||||
|
return httpClient.post(endpoint, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const del = (endpoint, data) => {
|
||||||
|
return httpClient.delete(endpoint, {data: data });
|
||||||
|
}
|
7
src/Guestbooky-admin/vite.config.js
Normal file
7
src/Guestbooky-admin/vite.config.js
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import { defineConfig } from 'vite'
|
||||||
|
import react from '@vitejs/plugin-react'
|
||||||
|
|
||||||
|
// https://vite.dev/config/
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [react()],
|
||||||
|
})
|
Loading…
Reference in a new issue