This commit is contained in:
yanczi 2025-10-19 02:44:10 +02:00
parent 7d1af32688
commit f3adf19649
139 changed files with 25604 additions and 0 deletions

3
.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
client_2.1/node_modules
server_2.1/node_modules
server_2.1/.env

8
2.1.code-workspace Normal file
View file

@ -0,0 +1,8 @@
{
"folders": [
{
"path": "."
}
],
"settings": {}
}

23
client_2.1/.gitignore vendored Normal file
View file

@ -0,0 +1,23 @@
.DS_Store
node_modules
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

24
client_2.1/README.md Normal file
View file

@ -0,0 +1,24 @@
# animelista_website
## Project setup
```
npm install
```
### Compiles and hot-reloads for development
```
npm run serve
```
### Compiles and minifies for production
```
npm run build
```
### Lints and fixes files
```
npm run lint
```
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).

View file

@ -0,0 +1,8 @@
{
"folders": [
{
"path": ".."
}
],
"settings": {}
}

View file

@ -0,0 +1,5 @@
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
]
}

19
client_2.1/jsconfig.json Normal file
View file

@ -0,0 +1,19 @@
{
"compilerOptions": {
"target": "es5",
"module": "esnext",
"baseUrl": "./",
"moduleResolution": "node",
"paths": {
"@/*": [
"src/*"
]
},
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
]
}
}

13715
client_2.1/package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

59
client_2.1/package.json Normal file
View file

@ -0,0 +1,59 @@
{
"name": "animelista_website",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"@vue/compiler-sfc": "^3.5.18",
"ajv": "^8.17.1",
"altcha": "^1.5.1",
"anymatch": "^3.1.3",
"axios": "^1.7.9",
"bootstrap": "^5.3.3",
"chokidar": "^4.0.1",
"core-js": "^3.8.3",
"globby": "^14.0.2",
"micromatch": "^4.0.8",
"pinia": "^3.0.3",
"pinia-plugin-persistedstate": "^4.4.1",
"vue": "^3.5.18",
"vue-multiselect": "^3.1.0",
"vue-router": "^4.4.5",
"vue-virtual-scroller": "^2.0.0-beta.8",
"vue3-virtual-scroller": "^0.2.3"
},
"devDependencies": {
"@babel/core": "^7.26.0",
"@babel/eslint-parser": "^7.25.9",
"@vitejs/plugin-vue": "^6.0.1",
"@vue/cli-plugin-babel": "~5.0.8",
"@vue/cli-plugin-eslint": "~5.0.8",
"@vue/cli-service": "^5.0.8",
"eslint": "^7.32.0",
"eslint-plugin-vue": "^8.0.3"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/vue3-essential",
"eslint:recommended"
],
"parserOptions": {
"parser": "@babel/eslint-parser"
},
"rules": {}
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead",
"not ie 11"
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View file

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

23
client_2.1/src/App.vue Normal file
View file

@ -0,0 +1,23 @@
<template lang="html">
<!--<PlaygroundXD />-->
<router-view />
</template>
<script>
//import PlaygroundXD from './components/playground.vue'
export default {
name: 'App',
components: {
//PlaygroundXD
},
data(){
return {
serverResponse: 'Hi mom!',
}
}
};
</script>
<style lang="css">
</style>

View file

@ -0,0 +1,49 @@
:root {
--bt-normal-color: #20C997;
--bt-hover-color: #41D1A7;
--bt-click-color: #3CBC96;
--bt-disable-color: #96C7B9;
--bg-normal-color: #212529;
--bg-dark-color: #14171A;
--state-red-color: #DC3545;
--state-yellow-color: #FFC107;
--state-green-color: #198754;
--state-gray-color: #6C757D;
/*Kolory tekstów*/
--text-color: #fff;
--text-no-hover-color: #aaa;
--text-distable: #666;
/*Defaultowe kolory*/
--default-border-color: #006283;
--default-border-hover: #008CBA;
--default-border-distable: #4f6066;
--default-content-hover: #9fdff5;
--default-glow-color-in: rgba(0, 153, 255, 0.24);
--default-glow-color-out: rgba(0, 153, 255, 0.19);
/*Danger kolorki*/
--danger-border-color: #c90909;
--danger-border-hover: #ff3030;
--danger-border-distable: #6e4c4c;
--danger-content-hover: #ffbcbc;
--danger-glow-color-in:rgba(255, 0, 0, 0.24);
--danger-glow-color-out: rgba(255, 0, 0, 0.19);
/*Accept kolorki*/
--accept-border-color: #09c953;
--accept-border-hover: #47fd8d;
--accept-border-distable: #659678;
--accept-content-hover: #cafcdd;
--accept-glow-color-in: #00ff623d;
--accept-glow-color-out: #00ff6213;
}

View file

@ -0,0 +1,131 @@
.control-body {
margin-top: 128px;
width: 100%;
display: flex;
justify-content: center;
}
.user-panel {
display: flex;
flex-direction: column;
width: 800px;
height: 1000px;
max-width: 1440px;
}
/*Sekcja avatara*/
.user-avatar-section {
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: start;
width: 100%;
min-height: 224px;
padding: 20px;
}
.user-avatar-container {
width: 128px;
height: 128px;
border-radius: 100%;
overflow: hidden;
position: relative;
}
.user-avatar-img {
width: 100%;
height: 100%;
}
.user-avatar-bt-set {
display: flex;
flex-direction: column;
margin-left: 24px;
height: 128px;
justify-content: space-around;
}
.avatar-row {
display: flex;
flex-direction: row;
justify-content: start;
align-items: center;
}
.avatar-input-box {
display: flex;
flex-direction: row;
align-items: center;
}
.avatar-path-string {
font-style: italic;
color: var(--text-color);
cursor: default;
max-width: 400px;
white-space: nowrap;
overflow: hidden;
text-overflow: "...";
}
.avatar-bt-update {
display: flex;
flex-direction: row;
width: 344px;
justify-content: space-between;
}
/*Sekcja edycji danych*/
.user-data-section {
width: 100%;
padding: 20px;
display: flex;
flex-direction: column;
}
.user-password-section {
width: 100%;
display: flex;
flex-direction: column;
padding: 8px;
}
.user-new-password-section {
width: 100%;
display: flex;
flex-direction: row;
justify-content: space-between;
}
.passwod-box {
display: flex;
flex-direction: column;
width: 45%;
}
/*Regular*/
.pad32 {
padding: 8px;
margin-top: 8px;
margin-bottom: 8px;
}
.margtb8 {
margin-top: 16px;
margin-bottom: 8px;
}
.panel-hr-separator {
color: var(--bg-dark-color);
border: 1px solid var(--text-distable);
}
.smol-info-txt {
color: #fff;
font-size: 8pt;
}
.smol-info-txt-wrong {
color: #ff0000;
font-size: 8pt;
}

View file

@ -0,0 +1,29 @@
/*Liberation Sans*/
@font-face {
font-family: 'Liberation Sans';
src: url('../fonts/LiberationSans-Regular.ttf') format('truetype');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'Liberation Sans';
src: url('../fonts/LiberationSans-Bold.ttf') format('truetype');
font-weight: bold;
font-style: normal;
}
@font-face {
font-family: 'Liberation Sans';
src: url('../fonts/LiberationSans-Italic.ttf') format('truetype');
font-weight: normal;
font-style: italic;
}
@font-face {
font-family: 'Liberation Sans';
src: url('../fonts/LiberationSans-BoldItalic.ttf') format('truetype');
font-weight: bold;
font-style: italic;
}

View file

@ -0,0 +1,106 @@
footer {
width: 100%;
height: 256px;
display: flex;
flex-direction: column;
justify-content: center;
align-content: space-between;
margin-top: 64px;
background-color: #14171a;
}
.ft-copy-date {
text-align: center;
font-size: 8pt;
}
.ft-others-container {
display: flex;
flex-direction: row;
justify-content: space-evenly;
}
.powered-by-container {
width: 30%;
}
.license-container {
width: 30%;
display: flex;
flex-direction: row;
justify-content: center;
justify-items: center;
align-items: center;
text-align: center;
}
.txt-license-url {
text-align: center;
}
.license-img {
width: auto;
height: 32px;
}
.txt-license-url {
margin-top: 16px;
}
.credits-container {
width: 30%;
}
.vue-logo-img {
width: auto;
height: 24px;
}
.vue-logo-set {
all: unset;
display: flex;
flex-direction: row;
align-content: center;
color: #fff;
font-style: normal;
}
.vue-txt-container {
font-size: 12pt;
margin-left: 8px;
}
.vue-containe {
display: flex;
flex-direction: column;
}
.vue-descryption {
margin-top: 8px;
display: flex;
flex-direction: column;
}
.vue-desc-txt {
font-size: 10pt;
}
.bootstrap-logo-set {
all: unset;
display: flex;
flex-direction: row;
align-content: center;
color: #fff;
font-style: normal;
margin-top: 32px;
}
.ft-author-txt {
text-align: end;
}
.ft-credits {
display: flex;
flex-direction: column;
justify-content: end;
}

View file

@ -0,0 +1,217 @@
.sign-in-body {
width: 100vw;
height: 100vh;
background-image: url("@/assets/img/bg.jpg");
background-position: center;
background-size: cover;
background-repeat: no-repeat;
}
.sign-in-container {
width: 840px;
height: 620px;
border: 2px solid var(--default-border-color);
border-radius: 32px;
overflow: hidden;
display: flex;
flex-direction: row;
background-color: var(--bg-normal-color);
border-radius: 8px 8px 8px 8px;
-webkit-box-shadow:0px 0px 45px 23px rgba(0,0,0,0.82);
-moz-box-shadow: 0px 0px 45px 23px rgba(0,0,0,0.82);
box-shadow: 0px 0px 45px 23px rgba(0,0,0,0.82);
}
.sign-in-segment {
width: 50%;
height: 100%;
background-color: var(--bg-normal-color);
}
.sin-bg-window {
background-image: url('@/assets/img/signin_window.jpg');
background-position: center;
background-size:cover;
display: flex;
justify-content: center;
align-items: start;
padding: 32px
}
.img-site-box {
width: 100%;
height: 72px;
display: flex;
justify-content: start;
align-items: center;
}
.img-logo {
width: 300px;
height: autopx;
}
.bt-sin {
width: 100%;
}
.in-email {
height: 64px;
}
.sin-mess {
width: 100%;
height: 48px;
border-radius: 8px 8px 8px 8px;
background-color: brown;
}
.sin-mess-visible {
visibility: visible;
}
.sin-mess-hidden {
visibility: hidden;
}
.lgn-input-box {
display: flex;
flex-direction: column;
}
.recovery-pass-hurl {
margin-bottom: 16px;
padding: 8px;
height: 96px;
}
.recovery-url {
font-size: 10pt;
cursor: pointer;
color: var(--text-no-hover-color);
}
.recovery-url:hover {
color: var(--text-color);
}
.capcha-container {
padding: 8px;
width: 100%;
display: flex;
justify-content: center;
}
.shake-horizontal {
-webkit-animation: shake-horizontal 0.8s cubic-bezier(0.455, 0.030, 0.515, 0.955) both;
animation: shake-horizontal 0.8s cubic-bezier(0.455, 0.030, 0.515, 0.955) both;
}
/* ----------------------------------------------
* Generated by Animista on 2025-2-15 20:8:9
* Licensed under FreeBSD License.
* See http://animista.net/license for more info.
* w: http://animista.net, t: @cssanimista
* ---------------------------------------------- */
/**
* ----------------------------------------
* animation scale-up-center
* ----------------------------------------
*/
@-webkit-keyframes shake-horizontal {
0%,
100% {
-webkit-transform: translateX(0);
transform: translateX(0);
}
10%,
30%,
50%,
70% {
-webkit-transform: translateX(-10px);
transform: translateX(-10px);
}
20%,
40%,
60% {
-webkit-transform: translateX(10px);
transform: translateX(10px);
}
80% {
-webkit-transform: translateX(8px);
transform: translateX(8px);
}
90% {
-webkit-transform: translateX(-8px);
transform: translateX(-8px);
}
}
@keyframes shake-horizontal {
0%,
100% {
-webkit-transform: translateX(0);
transform: translateX(0);
}
10%,
30%,
50%,
70% {
-webkit-transform: translateX(-10px);
transform: translateX(-10px);
}
20%,
40%,
60% {
-webkit-transform: translateX(10px);
transform: translateX(10px);
}
80% {
-webkit-transform: translateX(8px);
transform: translateX(8px);
}
90% {
-webkit-transform: translateX(-8px);
transform: translateX(-8px);
}
}
.scale-up-center {
-webkit-animation: scale-up-center 0.2s cubic-bezier(0.390, 0.575, 0.565, 1.000) both;
animation: scale-up-center 0.2s cubic-bezier(0.390, 0.575, 0.565, 1.000) both;
}
/* ----------------------------------------------
* Generated by Animista on 2025-2-15 20:8:9
* Licensed under FreeBSD License.
* See http://animista.net/license for more info.
* w: http://animista.net, t: @cssanimista
* ---------------------------------------------- */
/**
* ----------------------------------------
* animation scale-up-center
* ----------------------------------------
*/
@-webkit-keyframes scale-up-center {
0% {
-webkit-transform: scale(0.5);
transform: scale(0.5);
}
100% {
-webkit-transform: scale(1);
transform: scale(1);
}
}
@keyframes scale-up-center {
0% {
-webkit-transform: scale(0.5);
transform: scale(0.5);
}
100% {
-webkit-transform: scale(1);
transform: scale(1);
}
}

View file

@ -0,0 +1,282 @@
.md-body {
display: none; /* Hidden by default */
position: fixed; /* Stay in place */
z-index: 1000; /* Sit on top */
left: 0;
top: 0;
width: 100%; /* Full width */
height: 100%; /* Full height */
overflow: auto; /* Enable scroll if needed */
background-color: rgb(0,0,0); /* Fallback color */
background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
}
.md-content {
color: #fff;
background-color: var(--bg-normal-color);
position: relative;
width: 800px;
height: auto;
border-radius: 8px 8px 8px 8px;
-webkit-box-shadow:0px 0px 45px 23px rgba(0,0,0,0.82);
-moz-box-shadow: 0px 0px 45px 23px rgba(0,0,0,0.82);
box-shadow: 0px 0px 45px 23px rgba(0,0,0,0.82);
}
.md-header {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
align-content: center;
width: 100%;
height: 63px;
padding: 1rem;
border-bottom: 1px solid #0e0e0e;
}
.bt-md-exit {
color: #777;
display: flex;
justify-content: center;
align-items: center;
height: 32px;
width: 32px;
cursor: pointer;
}
.bt-md-exit:hover {
color: #fff;
}
.md-title {
font-size: 18pt;
}
.md-body-content {
display: flex;
flex-direction: row;
align-items: center;
min-height: 200px;
height: 600px;
width: 100%;
}
/*Group Body*/
.md-body-content-gr {
display: flex;
flex-direction: column;
align-items: center;
min-height: 200px;
height: 600px;
width: 100%;
}
.smol-text-lenght-glyph {
color: #fff;
font-size: 8pt;
}
.smol-text-content {
width: 100%;
display: flex;
justify-content: end;
}
.md-inp-box-content-gr {
display: flex;
flex-direction: column;
align-items: stretch;
width: 90%;
height: 112px;
}
.md-group-list-box {
width: 90%;
height: 320px;
max-width: 90%;
max-height: 320px;
border-radius: 8px;
padding: 24px;
overflow: scroll;
color: #fff;
background-color: var(--bg-dark-color);
}
.md-inp-box-input-content-gr {
display: flex;
width: 100%;
flex-direction: row;
}
/*Goup Row*/
.md-gr-row {
display: flex;
flex-direction: row;
padding: 12px;
}
.md-gr-cell-ch {
width: 32px;
}
.md-gr-cell-text {
max-width: 80%;
text-wrap: wrap;
text-align: left;
}
/*Delete*/
.md-body-content-del {
display: flex;
flex-direction: column;
align-items: center;
min-height: 200px;
height: 600px;
width: 100%;
}
.md-delete-list-box {
width: 90%;
height: 400px;
max-width: 90%;
max-height: 400px;
border-radius: 8px;
padding: 24px;
overflow: scroll;
color: #fff;
background-color: var(--bg-dark-color);
}
.md-delete-info-span {
display: flex;
flex-direction: row;
justify-content: start;
width: 90%;
}
.md-span-item {
margin-right: 64px;
font-size: 12pt;
color: #fff;
}
/*Footer*/
.md-footer {
width: 100%;
position: absolute;
bottom: 0;
border-top: 1px solid #0e0e0e;
}
.bt-clr {
color: #fff;
}
.bt-space {
margin-left: 16px;
}
.img-md-box {
width: 30%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 480px;
}
.content-md-box {
width: 70%;
display: flex;
justify-content: center;
}
.md-data-box {
width: 85%;
}
input:focus {
outline: none;
}
.lb-title {
width: 100%;
}
.lb-episodes {
width: 96px;
}
.md-select-group-menu {
width: 320px;
max-width: 320px;
}
.md-ep-x-gr-box {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
width: 100%;
}
.md-description-textbox {
width: 100%;
height: 200px;
resize: none;
}
.md-form-box {
display: flex;
flex-direction: column;
margin-bottom: 8px;
}
.md-img-url {
margin-top: 16px;
}
.img-file-upload-box {
margin-bottom: 16px;
border: 2px solid var(--default-border-color);
border-radius: 8px;
overflow: hidden;
transition: all .1s linear;
}
.img-file-upload-box:hover {
border: 2px solid var(--default-border-hover);
box-shadow: 0 0px 16px 0 var(--default-glow-color-in), 0 0px 50px 0 var(--default-glow-color-out);
}
.img-placeholder {
background-color: var(--bg-dark-color);
color: #fff;
width: 200px;
height: 285px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-weight: bold;
border-radius: 8px 8px 8px 8px;
}
.waiting-on-click-to-upload-image {
display: flex;
flex-direction: column;
align-items: center;
}
/* Ukrywanie strzałek w Chrome, Edge i Safari */
input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
-webkit-appearance: none;
margin: 0;
}
/* Ukrywanie strzałek w Firefox */
input[type="number"] {
-moz-appearance: textfield;
}

View file

@ -0,0 +1,73 @@
.nav-menu {
position: fixed;
top: 0;
left: 0;
height: 64px;
min-height: 64px;
width: 100%;
-webkit-box-shadow:0px 0px 45px 23px rgba(0,0,0,0.82);
-moz-box-shadow: 0px 0px 45px 23px rgba(0,0,0,0.82);
box-shadow: 0px 0px 45px 23px rgba(0,0,0,0.82);
z-index: 1000;
}
.bt-set-container {
height: 100%;
min-width: 560px;
}
.logo-container {
height: 64px;
width: 300px;
}
.logo-img {
width: auto;
height: 100%;
}
.user-container {
width: 320px;
max-width: 320px;
}
.user-box {
display: flex;
flex-direction: row;
width: 256px;
min-width: 256px;
height: 48px;
align-items: center;
cursor: pointer;
}
.user-img {
width: 48px;
height: 48px;
border-radius: 100%;
margin: 0px 8px 0px 8px;
}
.user-name-container {
color: #fff;
font-size: 16pt;
margin-right: 32px;
max-width: 160px;
width: 160px;
overflow: hidden;
text-overflow: "...";
}
.user-bt-logout {
margin-right: 32pxs;
height: 38px;
}
.user-menu-button {
width: 24px;
height: 24px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
}

View file

@ -0,0 +1,249 @@
body {
font-family: 'Liberation Sans';
font-size: 12pt;
color:#fff;
background-color: #212529;
}
.site-body-content {
display: flex;
flex-direction: column;
min-height: 100vh;
}
.content {
margin-top: 128px;
width: 100%;
display: flex;
justify-content: center;
flex: 1;
}
.dbt {
color: #fff;
}
.dbt:hover {
color: #bbbbbb;
}
button.bt {
border-radius: 24px;
padding: 6px 16px;
margin: 4px;
background-color: var(--bg-dark-color);
}
button.bt-circle {
width: 36px;
height: 36px;
border-radius: 24px;
background-color: var(--bg-dark-color);
}
/*Normalny guzik*/
button.bt-normal {
color: var(--text-color);
border: 2px solid var(--default-border-color);
transition: all .1s linear;
}
button.bt-normal:hover:enabled {
color: var(--default-content-hover);
border: 2px solid var(--default-border-hover);
box-shadow: 0 0px 16px 0 var(--default-glow-color-in), 0 0px 50px 0 var(--default-glow-color-out);
}
button.bt-normal:disabled {
border: 2px solid var(--default-border-distable);
color: var(--text-distable);
}
/*Dangerous guzik*/
button.bt-danger {
color: var(--text-color);
border: 2px solid var(--danger-border-color);
transition: all .1s linear;
}
button.bt-danger:hover:enabled {
color: var(--danger-content-hover);
border: 2px solid var(--danger-border-hover);
box-shadow: 0 0px 16px 0 var(--danger-glow-color-in), 0 0px 50px 0 var(--danger-glow-color-out);
}
button.bt-danger:disabled {
border: 2px solid var(--danger-border-distable);
color: var(--text-distable);
}
/*Akceptujący guzik*/
button.bt-accept {
color: var(--text-color);
border: 2px solid var(--accept-border-color);
transition: all .1s linear;
}
button.bt-accept:hover:enabled {
color: var(--accept-content-hover);
border: 2px solid var(--accept-border-hover);
box-shadow: 0 0px 16px 0 var(--accept-glow-color-in), 0 0px 50px 0 var(--accept-glow-color-out);
}
button.bt-accept:disabled {
border: 2px solid var(--accept-border-distable);
color: var(--text-distable);
}
/*Sellektory*/
select.sl-default {
color: var(--text-color);
background-color: var(--bg-dark-color);
border: 2px solid var(--default-border-color);
border-radius: 32px;
padding: 8px 16px;
}
/*Inputy Textowe*/
input.in {
background-color: var(--bg-dark-color);
border-radius: 32px;
color: var(--text-color);
padding: 6px 16px;
}
input.in-default {
border: 2px solid var(--default-border-color);
}
/*Text box*/
textarea.tx {
background-color: var(--bg-dark-color);
color: var(--text-color);
border-radius: 8px 8px 8px 8px;
padding: 8px;
}
textarea.tx:focus {
outline: none;
}
textarea.tx-default {
border: 2px solid var(--default-border-color);
}
/*Ogólne zastosowanie*/
.default-border {
border: 2px solid var(--default-border-color);
}
/*************************************/
/* */
/* MESSAGE BOX */
/* */
/*************************************/
.mess-window {
/*background: #2EC27E;*/
border-radius: 8px;
width: 100%;
min-height: 64px;
padding: 4px;
position: relative;
display: flex;
align-items: center;
color: #fff;
}
.mess-good {
border: 2px solid #8FF0A4;
box-shadow: 0px 0px 24px 8px #33D17A4E inset;
-webkit-box-shadow: 0px 0px 24px 8px #33D17A4E inset;
-moz-box-shadow: 0px 0px 24px 8px #33D17A4E inset;
}
.mess-bad {
border: 2px solid #f08f8f;
box-shadow: 0px 0px 24px 8px #E01B244E inset;
-webkit-box-shadow: 0px 0px 24px 8px #E01B244E inset;
-moz-box-shadow: 0px 0px 24px 8px #E01B244E inset;
}
.mess-warn {
border: 2px solid #f0e38f;
box-shadow: 0px 0px 24px 8px #F6D32D4E inset;
-webkit-box-shadow: 0px 0px 24px 8px #F6D32D4E inset;
-moz-box-shadow: 0px 0px 24px 8px #F6D32D4E inset;
}
.mess-info {
border: 2px solid #8fc1f0;
box-shadow: 0px 0px 24px 8px #3584E44E inset;
-webkit-box-shadow: 0px 0px 24px 8px #3584E44E inset;
-moz-box-shadow: 0px 0px 24px 8px #3584E44E inset;
}
.mess-txt {
width: 80%;
min-width: 40%;
}
.bt-mess-exit {
color: #777;
display: flex;
justify-content: center;
align-items: center;
height: 16px;
width: 16px;
cursor: pointer;
position: absolute;
top: 5px;
right: 5px
}
.bt-mess-exit:hover {
color: #fff;
}
.mess-body {
display: flex;
flex-direction: row;
width: 100%;
align-items: center;
padding-left: 16px;
padding-right: 16px;
}
/* <<<TOOL TIP>>> */
.tooltip {
position: relative;
display: inline-block;
border-bottom: 1px dotted black;
}
.tooltip .tooltiptext {
visibility: hidden;
width: 120px;
background-color: black;
color: #fff;
text-align: center;
border-radius: 6px;
padding: 5px 0;
/* Position the tooltip */
position: absolute;
z-index: 1;
top: 100%;
left: 50%;
margin-left: -60px;
}
.tooltip:hover .tooltiptext {
visibility: visible;
}

View file

@ -0,0 +1,307 @@
.anime-table {
width: 1440px;
min-width: 1024px;
}
.table-header-menu {
height: 64px;
display: flex;
justify-content: space-between;
align-items: center;
}
.filtr-state-selector {
width: 600px;
display: flex;
flex-direction: row;
align-items: center;
}
.search-input {
width: 320px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
}
.anime-row-container {
height: 70px;
}
.ch-column {
width: 16px;
}
.img-column {
width: 50px;
}
.title-column {
width: 70%;
}
.ch-container {
height: 70px;
display: flex;
align-items: center;
}
.title-container {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
height: 70px;
}
.title-box {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-content: space-between;
}
.anime-title-box {
max-width: 800px; /* Maksymalna szerokość */
overflow: hidden; /* Ukrywa nadmiarowy tekst */
white-space: nowrap; /* Zapobiega zawijaniu tekstu */
text-overflow: ellipsis; /* Dodaje "..." na końcu */
display: inline-block;
position: relative;
}
.anime-title {
font-size: 16pt;
font-weight: bold;
cursor: pointer;
}
.anime-title-scrolling {
display: inline-block;
max-width: 100%;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.anime-title-box:hover .anime-title {
animation: scrollText 5s linear infinite alternate;
text-overflow: clip; /* Usuwa "..." podczas przewijania */
}
@keyframes scrollText {
0% {
transform: translateX(0);
}
100% {
transform: translateX(-10%);
}
}
.anime-add-record-date {
font-size: 8pt;
}
.anm-epst-container {
display: flex;
flex-direction: row;
justify-content: space-evenly;
align-items: center;
height: 70px;
}
.anm-epbt-container {
display: flex;
flex-direction: row;
margin: 0px 32px 0px 16px;
}
.txt-ep-nuber-view {
width: 32px;
text-align: center;
}
.tb-filter-sellect {
width: 260px;
max-width: 260px;
padding: 12px;
background-color: var(--bg-dark-color);
border: 0;
border-radius: 8px 8px 8px 8px;
color: #fff;
}
/*Kolory ikon*/
.svg-watched {
color: var(--state-green-color);
}
.svg-watch {
color: var(--state-yellow-color);
}
.svg-nowatched {
color: var(--state-red-color);
}
.svg-canceled {
color: var(--state-gray-color);
}
.state-select-txt {
margin-left: 8px;
}
/*Selektor oglądania*/
.watch-state-selector {
width: 180px;
height: 38px;
background-color: var(--bg-dark-color);
display: flex;
flex-direction: row;
justify-content: space-around;
align-items: center;
cursor: pointer;
}
.watch-txt-content {
width: 120px;
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
}
.watch-state-hover-menu {
position: absolute;
display: flex;
flex-direction: column;
width: 180px;
background-color: var(--bg-dark-color);
border-radius: 0px 0px 6px 6px;
}
.watch-state-hidden-menu {
visibility: hidden;
}
.watch-state-visible-menu {
visibility: visible;
}
.watch-state-selector-radius-hidden-menu {
border-radius: 6px 6px 6px 6px;
}
.watch-state-selector-radius-visible-menu {
border-radius: 6px 6px 0px 0px;
}
.state-select {
height: 38px;
width: 100%;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
cursor: pointer;
}
.state-select-content {
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
width: 156px;
}
.state-select:hover {
background-color: var(--bg-normal-color);
}
/*Opis anime*/
.anime-descryption-content {
width: 100%;
min-height: 320px;
display: flex;
flex-direction: row;
justify-content: space-evenly;
}
.img-containcer {
width: 232px;
height: 100%;
display: flex;
justify-content: center;
align-content: center;
}
.descryption-containcer {
max-width: 1024px;
min-width: 1024px;
width: 1024px;
}
.title-box {
margin: 8px;
height: 64px;
display: flex;
justify-content: flex-start;
align-content: center;
}
.title-string {
font-size: 20pt;
font-weight: bold;
}
.descryption-string {
margin: 8px;
max-height: 284px;
width: 100%;
overflow-y: auto;
overflow-wrap: break-word;
text-align: justify;
}
.anime-descryption-header {
height: 38px;
max-height: 38px;
min-height: 38px;
width: 100%;
display: flex;
justify-content: flex-end;
}
.hide-descryption {
width: 38px;
height: 38px;
display: flex;
justify-content: center;
align-items: center;
margin-right: 16px;
cursor: pointer;
}
/* ROW SEPARATOR */
.row-separator {
height: 45px;
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
}
.separate-string {
font-size: 20pt;
font-weight: bold;
}
.ch-separator {
height: 45px;
display: flex;
align-items: center;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 B

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 390 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="408"><defs><linearGradient id="bs-logo-a" x1="76.079" x2="523.48" y1="10.798" y2="365.945" gradientUnits="userSpaceOnUse"><stop stop-color="#9013fe"/><stop offset="1" stop-color="#6610f2"/></linearGradient><linearGradient id="bs-logo-b" x1="193.508" x2="293.514" y1="109.74" y2="278.872" gradientUnits="userSpaceOnUse"><stop stop-color="#fff"/><stop offset="1" stop-color="#f1e5fc"/></linearGradient><filter xmlns="http://www.w3.org/2000/svg" id="bs-logo-c" width="197" height="249" x="161.901" y="83.457" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset dy="4"/><feGaussianBlur stdDeviation="8"/><feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow"/><feBlend in="SourceGraphic" in2="effect1_dropShadow" result="shape"/></filter></defs><path fill="url(#bs-logo-a)" d="M56.481 53.32C55.515 25.58 77.128 0 106.342 0h299.353c29.214 0 50.827 25.58 49.861 53.32-.928 26.647.277 61.165 8.964 89.31 8.715 28.232 23.411 46.077 47.48 48.37v26c-24.069 2.293-38.765 20.138-47.48 48.37-8.687 28.145-9.892 62.663-8.964 89.311.966 27.739-20.647 53.319-49.861 53.319H106.342c-29.214 0-50.827-25.58-49.86-53.319.927-26.648-.278-61.166-8.966-89.311C38.802 237.138 24.07 219.293 0 217v-26c24.069-2.293 38.802-20.138 47.516-48.37 8.688-28.145 9.893-62.663 8.965-89.31z"/><path fill="url(#bs-logo-b)" filter="url(#bs-logo-c)" stroke="#fff" d="M267.103 312.457c47.297 0 75.798-23.158 75.798-61.355 0-28.873-20.336-49.776-50.532-53.085v-1.203c22.185-3.609 39.594-24.211 39.594-47.219 0-32.783-25.882-54.138-65.322-54.138h-88.74v217h89.202zm-54.692-189.48h45.911c24.958 0 39.131 11.128 39.131 31.279 0 21.505-16.484 33.535-46.372 33.535h-38.67v-64.814zm0 161.961v-71.431h45.602c32.661 0 49.608 12.03 49.608 35.49 0 23.459-16.484 35.941-47.605 35.941h-47.605z"/></svg>

After

Width:  |  Height:  |  Size: 2 KiB

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 512 512" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<path d="M432,18L80,18C35.887,18 0,53.887 0,98L0,378C0,422.113 35.887,458 80,458L292,458C303.047,458 312,449.047 312,438C312,426.953 303.047,418 292,418L279.336,418L233.352,358.344L378.598,173.602L469.754,280.945C473.555,285.422 479.129,288 485,288L492,288C503.047,288 512,279.047 512,268L512,98C512,53.887 476.113,18 432,18ZM472,221.793L393.246,129.055C389.359,124.477 383.637,121.871 377.621,122.004C371.621,122.117 365.988,124.922 362.277,129.641L208.16,325.664L155.84,257.789C152.055,252.879 146.203,250 140,250L139.988,250C133.785,250.004 127.93,252.887 124.148,257.809L80.133,315.027C73.398,323.781 75.035,336.34 83.789,343.074C92.547,349.809 105.102,348.172 111.836,339.414L140.016,302.781L228.832,418L80,418C57.945,418 40,400.055 40,378L40,98C40,75.945 57.945,58 80,58L432,58C454.055,58 472,75.945 472,98L472,221.793ZM140,90C106.914,90 80,116.914 80,150C80,183.086 106.914,210 140,210C173.086,210 200,183.086 200,150C200,116.914 173.086,90 140,90ZM140,170C128.973,170 120,161.027 120,150C120,138.973 128.973,130 140,130C151.027,130 160,138.973 160,150C160,161.027 151.027,170 140,170ZM506.145,358.145L455.285,409L506.141,459.855C513.953,467.668 513.953,480.332 506.141,488.141C502.238,492.047 497.117,494 492,494C486.883,494 481.762,492.047 477.855,488.145L427,437.285L376.145,488.141C372.238,492.047 367.117,494 362,494C356.883,494 351.762,492.047 347.855,488.145C340.047,480.332 340.047,467.668 347.855,459.859L398.715,409L347.859,358.145C340.047,350.332 340.047,337.668 347.859,329.859C355.668,322.047 368.332,322.047 376.145,329.859L427,380.715L477.855,329.859C485.668,322.047 498.332,322.047 506.141,329.859C513.953,337.668 513.953,350.332 506.145,358.145Z" style="fill:white;fill-rule:nonzero;"/>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

File diff suppressed because it is too large Load diff

After

Width:  |  Height:  |  Size: 55 KiB

View file

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<g transform="matrix(0.0914286,0,0,0.0914286,-75.4286,-6.25171)">
<path d="M1000,166.243C985.787,166.243 974.265,154.721 974.265,140.508L974.265,94.114C974.265,79.9 985.787,68.378 1000,68.378C1014.21,68.378 1025.74,79.9 1025.74,94.114L1025.74,140.508C1025.74,154.721 1014.21,166.243 1000,166.243Z" style="fill:white;fill-rule:nonzero;"/>
</g>
<g transform="matrix(0.0914286,0,0,0.0914286,-75.4286,-6.25171)">
<path d="M1072.79,196.322C1066.2,196.322 1059.62,193.809 1054.59,188.785C1044.54,178.734 1044.54,162.44 1054.59,152.39L1087.35,119.634C1092.37,114.609 1098.96,112.097 1105.55,112.097C1112.13,112.097 1118.72,114.609 1123.74,119.634C1133.79,129.684 1133.79,145.979 1123.74,156.029L1090.99,188.785C1085.96,193.809 1079.38,196.322 1072.79,196.322Z" style="fill:white;fill-opacity:0.16;fill-rule:nonzero;"/>
</g>
<g transform="matrix(0.0914286,0,0,0.0914286,-75.4286,-6.25171)">
<path d="M1149.26,269.113L1102.94,269.113C1088.73,269.113 1077.21,257.591 1077.21,243.378C1077.21,229.164 1088.73,217.643 1102.94,217.643L1149.26,217.643C1163.48,217.643 1175,229.164 1175,243.378C1175,257.591 1163.48,269.113 1149.26,269.113Z" style="fill:white;fill-opacity:0.28;fill-rule:nonzero;"/>
</g>
<g transform="matrix(0.0914286,0,0,0.0914286,-75.4286,-6.25171)">
<path d="M1105.55,374.659C1098.96,374.659 1092.37,372.146 1087.35,367.121L1054.59,334.365C1044.54,324.315 1044.54,308.02 1054.59,297.97C1059.62,292.945 1066.2,290.433 1072.79,290.433C1079.38,290.433 1085.96,292.945 1090.99,297.97L1123.74,330.726C1133.79,340.776 1133.79,357.071 1123.74,367.121C1118.72,372.146 1112.13,374.659 1105.55,374.659Z" style="fill:white;fill-opacity:0.4;fill-rule:nonzero;"/>
</g>
<g transform="matrix(0.0914286,0,0,0.0914286,-75.4286,-6.25171)">
<path d="M1000,418.378C985.786,418.378 974.264,406.855 974.264,392.643L974.264,346.319C974.264,332.106 985.786,320.583 1000,320.583C1014.21,320.583 1025.74,332.106 1025.74,346.319L1025.74,392.643C1025.74,406.855 1014.21,418.378 1000,418.378Z" style="fill:white;fill-opacity:0.52;fill-rule:nonzero;"/>
</g>
<g transform="matrix(0.0914286,0,0,0.0914286,-75.4286,-6.25171)">
<path d="M894.454,374.659C887.868,374.659 881.282,372.146 876.256,367.121C866.206,357.071 866.206,340.776 876.256,330.726L909.063,297.92C914.088,292.895 920.674,290.382 927.26,290.382C933.846,290.382 940.432,292.895 945.458,297.92C955.509,307.97 955.509,324.265 945.458,334.315L912.652,367.121C907.627,372.146 901.04,374.659 894.454,374.659Z" style="fill:white;fill-opacity:0.64;fill-rule:nonzero;"/>
</g>
<g transform="matrix(0.0914286,0,0,0.0914286,-75.4286,-6.25171)">
<path d="M897.059,269.113L850.735,269.113C836.522,269.113 825,257.591 825,243.378C825,229.164 836.522,217.643 850.735,217.643L897.059,217.643C911.272,217.643 922.794,229.164 922.794,243.378C922.794,257.591 911.272,269.113 897.059,269.113Z" style="fill:white;fill-opacity:0.76;fill-rule:nonzero;"/>
</g>
<g transform="matrix(0.0914286,0,0,0.0914286,-75.4286,-6.25171)">
<path d="M927.21,196.322C920.624,196.322 914.038,193.809 909.012,188.785L876.256,156.029C866.206,145.979 866.206,129.684 876.256,119.634C881.282,114.609 887.868,112.097 894.454,112.097C901.04,112.097 907.627,114.609 912.652,119.634L945.407,152.39C955.458,162.44 955.458,178.734 945.407,188.785C940.382,193.809 933.796,196.322 927.21,196.322Z" style="fill:white;fill-opacity:0.88;fill-rule:nonzero;"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

View file

@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="640"
height="360"
viewBox="0 0 169.33333 95.249998"
version="1.1"
id="svg1"
sodipodi:docname="logo.svg"
inkscape:version="1.4 (1:1.4+202410161351+e7c3feb100)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="1.73125"
inkscape:cx="344.54874"
inkscape:cy="149.31408"
inkscape:window-width="1854"
inkscape:window-height="1011"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg1" />
<defs
id="defs1" />
<path
style="font-size:32.8523px;font-family:DREGS;-inkscape-font-specification:DREGS;fill:#ffffff;stroke-width:0.172396"
d="m 98.396826,50.318747 h 0.517179 q 1.551565,0 1.551565,8.242668 v 16.000479 h 8.24268 v 1.018209 q -9.794245,0.51719 -9.794245,1.03438 -0.517179,-12.137744 -0.517179,-18.053068 z m 13.058974,4.63852 h 1.03437 v 2.068749 l -0.51719,2.56977 h -1.03436 l -0.51719,-2.56977 v -1.03438 q 0.16162,-1.034369 1.03437,-1.034369 z m -0.51718,7.224459 h 1.55155 l 0.51719,5.155709 v 8.759858 h -1.55156 l -1.03437,-12.380168 q 0.51719,-0.985887 0.51719,-1.535399 z m 14.07718,-5.672899 q 0,0.775785 -6.19009,7.208298 6.70728,6.125435 6.70728,6.707269 l 1.03437,-0.51719 h 2.06875 l -1.03438,1.55157 1.03438,1.55156 -2.58594,0.51718 q 0,-0.51718 -0.51718,-0.51718 -4.63853,5.155709 -7.22446,5.155709 0,-0.711131 5.68906,-6.190089 l -6.70728,-6.190079 h -1.03437 l -1.03437,0.51719 v -1.03438 q 6.75575,-9.277049 9.79423,-9.277049 z m 8.79218,-9.81039 0.51719,1.03437 v 4.638519 l 0.51719,3.62031 h 2.58593 l 2.56978,0.517191 v 0.517189 q -3.2001,1.01821 -5.15571,1.01821 v 19.087437 q 0,1.03438 -1.03438,1.03438 h -0.51719 l -1.55156,-19.604628 -3.08696,-0.517189 q 0,-1.535399 3.60415,-1.535399 v -9.293209 q 0.54951,0 1.55156,-0.517181 z m 19.28138,8.25883 0.50103,8.258828 2.58593,-1.55156 v 0.517191 q -1.06669,2.052589 -2.58593,2.052589 v 11.345788 q 0,1.03438 -1.01821,1.03438 0,-1.03438 -5.67291,-1.535399 l -3.10311,0.501019 h -5.15572 v -0.501019 l 12.89735,-10.327589 v -1.03437 q 0,-2.58594 -0.51717,-7.725489 -1.69702,1.03438 -3.10313,1.03438 h -5.15571 v -1.03438 z m -10.32758,18.053067 v 0.51718 h 9.29321 l -0.5172,-1.03437 v -5.672899 q -7.98407,6.190089 -8.77601,6.190089 z"
id="text2"
aria-label="Lista" />
<g
id="g2"
transform="matrix(1.0075392,0,0,1.0075392,-16.416683,-94.277658)"
style="fill:#ffffff">
<path
style="fill:#ffffff;stroke-width:0.264583"
d="m 29.404109,147.76027 24.965754,-29.40411 8.506849,27.18493 -18.863013,-4.99315 11.465753,-1.84931 -3.328768,-8.50685 z"
id="path1" />
<path
style="fill:#ffffff;stroke-width:0.264583"
d="m 85.964916,127.3725 -2.484559,20.00725 5.099886,-2.74609 -0.261533,-15.9535 z"
id="path3" />
<path
style="fill:#ffffff;stroke-width:0.264583"
d="m 93.681336,120.28459 -0.523057,27.01245 3.897809,-5.57959 1.438421,-12.22727 4.994271,7.14753 10.50232,-5.00048 20.40824,15.76428 -19.23016,-21.15733 -11.68035,2.15524 -4.535794,-6.98064 z"
id="path4"
sodipodi:nodetypes="ccccccccccc" />
<path
id="path6"
style="fill:#ffffff;stroke-width:0.264583"
d="m 96.140067,111.32683 -7.157492,11.45215 1.595562,-3.81455 c 0,0 -3.053077,3.56345 -3.666089,4.48961 -0.336128,0.45432 -0.526495,0.92333 -0.572206,1.48812 3.3e-5,0.90271 0.731817,1.63449 1.634526,1.63452 0.517396,-0.002 0.89356,-0.23493 1.310514,-0.66456 0.416954,-0.42963 0.60197,-1.28077 0.902956,-1.92116 1.984074,-4.22138 5.952229,-12.66413 5.952229,-12.66413 z"
sodipodi:nodetypes="ccczcczsc" />
<path
style="fill:#ffffff;stroke-width:0.264583"
d="m 137.68041,136.52066 12.48287,-13.40753 -31.80821,3.14383 23.2089,20.71234 -12.6678,-16.82877 10.91095,-0.5548 z"
id="path8"
sodipodi:nodetypes="ccccccc" />
<path
style="fill:#ffffff;stroke-width:0.264583"
d="m 65.280822,143.78424 -6.010275,-20.80479 16.366437,15.44178 4.253426,-23.30137 4.80822,-2.58904 -6.287672,34.67466 -12.760275,-12.11302 z"
id="path9" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

View file

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="197.97437mm"
height="36.771091mm"
viewBox="0 0 197.97437 36.771091"
version="1.1"
id="svg1"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1" />
<g
id="layer1"
transform="translate(-5.2967662,-111.55891)">
<path
style="font-size:32.8523px;font-family:DREGS;-inkscape-font-specification:DREGS;fill:#ffffff;stroke-width:0.199077"
d="m 136.54938,116.1729 h 0.59722 q 1.7917,0 1.7917,9.51834 v 18.47681 h 9.51835 v 1.17578 q -11.31005,0.59724 -11.31005,1.19447 -0.59722,-14.01624 -0.59722,-20.84706 z m 15.08004,5.3564 h 1.19447 v 2.38892 l -0.59724,2.96748 h -1.19445 l -0.59723,-2.96748 v -1.19447 q 0.18663,-1.19445 1.19445,-1.19445 z m -0.59722,8.34256 h 1.79169 l 0.59723,5.95363 v 10.11558 h -1.7917 l -1.19445,-14.29619 q 0.59723,-1.13847 0.59723,-1.77302 z m 16.25585,-6.55087 q 0,0.89585 -7.1481,8.32389 7.74533,7.07345 7.74533,7.74532 l 1.19446,-0.59723 h 2.38891 l -1.19445,1.7917 1.19445,1.79169 -2.98614,0.59722 q 0,-0.59722 -0.59723,-0.59722 -5.35641,5.95363 -8.34255,5.95363 0,-0.82119 6.56952,-7.1481 l -7.74532,-7.14809 h -1.19446 l -1.19445,0.59723 v -1.19446 q 7.8013,-10.71282 11.31003,-10.71282 z m 10.1529,-11.3287 0.59724,1.19445 v 5.3564 l 0.59723,4.18061 h 2.98615 l 2.96748,0.59724 v 0.59723 q -3.69535,1.17579 -5.95363,1.17579 v 22.04151 q 0,1.19447 -1.19447,1.19447 h -0.59723 l -1.79169,-22.63875 -3.56471,-0.59723 q 0,-1.77302 4.16194,-1.77302 v -10.73148 q 0.63456,0 1.79169,-0.59722 z m 22.26548,9.53701 0.57856,9.53701 2.98615,-1.79169 v 0.59724 q -1.23179,2.37025 -2.98615,2.37025 v 13.10172 q 0,1.19447 -1.17579,1.19447 0,-1.19447 -6.55087,-1.77302 l -3.58338,0.57855 h -5.95363 v -0.57855 l 14.89341,-11.92595 v -1.19445 q 0,-2.98615 -0.59722,-8.92113 -1.95966,1.19447 -3.58339,1.19447 h -5.95363 v -1.19447 z m -11.92594,20.84706 v 0.59722 h 10.73148 l -0.59724,-1.19446 v -6.55086 q -9.21973,7.1481 -10.13424,7.1481 z"
id="text2"
aria-label="Lista" />
<g
id="g1"
transform="translate(-24.107343,0.2320709)"
style="fill:#ffffff">
<path
style="fill:#ffffff;stroke-width:0.264583"
d="m 29.404109,147.76027 24.965754,-29.40411 8.506849,27.18493 -18.863013,-4.99315 11.465753,-1.84931 -3.328768,-8.50685 z"
id="path1" />
<path
style="fill:#ffffff;stroke-width:0.264583"
d="m 85.964916,127.3725 -2.484559,20.00725 5.099886,-2.74609 -0.261533,-15.9535 z"
id="path3" />
<path
style="fill:#ffffff;stroke-width:0.264583"
d="m 93.681336,120.28459 -0.523057,27.01245 3.897809,-5.57959 1.438421,-12.22727 4.994271,7.14753 10.50232,-5.00048 20.40824,15.76428 -19.23016,-21.15733 -11.68035,2.15524 -4.535794,-6.98064 z"
id="path4" />
<path
id="path6"
style="fill:#ffffff;stroke-width:0.264583"
d="m 96.140067,111.32683 -7.157492,11.45215 1.595562,-3.81455 c 0,0 -3.053077,3.56345 -3.666089,4.48961 -0.336128,0.45432 -0.526495,0.92333 -0.572206,1.48812 3.3e-5,0.90271 0.731817,1.63449 1.634526,1.63452 0.517396,-0.002 0.89356,-0.23493 1.310514,-0.66456 0.416954,-0.42963 0.60197,-1.28077 0.902956,-1.92116 1.984074,-4.22138 5.952229,-12.66413 5.952229,-12.66413 z" />
<path
style="fill:#ffffff;stroke-width:0.264583"
d="m 137.68041,136.52066 12.48287,-13.40753 -31.80821,3.14383 23.2089,20.71234 -12.6678,-16.82877 10.91095,-0.5548 z"
id="path8" />
<path
style="fill:#ffffff;stroke-width:0.264583"
d="m 65.280822,143.78424 -6.010275,-20.80479 16.366437,15.44178 4.253426,-23.30137 4.80822,-2.58904 -6.287672,34.67466 -12.760275,-12.11302 z"
id="path9" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 949 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<path d="M4,6C4,7.097 4.903,8 6,8C7.097,8 8,7.097 8,6C8,4.903 7.097,4 6,4C4.903,4.001 4.001,4.903 4,6ZM7,6C7,6.549 6.549,7 6,7C5.451,7 5,6.549 5,6C5,5.451 5.451,5 6,5C6.548,5.001 6.999,5.452 7,6Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M10.525,16L2,16C1.522,15.995 1.112,15.644 1.035,15.172L4.5,11.707L7.646,14.853C7.74,14.943 7.864,14.994 7.994,14.994C8.268,14.994 8.494,14.768 8.494,14.494C8.494,14.364 8.443,14.24 8.353,14.146L7.457,13.25L12.5,8.207L14.059,9.766C14.152,9.858 14.278,9.909 14.409,9.909C14.683,9.909 14.909,9.683 14.909,9.409C14.909,9.278 14.858,9.152 14.766,9.059L12.853,7.146C12.76,7.053 12.633,7 12.5,7C12.367,7 12.24,7.053 12.146,7.146L6.75,12.543L4.854,10.646C4.76,10.553 4.633,10.5 4.5,10.5C4.367,10.5 4.24,10.553 4.147,10.646L1,13.793L1,4C1.001,3.452 1.452,3.001 2,3L19,3C19.548,3.001 19.999,3.452 20,4L20,8.936C20,9.21 20.226,9.436 20.5,9.436C20.774,9.436 21,9.21 21,8.936L21,4C20.999,2.903 20.097,2.001 19,2L2,2C0.903,2.001 0.001,2.903 0,4L0,15C0.001,16.097 0.903,16.999 2,17L10.525,17C10.8,17 11.025,16.774 11.025,16.5C11.025,16.226 10.8,16 10.525,16Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M18.354,13.146C18.26,13.053 18.133,13 18,13C17.867,13 17.74,13.053 17.647,13.146L15.647,15.146C15.557,15.24 15.506,15.364 15.506,15.494C15.506,15.768 15.732,15.994 16.006,15.994C16.136,15.994 16.26,15.943 16.354,15.853L17.5,14.707L17.5,18.499C17.5,18.774 17.726,18.999 18,18.999C18.274,18.999 18.5,18.774 18.5,18.499L18.5,14.707L19.646,15.853C19.74,15.943 19.864,15.994 19.994,15.994C20.268,15.994 20.494,15.768 20.494,15.494C20.494,15.364 20.443,15.24 20.353,15.146L18.354,13.146Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M18,10C14.708,10 12,12.708 12,16C12,19.292 14.708,22 18,22C21.292,22 24,19.292 24,16C23.996,12.71 21.29,10.004 18,10ZM18,21C15.257,21 13,18.743 13,16C13,13.257 15.257,11 18,11C20.743,11 23,13.257 23,16C22.997,18.742 20.742,20.997 18,21Z" style="fill:white;fill-rule:nonzero;"/>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

View file

@ -0,0 +1,121 @@
<template lang="html">
<button class="bt bt-normal" @click="modalGroupAddVisible = true">
<svg width="16" height="16" fill="currentColor" class="bi bi-database-fill-add">
<use xlink:href="#ico-add-group"/>
</svg>
Zarządzaj Grupami
</button>
<button class="bt bt-normal" @click="modalAddVisible = true">
<svg width="16" height="16" fill="currentColor" class="bi bi-database-fill-add">
<use xlink:href="#ico-add-record"/>
</svg>
Dodaj Anime
</button>
<button class="bt bt-normal" @click="ShowModalEditWindow" :disabled="editButton">
<svg width="16" height="16" fill="currentColor" class="bi bi-database-fill-add">
<use xlink:href="#ico-edit-record"/>
</svg>
Edytuj
</button>
<button class="bt bt-danger" @click="modalDeleteVisible = true" :disabled="deleteButton">
<svg width="16" height="16" fill="currentColor" class="bi bi-database-fill-add">
<use xlink:href="#ico-remove-record"/>
</svg>
Skasuj
</button>
<ModalAddWindow v-if="modalAddVisible" @closeModal="modalAddVisible = false"/>
<ModalEditWindow v-if="modalEditVisible" @closeModal="modalEditVisible = false" />
<ModalAddGroupWindow v-if="modalGroupAddVisible" @closeModal="modalGroupAddVisible = false" />
<ModalEditGroupWindow v-if="modalGroupEditVisible" @closeModal="modalGroupEditVisible = false" />
<ModalDeleteWindow v-if="modalDeleteVisible" @closeModal="modalDeleteVisible = false" />
</template>
<script lang="js">
import { useModeratedStore } from '@/stores/moderated';
import { ref, watch } from 'vue';
import ModalAddWindow from './modals/add.vue';
import ModalEditWindow from './modals/edit.vue';
import ModalAddGroupWindow from './modals/group.vue';
import ModalEditGroupWindow from './modals/group-edit.vue';
import ModalDeleteWindow from './modals/delete.vue';
export default{
name: "AnimeBtSet",
components: {
ModalAddWindow,
ModalEditWindow,
ModalAddGroupWindow,
ModalEditGroupWindow,
ModalDeleteWindow,
},
data() {
return {
//Modale
modalAddVisible: false,
modalEditVisible: false,
modalDeleteVisible: false,
modalGroupAddVisible: false,
modalGroupEditVisible: false,
}
},
setup() {
//Moderated
const moderated = useModeratedStore();
const editButton = ref(moderated.editButton);
const deleteButton = ref(moderated.deleteButton);
const animeEdit = ref(moderated.animeEdit);
const groupEdit = ref(moderated.groupEdit);
watch(
() => moderated.editButton, (newValue) => {
editButton.value = newValue;
}
);
watch(
() => moderated.deleteButton, (newValue) => {
deleteButton.value = newValue;
}
);
watch(
() => moderated.animeEdit, (newValue) => {
animeEdit.value = newValue;
}
);
watch(
() => moderated.groupEdit, (newValue) => {
groupEdit.value = newValue;
}
);
return {
//Moderated
editButton,
deleteButton,
//
animeEdit,
groupEdit,
};
},
methods: {
//Modal edycja
ShowModalEditWindow() {
this.modalEditVisible = this.animeEdit;
this.modalGroupEditVisible = this.groupEdit;
},
}
};
</script>

View file

@ -0,0 +1,11 @@
<template lang="html">
<button class="bt bt-danger" @click="$router.push('/anime')">Strona główna</button>
<!--<button class="bt bt-normal">Dane użytkownika</button>
<button class="bt bt-normal">Baza danych</button>
<button class="bt bt-normal">Panel Administracyjny</button>-->
</template>
<script lang="js">
export default {
name: "ControlBtSet",
};
</script>

View file

@ -0,0 +1,368 @@
<template lang="html">
<main class="user-panel">
<div class="user-avatar-section">
<h3>Avatar</h3>
<div class="avatar-row">
<div class="user-avatar-container">
<img :src="avatarImg" class="user-avatar-img" />
</div>
<div class="user-avatar-bt-set">
<div class="avatar-input-box">
<input type="file" id="avatar" accept="image/png, image/jpeg" @change="HandleAvatartFile" hidden />
<button class="bt bt-normal" style="width: 160px;"><label for="avatar">
<svg width="16" height="16" fill="currentColor">
<use xlink:href="#ico-catalog"/>
</svg>
Przeglądaj...</label></button>
<div class="avatar-path-string">{{ avatarFilePath }}</div>
</div>
<div class="avatar-bt-update">
<button class="bt bt-accept" style="width: 160px;" @click="UpdateAvatar">
<svg width="16" height="16" fill="currentColor">
<use xlink:href="#ico-upload"/>
</svg>
Aktualizuj</button>
<button class="bt bt-danger" style="width: 160px;" @click="DeleteUserAvatar">
<svg width="16" height="16" fill="currentColor">
<use xlink:href="#ico-remove-record"/>
</svg>
Skasuj
</button>
</div>
</div>
</div>
<span class="smol-info-txt" style="margin-top: 8px;">Plik nie może być większy niż 1MB</span>
<MessageWindow v-if="avatarMessBexVisible" :mess="avatarMessBoxData" @messHide="avatarMessBexVisible = false" />
</div>
<hr class="panel-hr-separator">
<div class="user-data-section">
<div>
<h3>Dane użytnika</h3>
<div class="pad32">
<label for="user-name">Nazwa użytkownika</label>
<input type="text" v-model="nickName" :maxlength="maxCharLen" id="user-name" class="in in-default" style="width: 100%;" />
<span class="smol-info-txt">Dostępne: {{ maxCharLen - UserLenghtGlyph }} znaków</span>
</div>
<button class="bt bt-accept" @click="UpdateProfile" style="width: 160px;">Aktualizuj dane</button>
<MessageWindow v-if="userMessBoxVisible" :mess="userMessBoxData" @messHide="userMessBoxVisible = false" />
</div>
</div>
<hr class="panel-hr-separator">
<div class="user-data-section">
<div class="user-password-section">
<h3>Hasło</h3>
<label for="user-old-password">Aktualne hasło</label>
<input type="password" v-model="actualPasswod" :minlength="minPassChar" :maxlength="maxPassChar" class="in in-default" />
<div class="user-new-password-section margtb8">
<div class="passwod-box">
<label for="user-new-password">Nowe hasło</label>
<input type="password" v-model="newPassword" :minlength="minPassChar" :maxlength="maxPassChar" id="user-new-password" class="in in-default" />
<span :class="newPassTxtClass">Dostępne: {{ maxPassChar - NewPassLenghtGlyph }} znaków</span>
</div>
<div class="passwod-box">
<label for="user-confirm-password">Potwierdź hasło</label>
<input type="password" v-model="confirmPassword" :minlength="minPassChar" :maxlength="maxPassChar" id="user-confirm-password" class="in in-default" />
</div>
</div>
<button @click="updatePassword" class="bt bt-accept" style="width: 160px; margin-top: 16px;">Aktualizuj hasło</button>
<MessageWindow v-if="passwordMessBoxVisible" :mess="passwdMessBoxData" @messHide="passwordMessBoxVisible = false" />
</div>
</div>
</main>
</template>
<script lang="js">
import { useAuterizationStore } from '@/stores/auterization';
import MessageWindow from '@/components/ui/message.vue';
import { ref, watch } from 'vue';
export default {
name: 'UserControlContent',
components: {
MessageWindow,
},
data() {
return {
avatarFilePath: "",
avatarDefaultText: "...Nie wybrano pliku...",
//avatar file
avatarFile: null,
//user data
nickName: '',
charLenght: 0,
maxCharLen: 56,
//user passwod
actualPasswod: '',
//new password
newPassword: '',
confirmPassword: '',
//max passwond lenght
minPassChar: 8,
maxPassChar: 255,
newPassTxtClass: 'smol-info-txt',
//Message Box
passwordMessBoxVisible: false,
passwdMessBoxData: {},
userMessBoxVisible: false,
userMessBoxData: {},
avatarMessBexVisible: false,
avaterMessBoxData: {},
}
},
setup() {
const auterization = useAuterizationStore();
const avatarImg = ref(auterization.userAvatar);
const userName = ref(auterization.userName);
watch(
() => auterization.userAvatar, (newValue) => {
avatarImg.value = newValue;
}
);
return {
avatarImg,
userName,
UpdateUserAvatar: auterization.UpdateUserAvatar,
DeleteUserAvatar: auterization.DeleteUserAvatar,
UpdateUserData: auterization.UpdateUserData,
UpdateUserPassword: auterization.UpdateUserPassword,
}
},
computed: {
UserLenghtGlyph() {
return this.nickName.length;
},
NewPassLenghtGlyph() {
return this.newPassword.length;
}
},
mounted() {
this.avatarFilePath = this.avatarDefaultText;
this.nickName = this.userName;
},
methods: {
async HandleAvatartFile(event) {
const file = event.target.files[0];
if(file) {
this.avatarFilePath = file.name;
this.avatarFile = file;
}
},
//Aktualizacja avatara
async UpdateAvatar() {
console.log(this.avatarFile);
if (!this.avatarFile) {
console.error(`Nie wybrano pliku`);
this.ShowAvatarMessage('MESS_ERROR', 'Nie wybrano pliku');
return;
}
if (this.avatarFile.size > 1024 * 1024) {
console.error(`Plik jest za duży. Max 1MB`);
this.ShowAvatarMessage('MESS_ERROR', 'Plik jest za duży. Maksymalny rozmiar pliku to 1MB');
return;
}
const formData = new FormData();
formData.append("file", this.avatarFile);
console.log(this.avatarFile);
const response = await this.UpdateUserAvatar(formData);
if (response.success) {
this.avatarFile = null;
this.ShowAvatarMessage('MESS_OK', response.mess);
}
else {
this.ShowAvatarMessage('MESS_ERROR', response.mess);
}
},
//Kasowanie avatara
async DeleteAvatar() {
const response = await this.DeleteUserAvatar();
if (response.success) {
this.avatarFile = null;
this.ShowAvatarMessage('MESS_OK', response.mess);
}
else {
this.ShowAvatarMessage('MESS_ERROR', response.mess);
}
},
//Aktualizacja hasła request
async updatePassword() {
if (this.newPassword && this.confirmPassword) {
if (this.NewPassLenghtGlyph < 8) {
this.ShowPasswordMessage('MESS_ERROR', 'Hasło musi zawierać conajmniej 8 znaków!');
return;
}
if (!this.CompareTwoPasswdStrings(this.newPassword, this.confirmPassword)) {
this.ShowPasswordMessage('MESS_ERROR', 'Hasła nie są do siebie podobne');
return;
}
if (!this.ValidGlyphsPass(this.newPassword)) {
this.ShowPasswordMessage('MESS_ERROR', 'Hasło musi składać się z liter a-z A-Z 0-9 i znaki specjalne');
return;
}
const passdata = {
pass: this.actualPasswod,
newPass: this.newPassword,
confirmPass: this.confirmPassword
};
const response = await this.UpdateUserPassword(passdata);
if (response.success) {
console.log('Udało się pomyślnie zaktualizować hasło');
this.actualPasswod = '';
this.newPassword = '';
this.confirmPassword = '';
this.ShowPasswordMessage('MESS_OK', response.mess);
}
else {
console.error(response.mess);
this.ShowPasswordMessage('MESS_ERROR', response.mess);
}
}
else {
console.error('Nie wypełniono pól');
this.ShowPasswordMessage('MESS_ERROR', 'Nie wypełniono wymaganych pól');
}
},
//Aktualizacja danych użytnika request
async UpdateProfile() {
if (this.nickName) {
if (this.UserLenghtGlyph < 3) {
this.ShowUserMessage('MESS_ERROR', 'Nazwa użytkownika powinna składać się co najmniej 3 znaków');
return;
}
if (!this.ValidGlyphsLetters(this.nickName) && !this.ValidGlyphsDigits(this.nickName) && this.ValidGlyphsSpecialChar(this.nickName)) {
this.ShowUserMessage('MESS_ERROR', 'Nazwa użytkownika powinna składać się jedynie ze znaków a-z A-Z 0-9');
return;
}
const data = {name: this.nickName};
const response = await this.UpdateUserData(data);
if (response.success) {
this.ShowUserMessage('MESS_OK', response.mess);
}
else {
this.ShowUserMessage('MESS_ERROR', response.mess);
}
}
else {
console.error('Pole nazwy użytkownika jest puste');
this.ShowUserMessage('MESS_ERROR', 'Wymagane pole jest puste');
}
},
//Sprawdzanie poprawności wpisanego hasla
ValidGlyphsPass(pass) {
const hasUpperCase = /[A-Z]/.test(pass);
const hasLowerCase = /[a-z]/.test(pass);
const hasDigit = /\d/.test(pass);
const hasSpecialChar = /[!@#$%^&*(),.?":{}|<>]/.test(pass);
return hasUpperCase && hasLowerCase && hasDigit && hasSpecialChar;
},
//Sprawdza duże i małe literki
ValidGlyphsLetters(s) {
const hasUpperCase = /[A-Z]/.test(s);
const hasLowerCase = /[a-z]/.test(s);
return hasUpperCase && hasLowerCase;
},
//Sprawdza cyferki
ValidGlyphsDigits(s) {
const hasDigit = /\d/.test(s);
return hasDigit;
},
//Sprawdza znaki specjalne
ValidGlyphsSpecialChar(s) {
const hasSpecialChar = /[!@#$%^&*(),.?":{}|<>]/.test(s);
return hasSpecialChar;
},
//Porównanie nowego hasła z ciągiem kontrolnym
CompareTwoPasswdStrings(pass1, pass2) {
if (pass1 === pass2) {return true}
return false;
},
//Kontrola message boxów
ShowPasswordMessage(c, m) {
this.passwordMessBoxVisible = false;
this.passwdMessBoxData = {
type: c,
content: m
};
this.passwordMessBoxVisible = true;
},
ShowUserMessage(c, m) {
this.userMessBoxVisible = false;
this.userMessBoxData = {
type: c,
content: m
};
this.userMessBoxVisible = true;
},
ShowAvatarMessage(c, m) {
this.avatarMessBexVisible = false;
this.avatarMessBoxData = {
type: c,
content: m
};
this.avatarMessBexVisible = true;
}
},
}
</script>

View file

@ -0,0 +1,54 @@
<template lang="html">
<footer>
<div class="ft-copy-date">v. 2.0.1 alpha</div>
<div class="ft-others-container">
<div class="powered-by-container">
<div class="vue-container">
<a href="https://vuejs.org/" class="vue-logo-set">
<img src="./../assets/img/vue-logo.png" class="vue-logo-img">
<div class="vue-txt-container">Vue.js</div>
</a>
<div class="vue-descryption">
<div class="vue-desc-txt">
This site using Vue.js to creating<br>frontend and pages.
</div>
</div>
<a href="https://getbootstrap.com/" class="bootstrap-logo-set">
<img src="./../assets/img/bootstrap-logo.png" class="vue-logo-img">
<div class="vue-txt-container">Bootstrap</div>
</a>
<div class="vue-descryption">
<div class="vue-desc-txt">
This site using Bootstap to designed.
</div>
</div>
</div>
</div>
<div class="license-container">
<img src="./../assets/img/mit-license-logo.png" class="license-img"/>
<div class="txt-license-url">
Treść licencji możesz przejżeć <a href="">TUTAJ</a>
</div>
</div>
<div class="credits-container">
<div class="ft-author-txt">Created by: Yanczi<br>2025</div>
<div class="ft-credits">
<p>Links:</p>
<ul>
<li>Github</li>
<li>Middleware</li>
</ul>
</div>
</div>
</div>
</footer>
</template>
<script>
export default {
name: 'WebSiteFooter'
}
</script>
<style lang="">
</style>

View file

@ -0,0 +1,135 @@
<template lang="html">
<!-- Set ikonek na przyciski -->
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
<!--Baza danych z plusikiem-->
<symbol id="ico-add-record" viewBox="0 0 16 16">
<path d="M12.5 16a3.5 3.5 0 1 0 0-7 3.5 3.5 0 0 0 0 7Zm.5-5v1h1a.5.5 0 0 1 0 1h-1v1a.5.5 0 0 1-1 0v-1h-1a.5.5 0 0 1 0-1h1v-1a.5.5 0 0 1 1 0ZM8 1c-1.573 0-3.022.289-4.096.777C2.875 2.245 2 2.993 2 4s.875 1.755 1.904 2.223C4.978 6.711 6.427 7 8 7s3.022-.289 4.096-.777C13.125 5.755 14 5.007 14 4s-.875-1.755-1.904-2.223C11.022 1.289 9.573 1 8 1Z"/>
<path d="M2 7v-.839c.457.432 1.004.751 1.49.972C4.722 7.693 6.318 8 8 8s3.278-.307 4.51-.867c.486-.22 1.033-.54 1.49-.972V7c0 .424-.155.802-.411 1.133a4.51 4.51 0 0 0-4.815 1.843A12.31 12.31 0 0 1 8 10c-1.573 0-3.022-.289-4.096-.777C2.875 8.755 2 8.007 2 7Zm6.257 3.998L8 11c-1.682 0-3.278-.307-4.51-.867-.486-.22-1.033-.54-1.49-.972V10c0 1.007.875 1.755 1.904 2.223C4.978 12.711 6.427 13 8 13h.027a4.552 4.552 0 0 1 .23-2.002Zm-.002 3L8 14c-1.682 0-3.278-.307-4.51-.867-.486-.22-1.033-.54-1.49-.972V13c0 1.007.875 1.755 1.904 2.223C4.978 15.711 6.427 16 8 16c.536 0 1.058-.034 1.555-.097a4.507 4.507 0 0 1-1.3-1.905Z"/>
</symbol>
<!--Kółko z plusikiem-->
<symbol id="ico-add-group" viewBox="0 0 16 16">
<path d="M0 2a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v2h2a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2v-2H2a2 2 0 0 1-2-2zm5 10v2a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V6a1 1 0 0 0-1-1h-2v5a2 2 0 0 1-2 2zm6-8V2a1 1 0 0 0-1-1H2a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h2V6a2 2 0 0 1 2-2z"/>
</symbol>
<!--Klucz-->
<symbol id="ico-edit-record" viewBox="0 0 16 16">
<path d="M1 0 0 1l2.2 3.081a1 1 0 0 0 .815.419h.07a1 1 0 0 1 .708.293l2.675 2.675-2.617 2.654A3.003 3.003 0 0 0 0 13a3 3 0 1 0 5.878-.851l2.654-2.617.968.968-.305.914a1 1 0 0 0 .242 1.023l3.27 3.27a.997.997 0 0 0 1.414 0l1.586-1.586a.997.997 0 0 0 0-1.414l-3.27-3.27a1 1 0 0 0-1.023-.242L10.5 9.5l-.96-.96 2.68-2.643A3.005 3.005 0 0 0 16 3c0-.269-.035-.53-.102-.777l-2.14 2.141L12 4l-.364-1.757L13.777.102a3 3 0 0 0-3.675 3.68L7.462 6.46 4.793 3.793a1 1 0 0 1-.293-.707v-.071a1 1 0 0 0-.419-.814L1 0Zm9.646 10.646a.5.5 0 0 1 .708 0l2.914 2.915a.5.5 0 0 1-.707.707l-2.915-2.914a.5.5 0 0 1 0-.708ZM3 11l.471.242.529.026.287.445.445.287.026.529L5 13l-.242.471-.026.529-.445.287-.287.445-.529.026L3 15l-.471-.242L2 14.732l-.287-.445L1.268 14l-.026-.529L1 13l.242-.471.026-.529.445-.287.287-.445.529-.026L3 11Z"/>
</symbol>
<!--Hobok-->
<symbol id="ico-remove-record" viewBox="0 0 16 16">
<path d="M11 1.5v1h3.5a.5.5 0 0 1 0 1h-.538l-.853 10.66A2 2 0 0 1 11.115 16h-6.23a2 2 0 0 1-1.994-1.84L2.038 3.5H1.5a.5.5 0 0 1 0-1H5v-1A1.5 1.5 0 0 1 6.5 0h3A1.5 1.5 0 0 1 11 1.5Zm-5 0v1h4v-1a.5.5 0 0 0-.5-.5h-3a.5.5 0 0 0-.5.5ZM4.5 5.029l.5 8.5a.5.5 0 1 0 .998-.06l-.5-8.5a.5.5 0 1 0-.998.06Zm6.53-.528a.5.5 0 0 0-.528.47l-.5 8.5a.5.5 0 0 0 .998.058l.5-8.5a.5.5 0 0 0-.47-.528ZM8 4.5a.5.5 0 0 0-.5.5v8.5a.5.5 0 0 0 1 0V5a.5.5 0 0 0-.5-.5Z"/>
</symbol>
<!--Lupka-->
<symbol id="ico-search" viewBox="0 0 16 16">
<path d="M6.5,12C9.517,12 12,9.517 12,6.5C12,3.483 9.517,1 6.5,1C3.483,1 1,3.483 1,6.5C1,9.517 3.483,12 6.5,12ZM13,6.5C13,10.066 10.066,13 6.5,13C2.934,13 0,10.066 0,6.5C-0,2.934 2.934,0 6.5,0C10.066,-0 13,2.934 13,6.5Z"/>
<path d="M10.344,11.742C10.374,11.782 10.406,11.82 10.442,11.857L14.292,15.707C14.48,15.895 14.734,16 14.999,16C15.548,16 16,15.549 16,15C16,14.735 15.894,14.48 15.707,14.293L11.857,10.443C11.821,10.407 11.783,10.373 11.742,10.343C11.35,10.878 10.878,11.35 10.344,11.743L10.344,11.742Z" style="fill-rule:nonzero;"/>
</symbol>
<!--Ikonka link-->
<symbol id="ico-hyperlink" viewBox="0 0 16 16">
<path d="M4.715 6.542 3.343 7.914a3 3 0 1 0 4.243 4.243l1.828-1.829A3 3 0 0 0 8.586 5.5L8 6.086a1.002 1.002 0 0 0-.154.199 2 2 0 0 1 .861 3.337L6.88 11.45a2 2 0 1 1-2.83-2.83l.793-.792a4.018 4.018 0 0 1-.128-1.287z"/>
<path d="M6.586 4.672A3 3 0 0 0 7.414 9.5l.775-.776a2 2 0 0 1-.896-3.346L9.12 3.55a2 2 0 1 1 2.83 2.83l-.793.792c.112.42.155.855.128 1.287l1.372-1.372a3 3 0 1 0-4.243-4.243L6.586 4.672z"/>
</symbol>
<!--Symbol wyłączenia-->
<symbol id="ico-logout" viewBox="0 0 16 16">
<path d="M7.5 1v7h1V1h-1z"/>
<path d="M3 8.812a4.999 4.999 0 0 1 2.578-4.375l-.485-.874A6 6 0 1 0 11 3.616l-.501.865A5 5 0 1 1 3 8.812z"/>
</symbol>
<!--strzałka w prawo-->
<symbol id="ico-next" viewBox="0 0 16 16">
<path d="m12.14 8.753-5.482 4.796c-.646.566-1.658.106-1.658-.753V3.204a1 1 0 0 1 1.659-.753l5.48 4.796a1 1 0 0 1 0 1.506z"/>
</symbol>
<symbol id="ico-next-border" viewBox="0 0 16 16">
<path d="M6 12.796V3.204L11.481 8zm.659.753 5.48-4.796a1 1 0 0 0 0-1.506L6.66 2.451C6.011 1.885 5 2.345 5 3.204v9.592a1 1 0 0 0 1.659.753"/>
</symbol>
<!--strzałka w lewo-->
<symbol id="ico-back" viewBox="0 0 16 16">
<path d="m3.86 8.753 5.482 4.796c.646.566 1.658.106 1.658-.753V3.204a1 1 0 0 0-1.659-.753l-5.48 4.796a1 1 0 0 0 0 1.506z"/>
</symbol>
<symbol id="ico-back-border" viewBox="0 0 16 16">
<path d="M10 12.796V3.204L4.519 8zm-.659.753-5.48-4.796a1 1 0 0 1 0-1.506l5.48-4.796A1 1 0 0 1 11 3.204v9.592a1 1 0 0 1-1.659.753"/>
</symbol>
<!--Strzałka w dół-->
<symbol id="ico-down" viewBox="0 0 16 16">
<path d="M7.247 11.14 2.451 5.658C1.885 5.013 2.345 4 3.204 4h9.592a1 1 0 0 1 .753 1.659l-4.796 5.48a1 1 0 0 1-1.506 0z"/>
</symbol>
<!--Strzałka do góry-->
<symbol id="ico-up" viewBox="0 0 16 16">
<path d="m7.247 4.86-4.796 5.481c-.566.647-.106 1.659.753 1.659h9.592a1 1 0 0 0 .753-1.659l-4.796-5.48a1 1 0 0 0-1.506 0z"/>
</symbol>
<!--Oglądane-->
<symbol id="ico-watch" viewBox="0 0 16 16">
<path d="M10.5 8a2.5 2.5 0 1 1-5 0 2.5 2.5 0 0 1 5 0"/>
<path d="M0 8s3-5.5 8-5.5S16 8 16 8s-3 5.5-8 5.5S0 8 0 8m8 3.5a3.5 3.5 0 1 0 0-7 3.5 3.5 0 0 0 0 7"/>
</symbol>
<!--Nie oglądane-->
<symbol id="ico-nowatch" viewBox="0 0 16 16">
<path d="m10.79 12.912-1.614-1.615a3.5 3.5 0 0 1-4.474-4.474l-2.06-2.06C.938 6.278 0 8 0 8s3 5.5 8 5.5a7 7 0 0 0 2.79-.588M5.21 3.088A7 7 0 0 1 8 2.5c5 0 8 5.5 8 5.5s-.939 1.721-2.641 3.238l-2.062-2.062a3.5 3.5 0 0 0-4.474-4.474z"/>
<path d="M5.525 7.646a2.5 2.5 0 0 0 2.829 2.829zm4.95.708-2.829-2.83a2.5 2.5 0 0 1 2.829 2.829zm3.171 6-12-12 .708-.708 12 12z"/>
</symbol>
<!--Porzucone-->
<symbol id="ico-canceled" viewBox="0 0 16 16">
<path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0m-4.646-2.646a.5.5 0 0 0-.708-.708l-6 6a.5.5 0 0 0 .708.708z"/>
</symbol>
<!--Oglądane-->
<symbol id="ico-watched" viewBox="0 0 16 16">
<path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0m-3.97-3.03a.75.75 0 0 0-1.08.022L7.477 9.417 5.384 7.323a.75.75 0 0 0-1.06 1.06L6.97 11.03a.75.75 0 0 0 1.079-.02l3.992-4.99a.75.75 0 0 0-.01-1.05z"/>
</symbol>
<!--Lejek-->
<symbol id="ico-funnel" viewBox="0 0 16 16">
<path d="M1.5 1.5A.5.5 0 0 1 2 1h12a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-.128.334L10 8.692V13.5a.5.5 0 0 1-.342.474l-3 1A.5.5 0 0 1 6 14.5V8.692L1.628 3.834A.5.5 0 0 1 1.5 3.5z"/>
</symbol>
<!--Trójkąt ostrzegawczy-->
<symbol id="ico-exclamation" viewBox="0 0 16 16">
<path d="M1.5 1.5A.5.5 0 0 1 2 1h12a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-.128.334L10 8.692V13.5a.5.5 0 0 1-.342.474l-3 1A.5.5 0 0 1 6 14.5V8.692L1.628 3.834A.5.5 0 0 1 1.5 3.5z"/>
</symbol>
<!--Krzyżyk-->
<symbol id="ico-cross" viewBox="0 0 16 16">
<path d="M12.946,3.05C13.14,3.243 13.141,3.562 12.946,3.757L8.7,8.003L12.941,12.244C13.136,12.439 13.135,12.757 12.941,12.951C12.748,13.145 12.429,13.146 12.234,12.951L7.993,8.71L3.757,12.946C3.562,13.141 3.243,13.14 3.05,12.946C2.856,12.753 2.855,12.434 3.05,12.239L7.286,8.003L3.035,3.752C2.84,3.557 2.841,3.238 3.035,3.045C3.229,2.851 3.547,2.85 3.742,3.045L7.993,7.296L12.239,3.05C12.434,2.855 12.753,2.856 12.946,3.05Z"/>
</symbol>
<!--Kółko graniaste koło kanciaste... GEAR-->
<symbol id="ico-gear" viewBox="0 0 16 16">
<path d="M9.405 1.05c-.413-1.4-2.397-1.4-2.81 0l-.1.34a1.464 1.464 0 0 1-2.105.872l-.31-.17c-1.283-.698-2.686.705-1.987 1.987l.169.311c.446.82.023 1.841-.872 2.105l-.34.1c-1.4.413-1.4 2.397 0 2.81l.34.1a1.464 1.464 0 0 1 .872 2.105l-.17.31c-.698 1.283.705 2.686 1.987 1.987l.311-.169a1.464 1.464 0 0 1 2.105.872l.1.34c.413 1.4 2.397 1.4 2.81 0l.1-.34a1.464 1.464 0 0 1 2.105-.872l.31.17c1.283.698 2.686-.705 1.987-1.987l-.169-.311a1.464 1.464 0 0 1 .872-2.105l.34-.1c1.4-.413 1.4-2.397 0-2.81l-.34-.1a1.464 1.464 0 0 1-.872-2.105l.17-.31c.698-1.283-.705-2.686-1.987-1.987l-.311.169a1.464 1.464 0 0 1-2.105-.872zM8 10.93a2.929 2.929 0 1 1 0-5.86 2.929 2.929 0 0 1 0 5.858z"/>
</symbol>
<!--Katalog-->
<symbol id="ico-catalog" viewBox="0 0 16 16">
<path d="M9.828 3h3.982a2 2 0 0 1 1.992 2.181l-.637 7A2 2 0 0 1 13.174 14H2.825a2 2 0 0 1-1.991-1.819l-.637-7a2 2 0 0 1 .342-1.31L.5 3a2 2 0 0 1 2-2h3.672a2 2 0 0 1 1.414.586l.828.828A2 2 0 0 0 9.828 3m-8.322.12q.322-.119.684-.12h5.396l-.707-.707A1 1 0 0 0 6.172 2H2.5a1 1 0 0 0-1 .981z"/>
</symbol>
<!--Upload-->
<symbol id="ico-upload" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M8 0a5.53 5.53 0 0 0-3.594 1.342c-.766.66-1.321 1.52-1.464 2.383C1.266 4.095 0 5.555 0 7.318 0 9.366 1.708 11 3.781 11H7.5V5.707L5.354 7.854a.5.5 0 1 1-.708-.708l3-3a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1-.708.708L8.5 5.707V11h4.188C14.502 11 16 9.57 16 7.773c0-1.636-1.242-2.969-2.834-3.194C12.923 1.999 10.69 0 8 0m-.5 14.5V11h1v3.5a.5.5 0 0 1-1 0"/>
</symbol>
<symbol id="ico-add" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M8 2a.5.5 0 0 1 .5.5v5h5a.5.5 0 0 1 0 1h-5v5a.5.5 0 0 1-1 0v-5h-5a.5.5 0 0 1 0-1h5v-5A.5.5 0 0 1 8 2"/>
</symbol>
</svg>
</template>
<script>
export default {
name: 'SvgIconSet'
}
</script>
<style lang="">
</style>

View file

@ -0,0 +1,86 @@
<template lang="html">
<div class="img-site-box">
<h2 style="font-weight: bold; margin-top: 38px;">Logowanie</h2>
</div>
<div class="sign-in-imput-box d-flex flex-column p-3 mt-5">
<div class="lgn-input-box">
<label for="sin-emial-input" class="form-label">E-mail</label>
<input type="email" class="in in-default" style="width: 100%;" id="sin-emial-input" aria-describedby="emailHelp" v-model="email">
<div id="emailHelp" class="form-text">We'll never share your email with anyone else.</div>
</div>
<div class="lgn-input-box">
<label for="sin-pass-input" class="form-label">Hasło</label>
<input type="password" class="in in-default" style="width: 100%;" id="sin-pass-input" v-model="password">
</div>
<div class="recovery-pass-hurl">
<span class="recovery-url" @click="$emit('recoverScr')" v-if="false">Nie pamiętam hasła</span>
</div>
</div>
<div class="sign-in-btcb p-3">
<button class="bt bt-normal mr-4 ml-4 bt-sin" @click="SignInUser" @ref="btnLogin">Zaloguj</button>
</div>
<div class="sin-mess mt-3 d-flex flex-row justify-content-center align-items-center scale-up-center" v-if="incorrectMess">
<div class="mess-string">Nie prawidłowe dane logowania</div>
</div>
</template>
<script lang="js">
import { useAuterizationStore } from '@/stores/auterization';
export default {
name: 'LoginWindow',
data() {
return {
email: "",
password: "",
remembersin: false,
//stanLogowania
success: false,
incorrectMess: false,
//Adres do validacji
validUrl: process.env.VUE_APP_USER_VALID
}
},
setup() {
const auterizationStore = useAuterizationStore();
return {
auterizationStore,
ValidLogin: auterizationStore.ValidLogin,
}
},
mounted() {
document.addEventListener('keydown', this.handleKeyEven);
},
unmounted() {
document.removeEventListener('keydown', this.handleKeyEven);
},
emits: ['shakeScr', 'recoverScr'],
methods: {
async SignInUser() {
this.success = await this.ValidLogin(this.email, this.password, this.remembersin);
if (this.success) {
this.$router.push('/anime');
//this.$emit('loginSuccess');
console.log('ZALOGOWANY');
this.incorrectMess = false;
}
else{
console.log('NIE DZIAŁA :C');
this.incorrectMess = true;
this.$emit("shakeScr");
}
},
}
}
</script>

View file

@ -0,0 +1,119 @@
<template lang="html">
<SvgIconSet />
<!-- Menu Główne -->
<nav class="navbar navbar-dark bg-dark nav-menu d-flex flex-row align-content-center align-items-center">
<div class="logo-container p-2 bd-highlight">
<img src='./../assets/img/logov3.svg' class="logo-img"/>
</div>
<div class="bt-set-container d-flex flex-row justicy-content-start align-items-center p-2 bd-highlight">
<AnimeBtSet v-if="animeSet" />
<ControlBtSet v-if="controlSet" />
</div>
<div class="user-container d-flex align-items-center justicy-content-between ms-auto p-2 bd-highlight">
<div class="user-box" @click="$router.push('/control-panel')">
<img :src='userAvatar' class="user-img"/>
<div class="user-name-container">
{{userName}}
</div>
</div>
<button class="bt-circle bt-danger user-bt-logout" @click="LogoutProcess">
<svg width="16" height="16" fill="currentColor" class="bi bi-database-fill-add">
<use xlink:href="#ico-logout"/>
</svg>
</button>
</div>
</nav>
</template>
<script>
import SvgIconSet from './iconset.vue';
import { useAuterizationStore } from '@/stores/auterization';
import { ref, watch } from 'vue';
import AnimeBtSet from './anime_menu_button_set.vue';
import ControlBtSet from './control_menu_button_set.vue';
export default {
name: 'MenuNav',
components: {
SvgIconSet,
AnimeBtSet,
ControlBtSet,
},
props: {
menuSet: String
},
data() {
return {
//Menu set ayzwalacze
animeSet: false,
controlSet: false,
}
},
setup() {
//User
const auterization = useAuterizationStore();
const userName = ref(auterization.userName);
const userAvatar = ref(auterization.userAvatar);
watch(
() => auterization.userName, (newValue) => {
userName.value = newValue;
},
() => auterization.userAvatar, (newValue) => {
userAvatar.value = newValue;
}
);
return {
userName,
userAvatar,
LogoutUser: auterization.LogoutUser,
};
},
mounted() {
switch(this.menuSet) {
case "ANIME":
this.animeSet = true;
break;
case "CONTROL":
this.controlSet = true;
break;
default:
this.animeSet = false;
this.controlSet = false;
break;
}
},
methods: {
async LogoutProcess() {
const res = await this.LogoutUser();
if(res){
console.log("TEST WYLOGOWANIA");
this.$router.push('/login');
}
else {
console.log("TEST NIE WYSZEDŁ");
}
},
ToggleUserMenu() {
this.userMenuVisible = !this.userMenuVisible;
},
}
}
</script>

View file

@ -0,0 +1,37 @@
<template lang="html">
<div class="modal" tabindex="-1" role="dialog" id="modal-window" aria-labelledby="modal-windowLabel" aria-hidden="true">
<div class="modal-dialog modal-color modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Modal</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<AddModal />
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Zamknij</button>
<button type="button" class="btn btn-primary">Dodaj</button>
</div>
</div>
</div>
</div>
</template>
<script>
import AddModal from '@/components/modals/add.vue';
export default {
name: 'ModalWindow',
props: {
modalType: String
},
components: {
AddModal
}
}
</script>
<style lang="css">
.modal-color {
color: #000;
}
</style>

View file

@ -0,0 +1,214 @@
<template lang="html">
<div class="md-body d-flex justify-content-center align-items-center">
<div class="md-content d-flex flex-column align-content-between">
<div class="md-header">
<h5>Dodaj animu</h5>
<div class="bt-md-exit" @click="CloseModal">
<svg width="32" height="32" fill="currentColor" class="bi bi-database-fill-add">
<use xlink:href="#ico-cross"/>
</svg>
</div>
</div>
<div class="md-body-content d-flex align-items-stretch p-4">
<div class="img-md-box">
<div class="img-file-upload-box">
<label for="img-input-form" style="cursor: pointer;">
<div class="img-placeholder" v-if="!imgShow">
<div class="waiting-on-click-to-upload-image" v-if="imgEmpty">
<img src="../../assets/img/upload_img.svg" width="64" height="64">
<p style=" user-select: none;">Załaduj obraz</p>
</div>
<LoadingWheel width="64" height="64" v-if="uploading" />
<img src="../../assets/img/fail_img.svg" width="64" height="64" v-if="imgFall">
</div>
<img :src="imageUrl" alt="" width="200" height="285" v-if="imgShow">
</label>
<input type="file" @change="HandleFileUpload" id="img-input-form" style="display: none;" accept="image/*" maxlength="1">
</div>
<button class="bt bt-danger" style="margin-top: 8px;" @click="DeleteImageUpload">Skasuj obraz</button>
</div>
<div class="content-md-box">
<div class="md-data-box">
<div class="md-form-box">
<label for="inp-title">Tytuł</label>
<input type="text" class="in in-default lb-title m-1" id="inp-title" v-model="title">
</div>
<div class="md-ep-x-gr-box">
<div class="md-form-box">
<label for="inp-episodes">Odcinki</label>
<input type="number" class="in in-default lb-episodes m-1" id="inp-episodes" v-model="episodes" pattern="\d*" oninput="this.value = this.value.replace(/[^0-9]/g, '')">
</div>
<div class="md-form-box">
<label for="slc-group">Grupa</label>
<select class="md-select-group-menu sl-default" id="slc-group" v-model="group">
<option value=0>Pozostałe</option>
<option v-for="option in groups" :key="option.gr_id" :value="option.gr_id">
{{ option.gr_title }}
</option>
</select>
</div>
</div>
<div class="md-form-box">
<label for="inp-url">Link</label>
<input type="text" class="in in-default lb-title m-1" id="inp-url" v-model="url">
</div>
<div class="md-form-box">
<label for="txa-description">Opis</label>
<textarea class="md-description-textbox tx tx-default" id="txa-description" v-model="description"></textarea>
</div>
</div>
</div>
</div>
<div class="md-footer d-flex flex-row justify-content-end p-4">
<button type="button" class="bt bt-normal" @click="CloseModal">Zamknij</button>
<button type="button" class="bt bt-accept bt-space" @click="AddAnime">Dodaj</button>
</div>
</div>
</div>
</template>
<script>
import { useAnimeStore } from '@/stores/anime';
import LoadingWheel from '../ui/loading.vue';
import '@/assets/css/modal.css';
export default {
name: 'ModalAddWindow',
components: {
LoadingWheel,
},
data() {
return {
//Dane animu do przesłania
title: '',
group: 0,
episodes: 1,
url: '',
description: '',
//obrazek
imageUrl: '',
timestamp: Date.now(),
//UPLOAD
uploading: false,
//Stany uploadowanej grafiki
imgShow: false,
imgEmpty: true,
imgFall: false,
}
},
setup() {
const animeStore = useAnimeStore();
return {
groups: animeStore.group_storage,
UploadImage: animeStore.UploadImage,
DeleteImage: animeStore.DeleteImage,
AddAnimeToDataBase: animeStore.AddAnimeToDataBase,
}
},
computed: {
imageUrlWithTimestamp() {
return `${this.imageUrl}?t=${this.timestamp}`;
},
//validIsNumber() {
// let tmp;
//}
},
inject: ['reloadTable'],
methods: {
CloseModal() {
this.$emit('closeModal');
},
async HandleFileUpload(event) {
const file = event.target.files[0];
if (!file) {
this.error = "No file selected.";
return;
}
if (file > 1024 * 1024) {
console.error(`Plik jest za duży. Max 1MB`);
//this.ShowAvatarMessage('MESS_ERROR', 'Plik jest za duży. Maksymalny rozmiar pliku to 1MB');
return;
}
this.uploading = true;
this.imgShow = false;
this.imgEmpty = false;
const formData = new FormData();
formData.append("file", file);
const response = await this.UploadImage(formData);
console.log(response.status);
if(response.status === 200) {
if(response.url) {
this.imageUrl = response.url;
this.timestamp = Date.now();
this.imgShow = true;
}
else {
this.imgFail = true;
}
}
else {
this.imgFall = true;
}
this.uploading = false;
},
async DeleteImageUpload(data) {
this.imgShow = false;
this.imgEmpty = true;
this.imageUrl = null;
await this.DeleteImage({id: data});
},
async AddAnime() {
const data = {
title: this.title,
group: this.group,
episodes: this.episodes,
url: this.url,
description: this.description
};
const response = await this.AddAnimeToDataBase(data);
if(response.complete) {
this.reloadTable();
this.CloseModal();
}
},
ApplyAndClose() {
this.reloadTable();
this.CloseModal();
}
}
}
</script>

View file

@ -0,0 +1,152 @@
<template lang="html">
<div class="md-body d-flex justify-content-center align-items-center">
<div class="md-content d-flex flex-column align-content-between">
<div class="md-header">
<h5>Skasuj</h5>
<div class="bt-md-exit" @click="CloseModal">
<svg width="32" height="32" fill="currentColor" class="bi bi-database-fill-add">
<use xlink:href="#ico-cross"/>
</svg>
</div>
</div>
<div class="md-body-content-del p-4">
<div class="md-delete-list-box default-border">
<div class="md-delete-list-content" v-if="groupVisible">
<h3>Grupy:</h3>
<ul>
<li v-for="item in groupDeleteData" :key="item.title">{{ item.title }}</li>
</ul>
</div>
<div class="md-delete-list-content" v-if="animeVisible">
<h3>Anime:</h3>
<ul>
<li v-for="item in animeDeleteData" :key="item.title">{{ item.title }}</li>
</ul>
</div>
</div>
<div class="md-delete-info-span">
<span class="md-span-item">Elementów do usunięcia: {{ allDeleteElements }}</span>
<span class="md-span-item" v-if="groupVisible">Grupy: {{ groupDeleteElements }}</span>
<span class="md-span-item" v-if="animeVisible">Anime: {{ animeDeleteElements }}</span>
</div>
</div>
<div class="md-footer d-flex flex-row justify-content-end p-4">
<button type="button" class="bt bt-normal" @click="CloseModal">Zamknij</button>
<button type="button" class="bt bt-danger bt-space" @click="StartDeleteProcess">Skasuj</button>
</div>
</div>
</div>
</template>
<script>
import { useAnimeStore } from '@/stores/anime';
import { useModeratedStore } from '@/stores/moderated';
import '@/assets/css/modal.css';
import { ref } from 'vue';
export default {
name: 'ModalDeleteWindow',
data() {
return {
groupDeleteData: [],
animeDeleteData: [],
allDeleteElements: 0,
groupDeleteElements: 0,
animeDeleteElements: 0,
groupVisible: false,
animeVisible: false,
}
},
setup() {
const animeStore = useAnimeStore();
const moderated = useModeratedStore();
const groupIndex = ref(moderated.checkIdGroupStore);
const animeIndex = ref(moderated.checkIdAnimeStore);
const group = ref(animeStore.group_storage);
const anime = ref(animeStore.animes_storage);
return {
DeleteRecords: animeStore.DeleteRecords,
groupIndex, animeIndex, group, anime
}
},
mounted() {
this.groupDeleteData = this.GetGroupTitlesFromStore();
this.animeDeleteData = this.GetAnimeTitlesFromStore();
this.groupDeleteElements = this.groupDeleteData.length;
this.animeDeleteElements = this.animeDeleteData.length;
this.allDeleteElements = this.groupDeleteElements + this.animeDeleteElements;
this.groupVisible = this.groupDeleteElements > 0 ? true : false;
this.animeVisible = this.animeDeleteElements > 0 ? true : false;
},
inject: ['reloadTable'],
methods: {
CloseModal() {
this.$emit('closeModal');
},
GetGroupTitlesFromStore() {
let tmp = [];
if(this.groupIndex.length > 0) {
this.groupIndex.forEach(index => {
this.group.forEach(element => {
if(index == element.gr_id) {
tmp.push({title: element.gr_title});
}
});
});
}
return tmp;
},
GetAnimeTitlesFromStore() {
let tmp = [];
if(this.animeIndex.length > 0) {
this.animeIndex.forEach(index => {
this.anime.forEach(element => {
if(index == element.id) {
tmp.push({title: element.title});
}
});
});
}
return tmp;
},
async StartDeleteProcess() {
const data = {
group: this.groupIndex,
anime: this.animeIndex
};
const response = await this.DeleteRecords(data);
if(response.complete) {
this.reloadTable();
this.CloseModal();
}
}
}
}
</script>

View file

@ -0,0 +1,245 @@
<template lang="html">
<div class="md-body d-flex justify-content-center align-items-center">
<div class="md-content d-flex flex-column align-content-between">
<div class="md-header">
<h5>Edytuj animu</h5>
<div class="bt-md-exit" @click="CloseModal">
<svg width="32" height="32" fill="currentColor" class="bi bi-database-fill-add">
<use xlink:href="#ico-cross"/>
</svg>
</div>
</div>
<div class="md-body-content d-flex align-items-stretch p-4">
<div class="img-md-box">
<div class="img-file-upload-box">
<label for="img-input-form" style="cursor: pointer;">
<div class="img-placeholder" v-if="!imgShow">
<div class="waiting-on-click-to-upload-image" v-if="imgEmpty">
<img src="../../assets/img/upload_img.svg" width="64" height="64">
<p style=" user-select: none;">Załaduj obraz</p>
</div>
<LoadingWheel width="64" height="64" v-if="uploading" />
<img src="../../assets/img/fail_img.svg" width="64" height="64" v-if="imgFall">
</div>
<img :src="imageContent" alt="" width="200" height="285" v-if="imgShow">
</label>
<input type="file" @change="HandleFileUpload" id="img-input-form" style="display: none;" accept="image/*" maxlength="1">
</div>
<button class="bt bt-danger" style="margin-top: 8px;" @click="DeleteImageUpload">Skasuj obraz</button>
</div>
<div class="content-md-box">
<div class="md-data-box">
<div class="md-form-box">
<label for="inp-title">Tytuł</label>
<input type="text" class="in in-default lb-title m-1" id="inp-title" v-model="title">
</div>
<div class="md-ep-x-gr-box">
<div class="md-form-box">
<label for="inp-episodes">Odcinki</label>
<input type="number" class="in in-default lb-episodes m-1" id="inp-episodes" v-model="episodes" pattern="\d*" oninput="this.value = this.value.replace(/[^0-9]/g, '')">
</div>
<div class="md-form-box">
<label for="slc-group">Grupa</label>
<select class="md-select-group-menu sl-default" id="slc-group" v-model="group">
<option value=0>Pozostałe</option>
<option v-for="option in groups" :key="option.gr_id" :value="option.gr_id">
{{ option.gr_title }}
</option>
</select>
</div>
</div>
<div class="md-form-box">
<label for="inp-url">Link</label>
<input type="text" class="in in-default lb-title m-1" id="inp-url" v-model="url">
</div>
<div class="md-form-box">
<label for="txa-description">Opis</label>
<textarea class="md-description-textbox tx tx-default" id="txa-description" v-model="description"></textarea>
</div>
</div>
</div>
</div>
<div class="md-footer d-flex flex-row justify-content-end p-4">
<button type="button" class="bt bt-normal" @click="CloseModal">Zamknij</button>
<button type="button" class="bt bt-accept bt-space" @click="EditAnime">Edytuj</button>
</div>
</div>
</div>
</template>
<script>
import { useAnimeStore } from '@/stores/anime';
import { useModeratedStore } from '@/stores/moderated';
import LoadingWheel from '../ui/loading.vue';
import { ref } from 'vue';
import '@/assets/css/modal.css';
export default {
name: 'ModalEditWindow',
components: {
LoadingWheel,
},
data() {
return {
//Dane animu do przesłania
id: 0,
title: '',
group: 0,
episodes: 1,
url: '',
description: '',
//obrazek
imageContent: '',
//UPLOAD
uploading: false,
//image delete record
removeImage: false,
newImage: false,
//Stany uploadowanej grafiki
imgShow: false,
imgEmpty: true,
imgFall: false,
}
},
setup() {
const animeStore = useAnimeStore();
const moderated = useModeratedStore();
const checkIdAnimeStore = ref(moderated.checkIdAnimeStore);
const GetAnimeForEditModal = ref(animeStore.GetAnimeForEditModal);
const groups = ref(animeStore.group_storage);
return {
groups,
UploadImage: animeStore.UploadImage,
DeleteImage: animeStore.DeleteImage,
UpdateAnimeInDataBase: animeStore.UpdateAnimeInDataBase,
checkIdAnimeStore,
GetAnimeForEditModal,
};
},
computed: {
//validIsNumber() {
// let tmp;
//}
},
inject: ['reloadTable'],
async mounted() {
const id = this.checkIdAnimeStore[0];
const response = await this.GetAnimeForEditModal(id);
this.title = response.title;
this.group = response.group;
this.episodes = response.episodes;
this.url = response.url;
this.description = response.description;
//Wyświetlenie obrazu
if(response.image) {
this.imgEmpty = false;
this.imgShow = true;
this.imageContent = response.image;
}
},
methods: {
CloseModal() {
this.$emit('closeModal');
},
async HandleFileUpload(event) {
const file = event.target.files[0];
if (!file) {
this.error = "No file selected.";
return;
}
this.uploading = true;
this.imgShow = false;
this.imgEmpty = false;
this.newImage = false;
this.removeImage = false;
const formData = new FormData();
formData.append("file", file);
const response = await this.UploadImage(formData);
console.log(response.status);
if(response.status === 200) {
if(response.url) {
this.imageContent = response.url;
this.imgShow = true;
this.newImage = true;
}
else {
this.imgFail = true;
}
}
else {
this.imgFall = true;
}
this.uploading = false;
},
async DeleteImageUpload(data) {
this.imgShow = false;
this.imgEmpty = true;
this.imageUrl = null;
this.removeImage = true;
await this.DeleteImage({id: data});
},
async EditAnime() {
const data = {
id: this.checkIdAnimeStore[0],
title: this.title,
group: this.group,
episodes: this.episodes,
url: this.url,
description: this.description,
removeImage: this.removeImage,
newImage: this.newImage
};
const response = await this.UpdateAnimeInDataBase(data);
if(response.complete) {
this.reloadTable();
this.CloseModal();
}
},
ApplyAndClose() {
this.reloadTable();
this.CloseModal();
}
},
}
</script>

View file

@ -0,0 +1,105 @@
<template lang="html">
<div class="md-body d-flex justify-content-center align-items-center">
<div class="md-content d-flex flex-column align-content-between">
<div class="md-header">
<h5>Edytuj grupę</h5>
<div class="bt-md-exit" @click="CloseModal">
<svg width="32" height="32" fill="currentColor" class="bi bi-database-fill-add">
<use xlink:href="#ico-cross"/>
</svg>
</div>
</div>
<div class="md-body-content-gr p-4">
<label for="inp-title">Nazwa grupy</label>
<input
type="text"
class="in in-default m-1"
style="width: 100%; height: 36px;"
v-model="groupTitle"
:maxlenght="maxGlyph"
@input="handleInput"
/>
<div class="smol-text-content"><span class="smol-text-lenght-glyph">Pozostało: {{ LenghtGlyph }}</span></div>
</div>
<div class="md-footer d-flex flex-row justify-content-end p-4">
<button type="button" class="bt bt-normal" @click="CloseModal">Zamknij</button>
<button type="button" class="bt bt-accept bt-space" @click="EditRecord">Edytuj</button>
</div>
</div>
</div>
</template>
<script>
import { useAnimeStore } from '@/stores/anime';
import { useModeratedStore } from '@/stores/moderated';
import { ref } from 'vue';
import '@/assets/css/modal.css';
export default {
name: 'ModalEditGroupWindow',
data() {
return {
//Zmienna nazwy
groupTitle: '',
groupId: 0,
//limit literków
maxGlyph: 64,
tmpString: '',
}
},
setup() {
const animeStore = useAnimeStore();
const moderated = useModeratedStore();
const checkIdGroupStore = ref(moderated.checkIdGroupStore);
return {
GetGroupForEditModal: animeStore.GetGroupForEditModal,
EditGroupRecord: animeStore.EditGroupRecord,
checkIdGroupStore,
}
},
computed: {
LenghtGlyph() {
return this.maxGlyph - this.groupTitle.length;
}
},
mounted() {
this.groupId = this.checkIdGroupStore[0];
this.groupTitle = this.GetGroupForEditModal(this.groupId);
},
inject: ['reloadTable'],
methods: {
CloseModal() {
this.$emit('closeModal');
},
async EditRecord() {
const response = await this.EditGroupRecord({
gid: this.groupId,
title: this.groupTitle
});
if(response){
this.reloadTable();
this.CloseModal();
}
},
handleInput() {
if (this.groupTitle.length > this.maxGlyph) {
this.groupTitle = this.groupTitle.slice(0, this.maxGlyph);
}
}
}
}
</script>

View file

@ -0,0 +1,143 @@
<template lang="html">
<div class="md-body d-flex justify-content-center align-items-center">
<div class="md-content d-flex flex-column align-content-between">
<div class="md-header">
<h5>Zarządzaj Grupami</h5>
<div class="bt-md-exit" @click="CloseModal">
<svg width="32" height="32" fill="currentColor" class="bi bi-database-fill-add">
<use xlink:href="#ico-cross"/>
</svg>
</div>
</div>
<div class="md-body-content-gr p-4">
<div class="md-inp-box-content-gr">
<label for="inp-title">Nazwa nowej grupy</label>
<div class="md-inp-box-input-content-gr">
<input
type="text"
class="in in-default m-1"
style="width: 100%; height: 36px;"
v-model="groupTitle"
:maxlenght="maxGlyph"
@input="handleInput"
/>
<button type="button" class="bt-circle bt-accept bt-space" @click="Add" :disabled="AddBtDisabler">
<svg width="16" height="16" fill="currentColor" class="bi bi-database-fill-add">
<use xlink:href="#ico-add"/>
</svg>
</button>
</div>
<div class="smol-text-content"><span class="smol-text-lenght-glyph">Pozostało: {{ LenghtGlyph }}</span></div>
</div>
<div class="md-group-list-box default-border">
<MdGroupRow v-for="(content, index) in groupData" :key="index" :data="content" />
</div>
<div class="md-delete-info-span">
<span class="md-span-item">Zaznaczone elementy: {{ LenghtElements }}</span>
</div>
</div>
<div class="md-footer d-flex flex-row justify-content-end p-4">
<button type="button" class="bt bt-normal" @click="CloseModal">Zamknij</button>
</div>
</div>
</div>
</template>
<script>
import { useGroupStore } from '@/stores/group';
import { useAuterizationStore } from '@/stores/auterization';
import { ref, watch } from 'vue';
import MdGroupRow from '@/components/modals/group_row.vue';
import '@/assets/css/modal.css';
export default {
name: 'ModalAddGroupWindow',
components: {
MdGroupRow,
},
data() {
return {
//Zmienna nazwy
groupTitle: '',
//limit literków
maxGlyph: 64,
minGlyph: 3,
tmpString: '',
}
},
setup() {
const auterization = useAuterizationStore();
const userPreference = ref(auterization.userPreference);
const groupStore = useGroupStore();
const groupData = ref(groupStore.data);
const sellectList = ref(groupStore.list);
watch (
() => groupStore.data, (newValue) => {
groupData.value = newValue;
},
() => groupStore.list, (newValue) => {
sellectList.value = newValue;
}
);
return {
groupData, sellectList,
userPreference,
GetRecords: groupStore.Get,
AddRecord: groupStore.Add,
EditRecord: groupStore.Edit,
DeleteRecord: groupStore.Delete,
}
},
computed: {
LenghtGlyph() {
return this.maxGlyph - this.groupTitle.length;
},
LenghtElements() {
return this.sellectList.length;
},
AddBtDisabler() {
return this.groupTitle.length >= this.minGlyph ? false : true;
}
},
async mounted() {
await this.GetGroupList()
},
inject: ['reloadTable'],
methods: {
CloseModal() {
this.$emit('closeModal');
},
async GetGroupList() {
await this.GetRecords()
},
async Add() {
await this.AddRecord({name: this.groupTitle});
},
handleInput() {
if (this.groupTitle.length > this.maxGlyph) {
this.groupTitle = this.groupTitle.slice(0, this.maxGlyph);
}
}
}
}
</script>

View file

@ -0,0 +1,54 @@
<template lang="html">
<div class="md-gr-row">
<div class="md-gr-cell-ch">
<input class="form-check-input" type="checkbox" v-model="checkActive" @change="CheckboxHandle"/>
</div>
<div class="md-gr-cell-text">
<div>{{ name }}</div>
</div>
</div>
</template>
<script>
import { useGroupStore } from '@/stores/group';
import '@/assets/css/modal.css';
export default {
name: 'MdGroupRow',
props: {
data: Object
},
data() {
return {
//Data
id: 0,
name: 'ROW NAME',
//Comp
checkActive: false,
}
},
setup() {
const groupStore = useGroupStore();
return {
AddToList: groupStore.AddToList,
RemoveFromList: groupStore.RemoveFromList,
}
},
mounted() {
this.id = this.data.gr_id;
this.name = this.data.gr_title;
},
methods: {
CheckboxHandle() {
if (this.checkActive) {
this.AddToList(this.id);
}
else {
this.RemoveFromList(this.id);
}
}
}
}
</script>

View file

@ -0,0 +1,13 @@
<template lang="html">
<div>
Baza danych nie zije :c
</div>
</template>
<script>
export default {
name: 'DataMissing',
}
</script>
<style lang="">
</style>

View file

@ -0,0 +1,345 @@
<template lang="html">
<tr class="anime-row-container">
<th scope="row">
<div class="ch-container">
<input class="form-check-input" type="checkbox" v-model="isChecked" @change="CheckboxHandle"/>
</div>
</th>
<td>
<img :src="animeImg" alt="NO-IMG" class="rounded">
</td>
<td>
<div class="title-container">
<div class="title-box">
<div class="anime-title-box">
<strong class="anime-title" :class="scroolTitleClass" @click="ShowDescryption()">{{ animeTitle }}</strong>
</div>
<small class="anime-add-record-date">{{ animeDate }}</small>
</div>
<div class="link-box">
<button class="bt-circle bt-normal" @click="openInNewTab" :disabled="btDisabledStatus">
<svg width="20" height="20" fill="currentColor" class="bi bi-database-fill-add">
<use xlink:href="#ico-hyperlink"/>
</svg>
</button>
</div>
</div>
</td>
<td>
<div class="anm-epst-container">
<div class="anm-epbt-container">
<div class="dbt" type="button" @click="SubstractEpisode()">
<svg width="16" height="16" fill="currentColor" class="bi bi-database-fill-add">
<use :xlink:href="backEpBt"/>
</svg>
</div>
<div class="txt-ep-nuber-view">{{ epCount }}</div>
<div class="dbt" type="button" @click="AddEpisode()">
<svg width="16" height="16" fill="currentColor" class="bi bi-database-fill-add">
<use :xlink:href="nextEpBt"/>
</svg>
</div>
</div>
<div class="anm-stsl-container">
<div class="watch-state-selector" :class="stateMenuRadius" @click="ToggleStateMenu" ref="rfStateMenu">
<div class="watch-txt-content">
<svg width="16" height="16" fill="currentColor" :class="stateColor">
<use :xlink:href="stateIcon"/>
</svg>
<div class="state-select-txt">{{ stateName }}</div>
</div>
<svg width="16" height="16" fill="currentColor" class="bi bi-database-fill-add">
<use xlink:href="#ico-down"/>
</svg>
</div>
<div class="watch-state-hover-menu" v-show="isVisible">
<div class="state-select" @click="SetStateOnMenu(0)">
<div class="state-select-content">
<svg width="16" height="16" fill="currentColor" class="svg-nowatched">
<use xlink:href="#ico-nowatch"/>
</svg>
<div class="state-select-txt">Planuję</div>
</div>
</div>
<div class="state-select" @click="SetStateOnMenu(1)">
<div class="state-select-content">
<svg width="16" height="16" fill="currentColor" class="svg-watch">
<use xlink:href="#ico-watch"/>
</svg>
<div class="state-select-txt">Oglądam</div>
</div>
</div>
<div class="state-select" @click="SetStateOnMenu(2)">
<div class="state-select-content">
<svg width="16" height="16" fill="currentColor" class="svg-watched">
<use xlink:href="#ico-watched"/>
</svg>
<div class="state-select-txt">Obejrzane</div>
</div>
</div>
<div class="state-select" @click="SetStateOnMenu(3)">
<div class="state-select-content">
<svg width="16" height="16" fill="currentColor" class="svg-canceled">
<use xlink:href="#ico-canceled"/>
</svg>
<div class="state-select-txt">Porzucone</div>
</div>
</div>
</div>
</div>
</div>
</td>
</tr>
<template v-if="desActive">
<RowDescription :data="desData" @desRow.once="CloseDescryption"/>
</template>
</template>
<script>
import { useAnimeStore } from '@/stores/anime';
import { useModeratedStore } from '@/stores/moderated';
import RowDescription from './rowinfo.vue';
export default {
name: 'DataRow',
components: {
RowDescription
},
props: {
data: Object
},
data() {
return {
epCount: 1, //Aktualny nr odcinka
mainState: 2,
checkActive: false,
desActive: false, //Wyświetla opis
desData: {},
stateName: 'Planuję',
stateIcon: '#ico-nowatch',
stateColor: 'svg-nowatched',
//Przewijanie epizodów
nextEpBt: '#ico-next',
backEpBt: '#ico-back',
arrEpNext: ['#ico-next', '#ico-next-border'],
arrEpBack: ['#ico-back', '#ico-back-border'],
//menu border
stateMenuRadius: 'watch-state-selector-radius-hidden-menu',
stateRadius: ['watch-state-selector-radius-hidden-menu', 'watch-state-selector-radius-visible-menu'],
//Tablica Ustawień statusów
arrName: ['Planuję', 'Oglądam', 'Obejrzane', 'Porzucone'],
arrSvgIcon: ['#ico-nowatch', '#ico-watch', '#ico-watched', '#ico-canceled'],
arrSvgColor: ['svg-nowatched', 'svg-watch', 'svg-watched', 'svg-canceled'],
//Skollowanie kiedy za długi tytuł
arrScroolTitle: ['', 'anime-title-scrolling'],
scroolTitleClass: '',
//menu statusu
isVisible: false,
//checkbox status
isChecked: false,
//button aktywny
btDisabledStatus: true,
//stałe
animeId: 1, //ID klucza rekordu
animeEpisodes: 12, //Liczba odcinków
animeTitle: 'Hi mom!', //Tytuł anime
animeDate: '1970-01-01', //Data dodania do bazydanych
animeLink: 'https://example.com', //Link do odcinka
animeState: 3, //Status oglądania 0-Nie obejżane, 1-Oglądam, 2-Obejżane, 3-Porzucone
animeImg: require('@/assets/img/no_img_min.jpg'), //Link do obrazka
//adress do aktualizacji rekordu
updateUrl: process.env.VUE_APP_UPDATE_RECORD
}
},
setup() {
const animeStore = useAnimeStore();
const moderated = useModeratedStore();
return {
UpdateStatus: animeStore.UpdateStatus,
GetDescription: animeStore.GetDescription,
AddIdToStore: moderated.AddIdToStore,
DeleteIdFromStore: moderated.DeleteIdFromStore,
};
},
mounted() {
document.addEventListener('click', this.handleClickOutside);
//Ustawianie danych
this.animeId = this.data.id;
this.animeTitle = this.data.title;
this.animeDate = this.data.date;
this.animeLink = this.data.url;
this.animeEpisodes = this.data.episodes;
if(this.data.img) {
this.animeImg = this.data.img;
}
this.SetState(this.data.fav.status);
this.SetEpisode(this.data.fav.episode);
if (this.data.url) {
this.UrlButtonToggle()
}
},
unmounted() {
//stałe
this.animeId = 1; //ID klucza rekordu
this.animeEpisodes = 12; //Liczba odcinków
this.animeTitle = 'Hi mom!'; //Tytuł anime
this.animeDate = '1970-01-01'; //Data dodania do bazydanych
this.animeLink = 'https://example.com'; //Link do odcinka
this.animeState = 3; //Status oglądania 0-Nie obejżane, 1-Oglądam, 2-Obejżane, 3-Porzucone
this.animeImg = require('@/assets/img/no_img_min.jpg'); //Link do obrazka
},
beforeMount() {
document.removeEventListener('click', this.handleClickOutside);
},
methods: {
SubstractEpisode(){
if (this.epCount > 1){
this.epCount--;
this.ArrowSvgToggler();
this.UpdateStatus(this.animeId, {episode: this.epCount});
}
else {
this.epCount = 1;
}
console.log('STATUS: ' + this.epCount);
},
AddEpisode(){
if (this.epCount < this.animeEpisodes){
this.epCount++;
this.ArrowSvgToggler();
this.UpdateStatus(this.animeId, {episode: this.epCount});
}
else {
this.epCount = this.animeEpisodes;
}
console.log('STATUS: ' + this.epCount);
},
SetEpisode(ep){
this.epCount = ep;
this.ArrowSvgToggler();
},
ArrowSvgToggler() {
this.nextEpBt = this.epCount == this.animeEpisodes ? this.arrEpNext[1] : this.arrEpNext[0];
this.backEpBt = this.epCount == 1 ? this.arrEpBack[1] : this.arrEpBack[0];
},
//Status oglądania
ToggleStateMenu(){
this.isVisible = !this.isVisible;
this.stateMenuRadius = this.stateRadius[Number(this.isVisible)];
},
SetState(st){
this.stateName = this.arrName[st];
this.stateIcon = this.arrSvgIcon[st];
this.stateColor = this.arrSvgColor[st];
this.animeState = st;
},
SetStateOnMenu(st){
this.SetState(st);
this.ToggleStateMenu();
console.log('STATUS: ' + st);
this.UpdateStatus(this.animeId, {status: st});
},
//Zamknij menu
handleClickOutside(event){
if(this.isVisible && this.$refs.rfStateMenu && !this.$refs.rfStateMenu.contains(event.target)) {
this.isVisible = false;
this.stateMenuRadius = this.stateRadius[0];
}
},
async ShowDescryption(){
//this.desActive = !this.desActive;
if(!this.desActive){
this.desData = await this.GetDescription(this.animeId);
if(this.desData) {
this.desActive = true;
}
else {
console.log('NIE UDAŁO SIĘ POBRAĆ DANYCH DESCRIPTION :C');
}
}
else {
this.CloseDescryption();
}
},
CloseDescryption(){
this.desActive = false;
},
CheckboxHandle() {
console.log(`Checkbox: ${this.isChecked}`);
if(this.isChecked) {
console.log(`Dodano index: ${this.animeId}`);
this.AddIdToStore(this.animeId);
}
else {
console.log(`Usunięto index: ${this.animeId}`);
this.DeleteIdFromStore(this.animeId);
}
},
openInNewTab() {
window.open(this.animeLink, '_blank');
},
UrlButtonToggle() {
this.btDisabledStatus = !this.btDisabledStatus;
}
}
}
</script>

View file

@ -0,0 +1,68 @@
<template lang="html">
<tr><td colspan="4">
<div class="anime-descryption">
<div class="anime-descryption-header">
<div class="hide-descryption" @click="ClosteRowInfo">
<svg width="16" height="16" fill="currentColor" class="bi bi-database-fill-add">
<use xlink:href="#ico-up"/>
</svg>
</div>
</div>
<div class="anime-descryption-content">
<div class="img-containcer">
<div class="m-1 d-flex justify-content-center d-flex align-items-start">
<img :src="animeImg" alt="NO-IMG" class="rounded" width="200" height="285">
</div>
</div>
<div class="descryption-containcer">
<div class="title-box">
<strong class="title-string">{{ animeTitle }}</strong>
</div>
<div class="descryption-box">
<div class="descryption-string">
{{ animeDescription }}
</div>
</div>
</div>
</div>
</div>
</td></tr>
</template>
<script>
export default {
name: 'RowDescription',
props: {
data: Object
},
data() {
return {
//Główne zmienne
animeTitle: 'Hi Animu Mommy',
animeDescription: 'Lorem Ipsum jest tekstem stosowanym jako przykładowy wypełniacz w przemyśle poligraficznym. Został po raz pierwszy użyty w XV w. przez nieznanego drukarza do wypełnienia tekstem próbnej książki. Pięć wieków później zaczął być używany przemyśle elektronicznym, pozostając praktycznie niezmienionym. Spopularyzował się w latach 60. XX w. wraz z publikacją arkuszy Letrasetu, zawierających fragmenty Lorem Ipsum, a ostatnio z zawierającym różne wersje Lorem Ipsum oprogramowaniem przeznaczonym do realizacji druków na komputerach osobistych, jak Aldus PageMaker',
animeImg: require('@/assets/img/no_img.jpg'),
}
},
mounted() {
this.animeTitle = this.data.title;
this.animeDescription = this.data.description;
if(this.data.img) {
this.animeImg = this.data.img;
}
},
methods: {
ClosteRowInfo(){
console.log("HIDE DES");
this.$emit('desRow');
}
}
}
</script>

View file

@ -0,0 +1,63 @@
<template lang="html">
<tr>
<td>
<div class="ch-separator">
<input class="form-check-input" type="checkbox" id="flexCheckDefault" v-model="isChecked" @change="CheckboxHandle">
</div>
</td>
<td colspan="3">
<div class="row-separator">
<div class="separate-string">{{ groupTitle }}</div>
</div>
</td></tr>
</template>
<script>
import { useModeratedStore } from '@/stores/moderated';
export default {
name: 'RowSeparator',
props: {
grData: Object
},
data() {
return {
isChecked: false,
groupTitle: '',
groupId: 0,
}
},
mounted() {
this.groupId = this.grData.grId;
this.groupTitle = this.grData.grTitle;
},
setup() {
const moderated = useModeratedStore();
return {
AddIdToStore: moderated.AddIdToStore,
DeleteIdFromStore: moderated.DeleteIdFromStore,
};
},
methods: {
CheckboxHandle() {
console.log(`Checkbox: ${this.isChecked}`);
if(this.isChecked) {
console.log(`Dodano index: ${this.groupId}`);
this.AddIdToStore(this.groupId, true);
}
else {
console.log(`Usunięto index: ${this.groupId}`);
this.DeleteIdFromStore(this.groupId, true);
}
}
}
}
</script>

View file

@ -0,0 +1,286 @@
<template lang="html">
<div class="anime-table">
<div class="table-header-menu">
<div class="filtr-state-selector">
<svg width="32" height="32" fill="currentColor" style="margin-right: 16px;">
<use xlink:href="#ico-funnel"></use>
</svg>
<!--Filtr statusu-->
<select class="sl-default" @change="GetAnimeSort()" v-model="filterTable">
<option value="-1">Wszystkie</option>
<option value="0">Nie obejżane</option>
<option value="1">Oglądane</option>
<option value="2">Obejżane</option>
<option value="3">Pożucone</option>
</select>
<!--Filtr grupy-->
<select class="sl-default" style="margin-left: 16px;" v-model="groupTable" @change="GetAnimeSort()">
<option value="0">Full DB</option>
<option v-for="(content, index) in rawGroup" :key="index" :value="content.gr_id">{{ content.gr_title }}</option>
</select>
</div>
<!--Wyszukiwarka-->
<div class="search-input">
<input type="text" class="in in-default" placeholder="Search..." aria-label="Search..." aria-describedby="basic-addon2" style="width: 85%;" v-model="searchString" @change="SearchRecord">
<button class="bt-circle bt-normal" type="button" style="width: 40px; height: 40px;" @click="SearchRecord">
<svg width="16" height="16" fill="currentColor" class="bi bi-database-fill-add">
<use xlink:href="#ico-search"/>
</svg>
</button>
</div>
</div>
<table class="table table-dark">
<thead>
<tr>
<th scope="col" class="ch-column">#</th>
<th scope="col" class="img-column">img</th>
<th scope="col" class="title-column">Tytuł</th>
<th scope="col" class="ep-column">Odcinki</th>
</tr>
</thead>
<!--Główna tabela-->
<tbody>
<template v-if="animeTableContent">
<DataRow v-for="(content, index) in rawAnime" :key="index" :data="content" />
</template>
</tbody>
</table>
</div>
</template>
<script>
import { useAnimeStore } from '@/stores/anime';
import { useAuterizationStore } from '@/stores/auterization';
import { useModeratedStore } from '@/stores/moderated';
import { ref } from 'vue';
import { nextTick } from 'vue';
import DataRow from './row.vue';
export default {
name: 'DataTable',
components: {
DataRow,
},
data() {
return{
animeData: [],
animeTableContent: true,
rawGroup: [],
rawAnime: [],
animeStorage: [],
groupStorage: [],
filterTable: -1,
groupTable: 0,
searchString: "",
}
},
inject: ['setReloadTable'],
setup() {
const animeStore = useAnimeStore();
const auterization = useAuterizationStore();
const moderated = useModeratedStore();
const isLogged = ref(auterization.isLogged);
const userPreference = ref(auterization.userPreference);
//const animeData = ref([]);
return {
GetAnime: animeStore.GetAnime,
GetFilteringData: animeStore.GetFilteringData,
GetSearchData: animeStore.GetSearchData,
GetAnimeContent: animeStore.GetAnimeContent,
isLogged,
userPreference,
//animeData,
UpdateUserPref: auterization.UpdateUserPref,
ResetIndex: moderated.ResetIndex,
}
},
mounted() {
//Filer
if(this.userPreference?.filter) {
this.filterTable = this.userPreference.filter;
}
else {
this.filterTable = -1;
}
if(this.isLogged) {
this.LoadData();
}
else {
console.error('Nie jesteś zalogowany! Brak danych do załadowania');
this.$router.push('/login');
}
this.setReloadTable(this.ReloadData);
},
methods: {
async LoadData(){
const data = await this.GetAnime(this.filterTable);
if (data) {
this.rawAnime = data.anime;
this.rawGroup = data.group;
console.log(this.rawGroup);
//this.animeData = this.SerializedData(this.rawGroup, this.rawAnime);
}
},
//Przeładowuje dane
async ReloadData() {
this.animeTableContent = false;
this.ResetIndex();
await this.LoadData();
this.animeTableContent = true;
},
//Pobierz posortowaną listę na postawie
async GetAnimeSort() {
this.animeTableContent = false;
const sort = {
"group": this.groupTable,
"status": this.filterTable,
};
console.log(sort);
this.rawAnime = await this.GetAnimeContent(sort);
this.animeTableContent = true;
},
//Zmiana wyświetlania kontentu
//Dopasowywanie statusu oglądania do filtra
async ChangeFiltr() {
this.animeTableContent = false;
const data = {filter: this.filterTable};
await this.UpdateUserPref(data);
//this.animeData.value = this.SerializedData(this.rawGroup, this.rawAnime);
this.animeTableContent = true;
},
//Funkcja wyszukiwania
async SearchRecord() {
this.animeTableContent = false;
this.animeData = [];
//this.animeData = this.SerializedData(this.rawGroup, this.rawAnime);
//Nic to nie daje. Jest to tylko po to bo tabela
//działa jak jest operacja async XD
await nextTick();
this.animeTableContent = true;
},
//Jebany potwór do segregacji danych
//Działa jako tako
SerializedData(group, anime) {
let tmpGroup = [...group];
tmpGroup.push({
gr_id: 0,
gr_title: 'Pozostałe',
});
let data = [];
let tmpAnime = [];
//Dziwadło do wyszukiwania
//Sprawdza czy podana fraza do szukania co kolwiek zawiera
if (this.searchString.length > 0) {
//konwertoruje wpisaną frazę na małe litery
const ssf = this.searchString.toLocaleLowerCase();
//Pętla szukająca
anime.forEach(element => {
//Konwertoruje tytuł anime na małe litery
const anmssf = element.title.toLocaleLowerCase();
//Sprawdza czy podana fraza istnieje
//Pobiera z nazwy anime tyle liter jak długa jest wpisana fraza
if (ssf === anmssf.substring(0, ssf.length)) {
tmpAnime.push(element);
}
});
}
else {
//Jak nie wyszuka to ma zalinkować faktyczne dane
tmpAnime = anime;
}
//Tu następuje magia segregacji
//What the fuck?
//Najpierw pętla wybiera pojedynczo grupę
tmpGroup.forEach(grp => {
//Tymczasowa zmienna dla tabeli anime;
let tmp = [];
//Tu pętla pobiera pojedyczo anime do sprawdzenia
tmpAnime.forEach(anm => {
//Sprawdza status filtra tabeli
//Filtr poniżej 0 nie sprawdza statusu oglądania
if (this.filterTable > -1) {
//Pobiera id grupy z anime i porównuje z id grupy
//Sprawdza status oglądania i porównuje z ustawionym filtrem
if(grp.gr_id == anm.group && anm.fav.status == this.filterTable) {
tmp.push(anm);
}
}
//Sprawdza tylko grupy bez statusu oglądania
else {
if (grp.gr_id == anm.group) {
tmp.push(anm);
}
}
});
//Filter -2 dodaje puste grupy do wyświetlenia
if (this.filterTable > -2) {
if(tmp.length > 0) {
data.push({
gid: grp.gr_id,
gtitle: grp.gr_title,
anime: tmp
});
}
}
//Dodaje tylko grupy które zawierają rekordy anime
else {
if(tmp.length > 0) {
data.push({
gid: grp.gr_id,
gtitle: grp.gr_title,
anime: tmp
});
}
}
});
return data;
}
}
}
</script>
<style lang="css">
</style>

View file

@ -0,0 +1,13 @@
<template lang="html">
<div>
</div>
</template>
<script>
export default {
}
</script>
<style lang="css">
</style>

View file

@ -0,0 +1,20 @@
<template lang="html">
<img src="../../assets/img/loading.svg" class="ui-loading-animation">
</template>
<script>
export default {
name: 'LoadingWheel'
}
</script>
<style lang="css">
.ui-loading-animation {
animation: loading 1s steps(8) infinite;
}
@keyframes loading {
0% {rotate: 0deg;}
100% {rotate: 360deg;}
}
</style>

View file

@ -0,0 +1,79 @@
<template lang="html">
<div class="mess-window" :class="cssMess">
<div class="bt-mess-exit" @click="CloseMessWindow">
<svg width="32" height="32" fill="currentColor" class="bi bi-database-fill-add">
<use xlink:href="#ico-cross"/>
</svg>
</div>
<div class="mess-body">
<svg width="24" height="24" fill="currentColor" viewBox="0 0 16 16" style="margin-right: 12px;">
<path v-if="goodVis" d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0m-3.97-3.03a.75.75 0 0 0-1.08.022L7.477 9.417 5.384 7.323a.75.75 0 0 0-1.06 1.06L6.97 11.03a.75.75 0 0 0 1.079-.02l3.992-4.99a.75.75 0 0 0-.01-1.05z"/>
<path v-if="badVis" d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0M5.354 4.646a.5.5 0 1 0-.708.708L7.293 8l-2.647 2.646a.5.5 0 0 0 .708.708L8 8.707l2.646 2.647a.5.5 0 0 0 .708-.708L8.707 8l2.647-2.646a.5.5 0 0 0-.708-.708L8 7.293z"/>
<path v-if="warnVis" d="M8.982 1.566a1.13 1.13 0 0 0-1.96 0L.165 13.233c-.457.778.091 1.767.98 1.767h13.713c.889 0 1.438-.99.98-1.767zM8 5c.535 0 .954.462.9.995l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 5.995A.905.905 0 0 1 8 5m.002 6a1 1 0 1 1 0 2 1 1 0 0 1 0-2"/>
<path v-if="infoVis" d="M2 6a6 6 0 1 1 10.174 4.31c-.203.196-.359.4-.453.619l-.762 1.769A.5.5 0 0 1 10.5 13h-5a.5.5 0 0 1-.46-.302l-.761-1.77a2 2 0 0 0-.453-.618A5.98 5.98 0 0 1 2 6m3 8.5a.5.5 0 0 1 .5-.5h5a.5.5 0 0 1 0 1l-.224.447a1 1 0 0 1-.894.553H6.618a1 1 0 0 1-.894-.553L5.5 15a.5.5 0 0 1-.5-.5"/>
</svg>
<div class="mess-txt">{{messText}}</div>
</div>
</div>
</template>
<script lang="js">
export default {
name: 'MessageWindow',
props: {
mess: Object
},
data() {
return {
goodVis: false,
badVis: false,
warnVis: false,
infoVis: false,
messText: "Sample Text",
cssMess: "mess-info",
}
},
mounted() {
this.messText = this.mess.content;
this.goodVis = false;
this.badVis = false;
this.warnVis = false;
this.infoVis = false;
switch(this.mess.type) {
case "MESS_OK":
this.cssMess = "mess-good";
this.goodVis = true;
break;
case "MESS_ERROR":
this.cssMess = "mess-bad";
this.badVis = true;
break;
case "MESS_WARNING":
this.cssMess = "mess-warn";
this.warnVis = true;
break;
default:
this.cssMess = "mess-info";
this.infoVis = true;
break;
}
},
methods: {
CloseMessWindow() {
console.log("Close 1");
this.$emit('messHide');
}
}
};
</script>

29
client_2.1/src/main.js Normal file
View file

@ -0,0 +1,29 @@
import { createApp } from 'vue';
import { createPinia } from 'pinia';
import piniaPluginPersistedState from 'pinia-plugin-persistedstate';
import router from './router';
import App from './App.vue';
import 'altcha';
//bootstrap
import 'bootstrap/dist/css/bootstrap.min.css';
import 'bootstrap/dist/js/bootstrap.bundle.min.js';
//Custem Styles
import './assets/css/fonts.css';
import './assets/css/colors.css';
import './assets/css/style.css';
import './assets/css/navimenu.css';
import './assets/css/table.css';
import './assets/css/footer.css';
import './stores/moderated.js'
const app = createApp(App);
const pinia = createPinia();
pinia.use(piniaPluginPersistedState);
app.use(pinia);
app.use(router);
app.mount('#app');

View file

@ -0,0 +1,41 @@
import { createRouter, createWebHistory } from 'vue-router';
import HomePage from '../views/Home.vue';
import LoginPage from '../views/Login.vue';
import AnimePage from '../views/Anime.vue';
import NotFound from '../views/Error404.vue';
import UserControlPage from '@/views/UserPanel.vue';
const routes = [
{
path: '/',
name: 'Home',
component: HomePage,
},
{
path: '/login',
name: 'Login',
component: LoginPage,
},
{
path: '/anime',
name: 'Anime',
component: AnimePage,
},
{
path: '/control-panel',
name: 'Control Panel',
component: UserControlPage,
},
{
path: '/:pathMatch(.*)*',
name: 'NotFound',
component: NotFound,
},
];
const router = createRouter({
history: createWebHistory(),
routes,
});
export default router;

View file

@ -0,0 +1,250 @@
import { defineStore } from 'pinia';
import axios from 'axios';
export const useAnimeStore = defineStore('Anime', {
state: () => ({
search: {},
group_storage: [],
animes_storage: [],
isLoaded: false,
filter: -2,
//Wyszukiwarka
searchStringFilter: "",
//adres do validacji
serverUrl: `${process.env.VUE_APP_SERVER}`
}),
actions: {
async GetAnime(filter){
try{
this.filter = filter;
const response = await axios.get('/api/content', {withCredentials: true});
console.log(response);
this.animes_storage = response.data.animes;
this.group_storage = response.data.groups;
this.isLoaded = true;
//return response.data;
return {
isLoaded: this.isLoaded,
anime: this.animes_storage,
search: this.search,
group: this.group_storage
};
}
catch(e){
console.error('Nie udało się pobrać danych');
return {
isLoaded: false,
mess: 'Nie udało się pobrać animu :c'
};
}
},
//filtrowane dane przez filtr
GetFilteringData(filter) {
this.filter = filter;
return this.SerializedData(this.group_storage, this.animes_storage);
},
//filtrowane dane przez wyszukiwarkę
GetSearchData(search) {
this.searchStringFilter = search;
console.log(`STORAGE1: ${this.searchStringFilter}`);
return this.SerializedData(this.group_storage, this.animes_storage);
},
//Pobieranie listy anime
async GetAnimeContent(sort) {
try {
const response = await axios.post('/api/content/result/sort', sort, {withCredentials: true});
return response.data;
}
catch(e) {
console.error('Nie udało się pobrać danych');
return {
isLoaded: false,
mess: 'Nie udało się pobrać animu :c'
};
}
},
//=======================================================
// Pobierz opisu
//=======================================================
async GetDescription(id){
try {
const animeId = {anime: id}
const response = await axios.get('/api/content/description', {params: animeId, withCredentials: true});
return response.data;
}
catch(e){
console.error('ERROR GET DESCRIPTION: ' + e);
return null;
}
},
//=======================================================
// Aktualizacja statusu oglądania
//=======================================================
async UpdateStatus(id, data){
try {
if(await axios.patch('/api/content/anime', data, {params: {id: id}, withCredentials: true})) {
console.log("Zaktualizowano Status");
}
else {
console.error("Nie udało się zaktualizować statusu");
}
}
catch(e){
console.error('ERROR UPDATE: ' + e);
}
},
//=======================================================
// Przesyłanie grafiki na server
//=======================================================
async UploadImage(data) {
try {
const response = await axios.post('/api/content/image', data, {withCredentials: true});
console.log(response)
if(!response) {
return null;
}
return {url: response.data.url, status: response.status};
}
catch(e) {
if (e.response && (e.response.status === 400 || e.response.status === 401)) {
console.warn(`Błąd HTTP ${e.response.status}:`, e.response.data);
return e.response.data;
}
console.error(`Nie udało się zuploadować obrazu: ${e}`);
return null;
}
},
//=======================================================
// Kasowanie grafiki
//=======================================================
async DeleteImage(data) {
try {
await axios.delete('/api/content/image', data, {withCredentials: true});
}
catch (e) {
console.error(`Nie udało się skasować grafiki: ${e}`);
}
},
//Dodawanie rekordu
async AddAnimeToDataBase(data) {
try {
const respornse = await axios.post('/api/content/anime', data, {withCredentials: true});
return respornse.data;
}
catch(e) {
console.error(`Nie udało się wykodać operazji dodania anime: ${e}`);
return false
}
},
//Edytowanie rekordu
async UpdateAnimeInDataBase(data) {
try {
const response = await axios.put('/api/content/anime', data, {withCredentials: true});
return response.data;
}
catch(e) {
console.error(`Nie udało się wykodać operazji edycji anime: ${e}`);
return false
}
},
//Pobieranie danych do edycji
async GetAnimeForEditModal(anime) {
try {
const response = await axios.get('/api/content/anime', {params: {anime: anime}, withCredentials: true});
console.log(response);
return response.data;
}
catch(e) {
console.error(`Nie udało się pobrać recordu: ${e}`);
return null;
}
},
//Pobierz grupę do edycji
GetGroupForEditModal(group) {
let result = {};
this.group_storage.forEach(element => {
if(element.gr_id == group) {
result = element.gr_title;
}
});
return result;
},
//Dodaj nową grupę
async AddGroupRecord(data) {
try {
const response = await axios.post('/api/content/group', data, {withCredentials: true});
if(response.data.complete) {
await this.UpdateGroupList();
}
return response.data.complete;
}
catch(e) {
console.error(`Nie udało się wykonać operacji ${e}`);
return false;
}
},
//Edytuj grupę
async EditGroupRecord(data) {
try {
const response = await axios.put('/api/content/group', data, {withCredentials: true});
if(response.data.complete) {
await this.UpdateGroupList();
}
return response.data.complete;
}
catch(e) {
console.error(`Nie udało się wykonać operacji ${e}`);
return false;
}
},
//Aktualizacja listy grup
async UpdateGroupList(){
try {
const response = await axios.get('/api/content/group', {withCredentials: true});
this.group_storage = response.data;
}
catch(e) {
console.error(`Nie udało się zaktualizować listy gróp: ${e}`);
}
},
async DeleteRecords(data) {
try {
console.log(`TEST ${data.anime}`);
const response = await axios.delete('/api/content/delete', {params: data, withCredentials: true});
return response.data;
}
catch(e) {
console.error(`Nie udało się skasować wpisów: ${e}`);
return false
}
}
},
});

View file

@ -0,0 +1,209 @@
import { defineStore } from 'pinia';
import axios from 'axios';
export const useAuterizationStore = defineStore('Auterization', {
state: () => ({
isLogged: false,
userName: 'NotUser',
userRegistered: '0000-00-00',
userAvatar: require('@/assets/img/user_avatar.jpg'),
//Preferencje
userPreference: {},
//failLogin
failLogin: false,
//adres do validacji
validUrl: `${process.env.VUE_APP_USER_VALID}`,
checkUrl: `${process.env.VUE_APP_USER_CHECK}`
}),
actions: {
async ValidLogin(email, password, remember) {
try{
const response = await axios.post('/api/user/login',
{
email: email,
password: password,
remember: remember,
}, {
withCredentials: true,
headers: {
'Content-Type': 'application/json'
},
});
if(!response.data.isLogged){
this.failLogin = true;
return false;
}
this.isLogged = response.data.isLogged;
this.userName = response.data.user;
this.userRegistered = response.data.regdate;
this.userPreference = response.data.preference;
this.failLogin = false;
if(response.data.avatar) {
this.userAvatar = 'data:image/jpeg;base64,' + response.data.avatar;
}
return true
}
catch(error){
console.error('Błąd:', error);
this.failLogin = true;
return false
}
},
//Pobierz zalogowaną sessję o ile jest zalogowany user
async GetLoginSession(){
try{
const response = await axios.get('/api/user/checksession', {withCredentials: true});
console.log('Sukces:', response);
if(!response.data.isLogged){
this.failLogin = true;
return false;
}
this.isLogged = response.data.isLogged;
this.userName = response.data.user;
this.userRegistered = response.data.regdate;
this.userPreference = response.data.preference;
this.failLogin = false;
if(response.data.avatar) {
this.userAvatar = 'data:image/jpeg;base64,' + response.data.avatar;
}
return true
}
catch(error){
console.error('Błąd:', error);
this.failLogin = true;
return false
}
},
async LogoutUser(){
try {
const response = await axios.delete('/api/user/logout', {withCredentials: true});
if(response.data.success) {
this.isLogged = false;
this.userName = 'NotUser';
this.userRegistered = '0000-00-00';
this.userAvatar = require('@/assets/img/user_avatar.jpg');
}
return response.data.success;
}
catch(e){
console.error('Błąd podczas wylogowywania:', e);
alert('Nie udało się wylogować.');
return false;
}
},
async UpdateUserPref(pref) {
try {
await axios.put('/api/user/preference', pref, {withCredentials: true});
this.userPreference = pref;
}
catch(e) {
console.error(`Nie udało się zaktualizować preferencji`);
}
},
//Aktualizacja danych użytkownika
async UpdateUserAvatar(avatar) {
try {
const response = await axios.put('/api/user/avatar/update', avatar, {withCredentials: true});
if (response.data.success) {
this.userAvatar = 'data:image/jpeg;base64,' + response.data.avatar;
}
return response.data;
}
catch(e) {
if (e.response && (e.response.status === 400 || e.response.status === 401)) {
console.warn(`Błąd HTTP ${e.response.status}:`, e.response.data);
return e.response.data;
}
console.error(`Nie udało się zaktualizować avatara: ${e}`);
}
},
async DeleteUserAvatar() {
try {
const response = await axios.delete('/api/user/avatar/delete', {withCredentials: true});
if(response.data.success) {
this.userAvatar = require('@/assets/img/user_avatar.jpg');
}
return response.data;
}
catch(e) {
if (e.response && (e.response.status === 400 || e.response.status === 401)) {
console.warn(`Błąd HTTP ${e.response.status}:`, e.response.data);
return e.response.data;
}
console.error(`Nie udało się skasować avatara: ${e}`);
}
},
async UpdateUserData(data) {
try {
const response = await axios.put('/api/user/data/update', data, {withCredentials: true});
if(response.data.success) {
this.userName = response.data.username;
}
return response.data;
}
catch(e) {
if (e.response && (e.response.status === 400 || e.response.status === 401)) {
console.warn(`Błąd HTTP ${e.response.status}:`, e.response.data);
return e.response.data;
}
console.error(`Nie udało się zaktualizować danych użytkownika: ${e}`);
}
},
async UpdateUserPassword(password) {
try {
const response = await axios.put('/api/user/password/update', password, {withCredentials: true});
return response.data;
}
catch(e) {
if (e.response && (e.response.status === 400 || e.response.status === 401)) {
console.warn(`Błąd HTTP ${e.response.status}:`, e.response.data);
return e.response.data;
}
console.log(`Nie udało się zaktualizować hasła: ${e}`);
return {success: false, mess: `Nie udało się zaktualizować hasła: ${e}`};
}
},
//Reset hasla
async RecoveryAccount(email) {
try {
const response = await axios.post('/api/user/recovery', email, {withCredentials: true});
return response.data;
}
catch(e) {
console.warn(`Nie udało się odzyskać konta :c ${e}`);
return {success: false, mess: "Nie udało sie wysłać maila recovery"};
}
}
},
persist: {
enabled: true, // Włączenie persystencji dla tego store
strategies: [
{
key: 'auterization', // Klucz w localStorage
storage: localStorage, // Możesz zmienić na sessionStorage
},
],
},
});

View file

@ -0,0 +1,12 @@
import { defineStore } from 'pinia';
export const useCounterStore = defineStore('counter', {
state: () => ({
counter: 0,
}),
actions: {
increment() {
this.counter++;
},
},
});

View file

@ -0,0 +1,119 @@
import { defineStore } from "pinia";
import axios from "axios";
export const useGroupStore = defineStore('Group', {
state: () => ({
data: [],
list: [],
}),
actions: {
//=======================================================
// Pobieranie grupy ze store
//=======================================================
GetData() {
return this.data;
},
//=======================================================
// Dodawanie id grupy do listy
//=======================================================
AddToList(id) {
this.list.push(id);
},
//=======================================================
// Usuwanie id grupy z listy
//=======================================================
RemoveFromList(id) {
let index = null;
for (let i = 0; i < this.list.length; ++i) {
if (this.list[i] === id) {
index = i;
break;
}
}
if (index != null) {
this.list.splice(index);
}
},
//=======================================================
// Pobierz listę grup
//=======================================================
async Get(){
try {
const res = await axios.get('/api/content/group', {withCredentials: true});
this.data = res.data;
}
catch(e) {
if (e.res.status === 400 || e.res.status === 401) {
console.warn('Nie udało się pobrać listy grup!');
}
else {
console.error('Server nie odpowiada!');
}
}
},
//=======================================================
// Dodawanie nowej grupy
//=======================================================
async Add(data) {
try {
const res = await axios.post('/api/content/group', data, {withCredentials: true});
console.log(res.data.data);
if (res.data.data) {
this.data = res.data.data;
}
}
catch(e) {
if (e.res.status === 400 || e.res.status === 401) {
console.warn('Nie udało się pobrać listy grup!');
}
else {
console.error('Server nie odpowiada!');
}
}
},
//=======================================================
// Edytowanie nazwy grupy
//=======================================================
async Edit(data) {
try {
const res = await axios.patch('/api/content/group', {name: data.name}, {params: {id: data.id}, withCredentials: true});
this.data = res.data;
}
catch(e) {
if (e.res.status === 400 || e.res.status === 401) {
console.warn('Nie udało się pobrać listy grup!');
}
else {
console.error('Server nie odpowiada!');
}
}
},
//=======================================================
// Kasowanie grupy
//=======================================================
async Delete() {
try {
const data = {id: this.list};
const res = await axios.delete('/api/content/group', {params: data, withCredentials: true});
this.data = res;
}
catch(e) {
if (e.res.status === 400 || e.res.status === 401) {
console.warn('Nie udało się pobrać listy grup!');
}
else {
console.error('Server nie odpowiada!');
}
}
}
}
});

View file

@ -0,0 +1,47 @@
import { defineStore } from "pinia";
import axios from 'axios';
const useModalStore = defineStore('Modal', {
state: {
mdType: '',
mdMessage: {
add: [
'Dodanie Anime zakończone',
'Dodanie Anime nie powiodło się',
'Anime o tym tytule już istnieje',
],
edit: [
'Dodanie Rekordu zakończone',
'Dodanie Rekordu nie powiodło się'
],
group: [
'Dodanie Grupy zakończone',
'Dodanie Grupy nie powiodło się',
],
delete: [
'Kasacja zakończona sukcesem',
'Nie udało się skasować'
]
}
},
actions: {
SetModalType(tp){
this.mdType = tp;
},
GetModalType(){
return this.mdType;
},
async AddAnime(data){
try {
response = await axios.post('/anime/add', data, {withCredentials: true});
return mdMessage.add[response];
}
catch(e){
return mdMessage.add[1];
}
}
}
});

View file

@ -0,0 +1,110 @@
import { defineStore } from 'pinia';
export const useModeratedStore = defineStore('Moderated', {
state: () => ({
//editButton: true,
//deleteButton: true,
checkIdAnimeStore: [],
checkIdGroupStore: [],
}),
getters: {
editButton(state) {
const groupLen = state.checkIdGroupStore.length;
const animeLen = state.checkIdAnimeStore.length;
if(animeLen > 0 || groupLen > 0) {
if(animeLen > 0 && groupLen > 0) {
return true;
}
if(animeLen > 1 || groupLen > 1) {
return true;
}
return false;
}
return true;
},
deleteButton(state) {
const gouprLen = state.checkIdGroupStore.length;
const animeLen = state.checkIdAnimeStore.length;
// Przyciski usuwania aktywny tylko gdy oba length są 0
return !(gouprLen > 0 || animeLen > 0);
},
animeEdit(state) {
return state.checkIdAnimeStore.length > 0 ? true : false;
},
groupEdit(state) {
return state.checkIdGroupStore.length > 0 ? true : false;
}
},
actions: {
AddIdToStore(data, isGroup = false) {
if(isGroup) {
this.checkIdGroupStore.push(data);
console.log(this.checkIdGroupStore);
}
else {
this.checkIdAnimeStore.push(data);
console.log(this.checkIdAnimeStore);
}
//this.ActiveTogglerButton();
},
DeleteIdFromStore(data, isGroup = false) {
if(isGroup) {
for(var i=0; i < this.checkIdGroupStore.length; i++) {
if(this.checkIdGroupStore[i] === data) {
this.checkIdGroupStore.splice(i, 1);
i--;
}
}
console.log(this.checkIdGroupStore);
}
else {
for(var j=0; j < this.checkIdAnimeStore.length; j++) {
if(this.checkIdAnimeStore[j] === data) {
this.checkIdAnimeStore.splice(j, 1);
j--;
}
}
console.log(this.checkIdAnimeStore);
}
//this.ActiveTogglerButton();
},
ActiveTogglerButton(){
const gouprLen = this.checkIdGroupStore.length;
const animeLen = this.checkIdAnimeStore.length;
if(gouprLen > 0 || animeLen > 0) {
this.deleteButton = false;
if((gouprLen > 0 && animeLen > 0) || (gouprLen > 1 || animeLen > 1)) {
this.editButton = true;
}
else {
this.editButton = false;
}
}
else {
this.deleteButton = true;
this.editButton = true;
}
},
ResetIndex() {
console.log("Zresetowano index");
this.checkIdAnimeStore = [];
this.checkIdGroupStore = [];
},
}
});

View file

@ -0,0 +1,39 @@
<template lang="html">
<div class="site-body-content">
<MenuNav :menuSet="menuNav" />
<div class="content">
<DataTable />
</div>
<WebSiteFooter />
</div>
</template>
<script>
import MenuNav from './../components/menu.vue';
import DataTable from './../components/table/table.vue';
import WebSiteFooter from './../components/footer.vue';
export default {
name: 'AnimePage',
components: {
MenuNav,
DataTable,
WebSiteFooter,
},
provide(){
return {
setReloadTable: (fn) => (this.reloadTable = fn),
reloadTable: () => {
this.reloadTable();
},
}
},
data() {
return {
reloadTable: null,
menuNav: "ANIME",
}
}
}
</script>

View file

@ -0,0 +1,34 @@
<template lang="html">
<div class="not-found-body d-flex justify-content-center">
<div class="not-found-box d-flex flex-column justify-content-center align-items-center">
<h1>ERROR 404</h1>
<h4>Page not found</h4>
<p class="mt-5">Nie tędy droga. Jesteś w backroomsach.</p>
</div>
</div>
</template>
<script>
export default {
name: "Error404Pane"
}
</script>
<style lang="css">
.not-found-body {
width: 100vw;
height: 100vh;
background-image: url("../assets/img/bg404.jpg");
background-size: cover;
}
.not-found-box {
width: 512px;
height: 256px;
margin-top: 64px;
background-color: var(--bg-normal-color);
border-radius: 8px 8px 8px 8px;
-webkit-box-shadow:0px 0px 45px 23px rgba(0,0,0,0.82);
-moz-box-shadow: 0px 0px 45px 23px rgba(0,0,0,0.82);
box-shadow: 0px 0px 45px 23px rgba(0,0,0,0.82);
}
</style>

View file

@ -0,0 +1,44 @@
<template lang="html">
<div>
Animu Lista<br>
v. 2.0.0<br>
2024
</div>
</template>
<script>
import {useAuterizationStore} from '@/stores/auterization';
export default {
name: 'HomePage',
setup() {
const auterizationStore = useAuterizationStore();
return{
GetLoginSession: auterizationStore.GetLoginSession
}
},
mounted() {
this.CheckUserSession();
},
methods: {
async CheckUserSession(){
var isLogged = false;
isLogged = await this.GetLoginSession();
if(isLogged){
this.$router.push('/anime');
console.log('Zalogowany');
}
else{
this.$router.push('/login');
console.log('Nie zalogowany');
}
}
}
}
</script>
<style lang="">
</style>

View file

@ -0,0 +1,58 @@
<template lang="html">
<div class="sign-in-body d-flex justify-content-center align-items-center">
<div class="sign-in-container" :class="shakeClass">
<div class="sign-in-segment sin-bg-window">
<img src="@/assets/img/logov2.png" alt="Anime Lista" class="img-logo">
</div>
<div class="sign-in-segment p-4">
<LoginWindow @shakeScr="ShakeScreen" @recoverScr="ChangeView('RECOVERY')" />
</div>
</div>
</div>
</template>
<script>
import LoginWindow from '@/components/login_window.vue';
import '@/assets/css/login.css';
export default {
name: 'LoginPage',
components: {
LoginWindow,
},
data() {
return {
//Błąd shake
shakeClass: "",
shakeTimer: false,
}
},
watch: {
shakeTimer(value) {
if (value) {
setTimeout(() => {
this.shakeClass = '';
this.shakeTimer = false;
}, 1000);
}
},
},
methods: {
GoToAnimeDashboard() {
this.$router.push('/anime');
},
ShakeScreen() {
this.shakeClass = '';
this.shakeTimer = true;
this.shakeClass = 'shake-horizontal';
},
}
}
</script>

View file

@ -0,0 +1,24 @@
<template lang="html">
<MenuNav :menuSet="'CONTROL'" />
<div class="control-body">
<UserControlContent />
</div>
<WebSiteFooter />
</template>
<script>
import MenuNav from '@/components/menu.vue';
import WebSiteFooter from '@/components/footer.vue';
import UserControlContent from '@/components/control_panel_content.vue';
import '@/assets/css/control.css';
export default {
name: 'UserControlPage',
components: {
MenuNav,
WebSiteFooter,
UserControlContent,
}
}
</script>

18
client_2.1/vue.config.js Normal file
View file

@ -0,0 +1,18 @@
const { defineConfig } = require('@vue/cli-service');
module.exports = defineConfig({
transpileDependencies: true,
devServer: {
proxy: {
// Wszystkie zapytania zaczynające się od /user LUB /content
// zostaną przekierowane do serwera backendowego.
'/api': {
target: 'http://127.0.0.1:3000', // Adres backendu
changeOrigin: true,
secure: false,
// Usuwamy /api z początku ścieżki przed wysłaniem do backendu
pathRewrite: { '^/api': '' },
},
},
},
});

11
server_2.1/.gitignore vendored Normal file
View file

@ -0,0 +1,11 @@
#Katalog upload
upload/**
!upload/index.htm
#Moduły node js
node_modules
#ENV
.env
test.js

View file

@ -0,0 +1,17 @@
{
"extension": "jpg",
"type": "jpeg",
"quality": 80,
"poster": {
"width": 200,
"height": 285
},
"miniature": {
"width": 50,
"height": 70
},
"avatar": {
"width": 128,
"height": 128
}
}

35
server_2.1/index.js Normal file
View file

@ -0,0 +1,35 @@
const express = require('express');
require('dotenv').config();
const session = require('./src/session')
const cors = require('./src/cors');
const fileUpload = require('express-fileupload');
const userRoute = require('./src/UserRouting');
const animeRoute = require('./src/AnimeRouting');
const app = express();
app.use(express.json());
app.use(cors);
app.use(fileUpload({
limits: { fileSize: 1024 * 1024 }, // Ustaw limit na 1 MB
abortOnLimit: true, // Przerwij przesyłanie, jeśli plik przekracza limit
}));
app.use((req, res, next) => {
console.log(`Otrzymano żądanie: ${req.method} ${req.url}`);
next();
});
app.use(session);
app.get('/', (req, res) => {
res.send(`
Hi Mom!
`);
})
app.use('/user', userRoute);
app.use('/content', animeRoute);
app.listen(process.env.EX_PORT, () => {
console.log(`Serwer HTTP działa na http://localhost:${process.env.EX_PORT}`);
});

2830
server_2.1/package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

34
server_2.1/package.json Normal file
View file

@ -0,0 +1,34 @@
{
"name": "animelista_server",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"altcha": "^2.0.5",
"altcha-lib": "^1.2.0",
"bcrypt": "^5.1.1",
"compression": "^1.8.0",
"cors": "^2.8.5",
"dotenv": "^16.4.7",
"express": "^4.21.1",
"express-fileupload": "^1.5.1",
"express-mysql-session": "^3.0.3",
"express-session": "^1.18.1",
"glob": "^11.0.0",
"html-entities": "^2.5.2",
"jsonwebtoken": "^9.0.2",
"mysql": "^2.18.1",
"mysql2": "^3.11.4",
"nodemailer": "^6.10.0",
"rimraf": "^6.0.1",
"sharp": "^0.33.5",
"uuid": "^11.0.5",
"validator": "^13.12.0"
},
"description": ""
}

View file

@ -0,0 +1,38 @@
const express = require('express');
const router = express.Router();
const anime = require('./anime');
const imm = require('./image');
//Routing
//Pobieranie anime
router.get('/', anime.GetAnime);
//Pobieranie posortowanego warunkowo anime
router.post('/result/sort', anime.GetFilteringAnime)
//Pobieranie opisu
router.get('/description', anime.GetDescription);
//Dodawanie nowego anime do bazy danych
router.post('/anime/add', anime.AddAnimeToRecord);
//Edycja
router.get('/anime/get', anime.GetAnimeRecord);
router.put('/anime/edit', anime.EditAnimeRecord);
//Dodawanie grupy
router.post('/group', anime.AddGrouptoRecord);
router.put('/group', anime.EditGroupRecord);
router.get('/group', anime.GetGroupList);
//Kasowanie rekordów
router.delete('/delete', anime.DeleteRecords);
//Aktualizacja statusów oglądania
router.put('/anime/update', anime.UpdateState);
//Przesyłanie obrazu i czyszczenie rekordu obrazu
router.post('/image/add', imm.UploadImageFromFile);
router.delete('/image/delete', imm.DeleteImage);
module.exports = router;

View file

@ -0,0 +1,23 @@
class DateTime {
constructor () {
this.date = new Date();
}
GetDateNow() {
const year = String(this.date.getFullYear());
const month = String(this.date.getMonth() + 1).padStart(2, '0'); // Poprawna nazwa zmiennej
const day = String(this.date.getDate()).padStart(2, '0'); // Poprawna metoda
return `${year}-${month}-${day}`;
}
GetTimeNow() {
const hour = String(this.date.getHours()).padStart(2, '0');
const minute = String(this.date.getMinutes()).padStart(2, '0');
const second = String(this.date.getSeconds()).padStart(2, '0');
return `${hour}:${minute}:${second}`;
}
}
module.exports = new DateTime();

View file

@ -0,0 +1,29 @@
const fs = require('node:fs');
const dt = require('./DateTime');
class LogsCapture {
constructor() {
this.path = '@/logs.log'; //Ścieżka do pliku log
this.fs = fs;
this.dt = dt;
}
async SaveLog(prefix, sufix, description) {
const date = this.dt.GetDate();
const time = this.dt.GetTime();
const content = date + '_' + time + '___' + prefix + '_' + sufix + '___: ' + description;
await this.fs.writeFile(this.path, content);
}
async CaptureError(sufix ,error) {
await this.SaveLog('ERROR', sufix, error);
}
async CaptureWarning(sufix ,error) {
await this.SaveLog('WARNING', sufix, error);
}
}
module.exports = new LogsCapture();

View file

@ -0,0 +1,18 @@
const express = require('express');
const router = express.Router();
const user = require('./user');
router.post('/login', user.LoginProcess);
router.get('/checksession', user.UserCheckSession);
router.delete('/logout', user.LogoutUser);
router.put('/preference', user.UpdateUserPreference);
router.put('/password/update', user.UpdateUserPassword);
router.put('/data/update', user.UpdateUserName);
router.put('/avatar/update', user.UpdateUserAvatar);
router.delete('/avatar/delete', user.DeleteAvatar);
router.post('/altcha', user.AltchaHumanTest);
module.exports = router;

View file

View file

@ -0,0 +1,51 @@
const fs = require('fs');
const path = require('path');
const imm = require('../image');
const AnimeRepository = require('../database/AnimeRepository');
const SegregatedRepository = require('../database/SegregatedRepository');
async function AddAnimeToRecord(req, res) {
try {
if(!req.session.isLogged) {
console.error('DESCRYPTION ERROR: Urzytkownik nie jest zalogowany');
return res.status(401).json({complete: false});
}
const data = req.body;
const group = Number(data.group);
const imgPaths = req.session.img_files;
const userId = req.session.user_id;
const userHash = req.session.user_hash;
const posterPath = imgPaths?.poster !== undefined ? imgPaths.poster : null;
const miniaturePath = imgPaths?.miniature !== undefined ? imgPaths.miniature : null;
const blob = {
poster: await imm.LoadImgToBlob(posterPath),
miniature: await imm.LoadImgToBlob(miniaturePath),
};
const animeId = await AnimeRepository.Add(data, blob, userId);
if(group != 0) {
SegregatedRepository.Add(animeId, group, userId);
}
const uploadDirectory = path.join(__dirname, '../../upload', userHash);
console.log(uploadDirectory);
if(fs.existsSync(uploadDirectory)) {
fs.rmSync(uploadDirectory, { recursive: true });
}
res.status(200).json({
mess: 'Dodano nowe anime',
complete: true
});
}
catch(e) {
console.error(`ERROR Nie udało się dodać anime do bazy danych: ${e}`);
res.status(500).json({complete: false});
}
}
module.exports = AddAnimeToRecord;

View file

@ -0,0 +1,55 @@
const GroupRepository = require('../database/GroupRepository');
async function AddGroupToRecord(req, res) {
try {
if(!req.session.isLogged) {
console.error(`ERROR: Nowa grupa nie dodana. Użytkownik nie zalogowany`);
return res.status(401).json({
mess: 'Użytkownik nie zalogowany',
complete: false,
data: null
});
}
const group = req.body.name;
if(!group) {
console.error(`ERROR: AddGroupToRecord: Zapyanie jest puste!`);
return res.status(400).json({
mess: 'Brak danych',
complete: false,
data: null
});
}
const userId = req.session.user_id;
if (!await GroupRepository.Add(group, userId)) {
console.error(`ERROR: Niepowodzenie dodania rekordu!`);
return res.status(400).json({
mess: 'Błąd wewnętrzny',
complete: false,
data: null
});
}
const data = await GroupRepository.Get(userId);
console.log(data);
res.status(200).json({
mess: 'Record dodany',
complete: true,
data: data
});
}
catch(e) {
console.error(`ERROR: Function AddGroupToRecord: ${e}`);
res.status(500).json({
mess: 'Błąd wewnętrzny',
complete: false,
data: null
});
}
}
module.exports = AddGroupToRecord;

View file

@ -0,0 +1,60 @@
const AnimeRepo = require('../database/AnimeRepository');
const GroupRepo = require('../database/GroupRepository');
const SegregatedRepo = require('../database/SegregatedRepository');
const FavoritesRepo = require('../database/FavoritesRepository');
async function DeleteAnimeRecords(anime, user) {
try {
await AnimeRepo.Delete(anime, user);
await FavoritesRepo.Delete(anime, user);
await SegregatedRepo.DeleteByAnime(anime, user);
}
catch(e) {
console.error(`ERROR: Nie udało się skasować anime: ${e}`);
}
}
async function DeleteGroupRecords(group, user) {
try {
await GroupRepo.Delete(group, user);
await SegregatedRepo.DeleteByGroup(group, user);
}
catch(e) {
console.error(`ERROR: Nie udało się skasować grupy: ${e}`);
}
}
async function DeleteRecords(req, res) {
try {
if(!req.session.isLogged) {
console.error('DESCRYPTION ERROR: Urzytkownik nie jest zalogowany');
return res.status(401).json({
mess: 'Użytkownik nie jest zalogowany',
complete: false
});
}
const group = req.query.group;
const anime = req.query.anime;
const userId = req.session.user_id;
if(group) {
await DeleteGroupRecords(group, userId);
}
if(anime) {
await DeleteAnimeRecords(anime, userId);
}
res.status(200).json({mess: 'Pomyślnie skasowano rekordy', complete: true});
}
catch(e) {
console.error(`ERROR: Nie udało się skasować anime: ${e}`);
res.status(500).json({
mess: 'Nie udało się wykoniac operacji kasowania',
complete: false
});
}
}
module.exports = DeleteRecords;

View file

@ -0,0 +1,132 @@
const AnimeRepository = require('../database/AnimeRepository');
const SegregatedRepository = require('../database/SegregatedRepository');
const imm = require('../image');
const path = require('path');
const fs = require('fs');
//Aktualizacja lub kasowanie przypisania anime do danej grupy
async function SergregateUpdate(userId, updateData) {
if(updateData.group != 0) {
const result = await SegregatedRepository.Edit(updateData.group, updateData.id, userId);
if(result === 0) {
await SegregatedRepository.Add(updateData.id, updateData.group, userId);
}
}
else {
await SegregatedRepository.DeleteByAnime(updateData.id, userId);
}
}
//Aktualizacja obrazka
async function PosterUpdater(updateData, imgPaths) {
let updateImage = false;
let poster = null;
let miniature = null;
if(updateData.newImage && !updateData.removeImage) {
const posterPath = imgPaths?.poster !== undefined ? imgPaths.poster : null;
const miniaturePath = imgPaths?.miniature !== undefined ? imgPaths.miniature : null;
poster = await imm.LoadImgToBlob(posterPath);
miniature = await imm.LoadImgToBlob(miniaturePath);
updateImage = true;
}
if(updateData.removeImage && !updateData.newImage) {
updateImage = true;
}
return {
update: updateImage,
poster: poster,
miniature: miniature
};
}
//Aktualizacja opisu
async function DescriptionUpdate(dsdata) {
if (dsdata.description || dsdata.poster) {
if (await AnimeRepository.EditDescription(dsdata) < 1) {
await AnimeRepository.AddDescription(dsdata);
}
}
else {
await AnimeRepository.DeleteDescription(dsdata.anie);
}
}
async function EditAnimeRecord(req, res) {
try {
if(!req.session.isLogged) {
console.error('ERROR: Edit anime record request. Użytkownik nie jest zalogowany');
return res.status(401).json({
mess: 'Użytkownik nie jest zalogowany',
complete: false
});
}
if(!req.body) {
console.error('ERROR: Edit anime record request. Użytkownik nie jest zalogowany');
return res.status(401).json({
mess: 'Użytkownik nie jest zalogowany',
complete: false
});
}
const updateData = req.body;
const userHash = req.session.user_hash;
const imgPaths = req.session.img_files;
const userId = req.session.user_id;
//Aktualizacja obrazu
const image = await PosterUpdater(updateData, imgPaths);
// Dane do aktualizacji tabeli anm_animes
const data = {
id: updateData.id,
user: userId,
title: updateData.title,
url: updateData.url,
episodes: updateData.episodes,
miniature: image.miniature,
updateMiniature: image.update
};
// Dane do aktualizacji tabeli anm_descriptions
const dsdata = {
anime: updateData.id,
description: updateData.description,
poster: image.poster,
posterUpdate: image.update
};
//Aktualizuj rekord
await AnimeRepository.Update(data);
//Aktualizacja lub kasowanie przypisania anime do danej grupy
await SergregateUpdate(userId, updateData);
//Aktualizacja rekordu descryption
await DescriptionUpdate(dsdata);
const uploadDirectory = path.join(__dirname, '../../upload', userHash);
if(fs.existsSync(uploadDirectory)) {
fs.rmSync(uploadDirectory, { recursive: true });
}
res.status(200).json({
mess: 'Rekord został zaktualizowany',
complete: true
});
}
catch(e) {
console.error(`ERROR: Nie udało się zaktualizować recordu: ${e}`);
res.status(500).json({
mess: 'Nie udało się zaktualizować rekordu',
complete: false
});
}
}
module.exports = EditAnimeRecord;

Some files were not shown because too many files have changed in this diff Show more