Compare commits

..

39 Commits

Author SHA1 Message Date
Julien Aldon
84369911cd add restart always to nginx service
All checks were successful
Deploy Bookshelf / deploy (push) Successful in 6s
2026-01-14 13:49:17 +01:00
Julien Aldon
443cfd7615 add front responsiveness for phone
All checks were successful
Deploy Bookshelf / deploy (push) Successful in 20s
2026-01-13 17:02:16 +01:00
Julien Aldon
ed22dfd412 fix docker-compose version
All checks were successful
Deploy Bookshelf / deploy (push) Successful in 36s
2026-01-13 16:03:08 +01:00
Julien Aldon
aec31fbd06 fix docker-compose version
Some checks failed
Deploy Bookshelf / deploy (push) Failing after 5s
2026-01-13 15:59:28 +01:00
Julien Aldon
2e5aeb46ab fix docker-compose prod ROOT_FQDN var
All checks were successful
Deploy Bookshelf / deploy (push) Successful in 36s
2026-01-13 15:57:07 +01:00
Julien Aldon
b718865030 add ROOT_FQDN env variable
Some checks failed
Deploy Bookshelf / deploy (push) Failing after 5s
2026-01-13 15:55:32 +01:00
Julien Aldon
e8208363c0 fix front config.js
All checks were successful
Deploy Bookshelf / deploy (push) Successful in 19s
2026-01-13 15:28:26 +01:00
Julien Aldon
f8f37db3d6 Merge branch 'main' of gitea.aldon.fr:Mop/bookshelf
All checks were successful
Deploy Bookshelf / deploy (push) Successful in 52s
2026-01-13 15:26:20 +01:00
Julien Aldon
d5fc8142b2 fix front pagination and search 2026-01-13 15:26:11 +01:00
9ce55782c6 add workflow_dispatch for api trigger
All checks were successful
Deploy Bookshelf / deploy (push) Successful in 7s
2026-01-12 22:30:23 +01:00
Julien Aldon
959d4e7281 remove debug
All checks were successful
Deploy Bookshelf / deploy (push) Successful in 6s
2026-01-12 16:24:12 +01:00
Julien Aldon
626fbaa269 test deploy
All checks were successful
Deploy Bookshelf / deploy (push) Successful in 7s
2026-01-12 15:23:52 +01:00
Julien Aldon
eef53216de test deploy
All checks were successful
Deploy Bookshelf / deploy (push) Successful in 7s
2026-01-12 15:22:37 +01:00
Julien Aldon
fae951d9f4 fix environment phase
All checks were successful
Deploy Bookshelf / deploy (push) Successful in 6s
2026-01-12 14:19:51 +01:00
Julien Aldon
098c442bb9 add debug
All checks were successful
Deploy Bookshelf app / deploy (push) Successful in 5s
2026-01-12 14:13:05 +01:00
Julien Aldon
f2e642d376 add debug
Some checks failed
Deploy Bookshelf app / deploy (push) Has been cancelled
2026-01-12 14:10:59 +01:00
Julien Aldon
bbbc054c54 add debug
Some checks failed
Deploy Bookshelf app / deploy (push) Has been cancelled
2026-01-12 14:01:42 +01:00
Julien Aldon
9a6e443c44 add debug
Some checks failed
Deploy Bookshelf app / deploy (push) Failing after 5s
2026-01-12 14:00:09 +01:00
Julien Aldon
ccaf1bc039 add volume to deploy
Some checks failed
Deploy Bookshelf app / deploy (push) Failing after 4s
2026-01-12 13:57:45 +01:00
Julien Aldon
5c1e3fe68f fix deploy 2026-01-12 13:56:43 +01:00
Julien Aldon
39890a02ab fix deploy 2026-01-12 13:56:15 +01:00
Julien Aldon
bab1954ed0 fix deploy 2026-01-12 13:54:16 +01:00
Julien Aldon
75df01b8f6 add environment setup phase
All checks were successful
Deploy Bookshelf / deploy (push) Successful in 6s
2026-01-12 13:25:55 +01:00
Julien Aldon
12788c6109 add environment setup phase
All checks were successful
Deploy Bookshelf / deploy (push) Successful in 7s
2026-01-12 13:24:14 +01:00
Julien Aldon
531ac4b85f add environment setup phase
All checks were successful
Deploy Bookshelf / deploy (push) Successful in 8s
2026-01-12 13:22:42 +01:00
Julien Aldon
9ce4810f25 add environment setup phase 2026-01-12 13:22:09 +01:00
Julien Aldon
2b6b9794b0 fix deploy action
All checks were successful
Deploy Bookshelf / deploy (push) Successful in 6s
2026-01-12 13:18:01 +01:00
Julien Aldon
69bc74835b fix .env
Some checks failed
Deploy Bookshelf / deploy (push) Failing after 5s
2026-01-12 13:16:39 +01:00
Julien Aldon
50d486a3a3 fix secret.py
Some checks failed
Deploy Bookshelf / deploy (push) Failing after 4s
2026-01-12 13:15:51 +01:00
Julien Aldon
164eec1997 fix secret.py
Some checks failed
Deploy Bookshelf / deploy (push) Failing after 5s
2026-01-12 13:12:10 +01:00
Julien Aldon
5d8c7b0724 fix docker compose and deploy.yaml
Some checks failed
Deploy Bookshelf / deploy (push) Failing after 5s
2026-01-12 13:10:10 +01:00
Julien Aldon
93eabcb75e fix docker-compose env file path
Some checks failed
Deploy Bookshelf / deploy (push) Failing after 4s
2026-01-12 13:04:37 +01:00
Julien Aldon
fd49074614 fix docker-compose env file path
Some checks failed
Deploy Bookshelf / deploy (push) Failing after 4s
2026-01-12 13:04:10 +01:00
Julien Aldon
02f434a6ff fix docker-compose env file path
Some checks failed
Deploy Bookshelf / deploy (push) Failing after 4s
2026-01-12 13:03:00 +01:00
Julien Aldon
e3b94ceaeb add debug
All checks were successful
Deploy Bookshelf / deploy (push) Successful in 6s
2026-01-12 12:23:13 +01:00
e657581220 remove dist folder
All checks were successful
Deploy Bookshelf / deploy (push) Successful in 7s
2026-01-12 11:33:22 +01:00
Julien Aldon
e50efe1cdf fix docker compose merge
All checks were successful
Deploy Bookshelf / deploy (push) Successful in 7s
2026-01-12 11:32:33 +01:00
Julien Aldon
e9ea592a2d add docker-compose 2026-01-12 11:32:09 +01:00
Julien Aldon
f8ae52d861 add gitignore 2026-01-12 11:30:45 +01:00
21 changed files with 352 additions and 52 deletions

10
.env.example Normal file
View File

@@ -0,0 +1,10 @@
DB_HOST=database
MARIADB_USER=root
# openssl rand -hex 32
SERVICE_SECRET_KEY=xxxx
# openssl rand -hex 12
MARIADB_PASSWORD=xxxx
# openssl rand -hex 12
MARIADB_ROOT_PASSWORD=xxxx
MARIADB_DATABASE=bookshelf
SERVICE_ORIGIN=http://localhost:8080

View File

@@ -3,6 +3,7 @@ on:
push: push:
branches: branches:
- main - main
workflow_dispatch:
jobs: jobs:
deploy: deploy:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -10,7 +11,6 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Build & deploy - name: Build & deploy
run: | run: |
git pull git pull

183
.gitignore vendored Normal file
View File

@@ -0,0 +1,183 @@
# Created by https://www.toptal.com/developers/gitignore/api/python,vuejs
# Edit at https://www.toptal.com/developers/gitignore?templates=python,vuejs
### Python ###
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/#use-with-ide
.pdm.toml
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
### Python Patch ###
# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration
poetry.toml
# ruff
.ruff_cache/
# LSP config files
pyrightconfig.json
### Vuejs ###
# Recommended template: Node.gitignore
node_modules/
npm-debug.log
yarn-error.log
# End of https://www.toptal.com/developers/gitignore/api/python,vuejs

View File

@@ -3,16 +3,22 @@ Ce projet est un gestionnaire de base de données pour la bibliotèque de la mai
L'outil permet l'ajout, la modification, la suppression et de consulter les livres présents dans la base de donnée. L'outil permet l'ajout, la modification, la suppression et de consulter les livres présents dans la base de donnée.
## Deploiement ## Développement local
### Avec Docker Compose ### Avec Docker Compose
```sh ```sh
docker-compose up --build -d docker compose -f docker-compose.dev.yml down -v
docker compose -f docker-compose.dev.yml up
# Init database # Init database (if database not initialized)
docker-compose exec database mariadb -u root -p docker-compose exec database mariadb -u root -p
$>source /docker-entrypoint-initdb.d/*.sql $>source /docker-entrypoint-initdb.d/*.sql
```
## Déploiement
```sh
# SSL https://certbot-dns-ovh.readthedocs.io/en/stable/ # SSL https://certbot-dns-ovh.readthedocs.io/en/stable/
# Create certificates # Create certificates
certbot certonly --dns-ovh --dns-ovh-credentials ~/.secrets/certbot/ovh.ini -d home.aldon.fr certbot certonly --dns-ovh --dns-ovh-credentials ~/.secrets/certbot/ovh.ini -d bookshelf.aldon.fr
``` ```
## TODO
Change `front/src/config.js` : Variabilize `ROOT_FQDN`

View File

@@ -1,15 +1,15 @@
import os import os
origins = [ origins = [
os.environ['ORIGIN'] os.environ['SERVICE_ORIGIN']
] ]
host = os.environ['DB_HOST'] host = os.environ['DB_HOST']
user = os.environ['DB_USER'] user = os.environ['MARIADB_USER']
password = os.environ['DB_PASS'] password = os.environ['MARIADB_PASSWORD']
database = os.environ['DB_NAME'] database = os.environ['MARIADB_DATABASE']
# openssl rand -hex 32 # openssl rand -hex 32
SECRET_KEY = os.environ['SECRET_KEY'] SECRET_KEY = os.environ['SERVICE_SECRET_KEY']
ALGORITHM = 'HS256' ALGORITHM = 'HS256'
ACCESS_TOKEN_EXPIRE_MINUTES = 600 ACCESS_TOKEN_EXPIRE_MINUTES = 600

52
docker-compose.dev.yml Normal file
View File

@@ -0,0 +1,52 @@
version: "3.9"
services:
back:
image: python:3.10
working_dir: /code
volumes:
- ./back:/code
command: >
sh -c "pip install -r requirements.txt &&
uvicorn main:app --host 0.0.0.0 --port 8000 --reload"
environment:
SERVICE_ORIGIN: ${SERVICE_ORIGIN}
DB_HOST: database
MARIADB_USER: ${MARIADB_USER}
MARIADB_PASSWORD: ${MARIADB_PASSWORD}
MARIADB_DATABASE: ${MARIADB_DATABASE}
SERVICE_SECRET_KEY: ${SERVICE_SECRET_KEY}
ports:
- "8000:8000"
depends_on:
- database
front:
image: node:19.1-alpine
working_dir: /app
volumes:
- ./front:/app
command: sh -c "npm install && npm run serve"
ports:
- "8080:8080"
environment:
VUE_APP_ROOT_FQDN: ${SERVICE_ROOT_FQDN}
depends_on:
- back
database:
image: mariadb
restart: always
environment:
MARIADB_USER: ${MARIADB_USER}
MARIADB_PASSWORD: ${MARIADB_PASSWORD}
MARIADB_ROOT_PASSWORD: ${MARIADB_ROOT_PASSWORD}
MARIADB_DATABASE: ${MARIADB_DATABASE}
volumes:
- db:/var/lib/mysql
- ./back/db/:/docker-entrypoint-initdb.d/
ports:
- "3306:3306"
volumes:
db:

View File

@@ -1,9 +1,12 @@
version: "2" version: "3.9"
services: services:
nginx: nginx:
restart: always
build: build:
context: . context: .
dockerfile: front/Dockerfile dockerfile: front/Dockerfile
args:
VUE_APP_ROOT_FQDN: ${SERVICE_ROOT_FQDN}
ports: ports:
- 80:80 - 80:80
depends_on: depends_on:
@@ -13,6 +16,13 @@ services:
context: . context: .
dockerfile: back/Dockerfile dockerfile: back/Dockerfile
restart: always restart: always
environment:
SERVICE_ORIGIN: ${SERVICE_ORIGIN}
DB_HOST: database
MARIADB_USER: ${MARIADB_USER}
MARIADB_PASSWORD: ${MARIADB_PASSWORD}
MARIADB_DATABASE: ${MARIADB_DATABASE}
SERVICE_SECRET_KEY: ${SERVICE_SECRET_KEY}
ports: ports:
- 8000:8000 - 8000:8000
depends_on: depends_on:
@@ -25,6 +35,7 @@ services:
MARIADB_PASSWORD: ${MARIADB_PASSWORD} MARIADB_PASSWORD: ${MARIADB_PASSWORD}
MARIADB_ROOT_PASSWORD: ${MARIADB_ROOT_PASSWORD} MARIADB_ROOT_PASSWORD: ${MARIADB_ROOT_PASSWORD}
MARIADB_DATABASE: ${MARIADB_DATABASE} MARIADB_DATABASE: ${MARIADB_DATABASE}
ROOT_FQDN: ${ROOT_FQDN}
volumes: volumes:
- db:/var/lib/mysql - db:/var/lib/mysql
- ./back/db/:/docker-entrypoint-initdb.d/ - ./back/db/:/docker-entrypoint-initdb.d/

View File

@@ -2,6 +2,9 @@ FROM node:19.1-alpine AS build
WORKDIR /app WORKDIR /app
ARG VUE_APP_ROOT_FQDN
ENV VUE_APP_ROOT_FQDN=$VUE_APP_ROOT_FQDN
COPY front/package.json front/package-lock.json /app/ COPY front/package.json front/package-lock.json /app/
RUN npm install RUN npm install

View File

@@ -1 +0,0 @@
.red{background-color:#de1656;color:#fff}.red:hover{background-color:#961656}.green{background-color:#16de56}.green:hover{background-color:#169656}header{width:60.5vw}.header,header{display:flex}body{margin:0}.button{border:none;padding:.2rem;margin:.3rem}.view{display:flex;flex-direction:column;align-items:center;margin-top:5rem}.router{flex:1 1 auto;background-color:#de1656;border:none;color:#fff;text-align:center;text-decoration:none;display:inline-block;font-size:16px;transition:background-color .5s ease-in}.router:focus,.router:hover{background-color:#961656}#app{font-family:Avenir,Helvetica,Arial,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;text-align:center;color:#2c3e50}nav a{padding:1rem;margin:0}table{table-layout:fixed}.button-panel{display:flex;flex-flow:row}tr:nth-child(odd){background-color:hsla(0,0%,4%,.1)}tr{text-align:left}tr:hover{background-color:hsla(0,0%,4%,.1)}h1{flex:1 1 auto;text-align:left}td{overflow:hidden;text-overflow:ellipsis;word-wrap:break-word}.AddButton{border:none;margin-right:.2rem;justify-content:flex-end;align-self:center}.AddButton,.edit{height:2rem;width:2rem}.AddForm{display:flex;flex-direction:column;overflow:hidden;-webkit-animation-duration:.3s;animation-duration:.3s;-webkit-animation-name:lineInsertedIn;animation-name:lineInsertedIn;transition:height .3s}.AddForm input{margin:1rem}.AddForm .button{align-self:center;align-self:flex-end}.AddForm label{align-self:flex-start}th[data-v-aa4cb3dc]{text-align:left}.elipsis[data-v-6b7294ba]{font-size:3rem}.page[data-v-6b7294ba]{border:none;padding:1rem;margin:.2rem}footer[data-v-6b7294ba]{display:flex;padding:.2rem;align-items:center}.searchBox[data-v-559755d8]{display:flex}.search[data-v-559755d8]{display:flex;min-width:95%;margin-top:1rem;margin-bottom:1rem}.searchButton[data-v-559755d8]{color:hsla(0,0%,4%,.8);margin-left:-1.1rem;width:1rem;height:1rem;align-self:center;align-items:center;font-size:.6rem;border:none}select[data-v-1faa4947]{max-width:14.5rem}.dropdown[data-v-1faa4947]{background-color:transparent}i[data-v-1faa4947]:hover{color:#de1656}.dropdown-content[data-v-1faa4947]{position:absolute;background-color:#f8f8f8;min-width:14em;max-width:15rem;max-height:15rem;margin-top:.5rem;border:1px solid #de1656;box-shadow:0 -8px 34px 0 rgba(0,0,0,.05);overflow:auto;z-index:1}.dropdown-input[data-v-1faa4947]{width:14em;margin-right:1em}.dropdown-item[data-v-1faa4947]{color:#000;line-height:1em;text-decoration:none;padding:.5em;display:block;cursor:pointer}main[data-v-68f1643b]{max-width:60.5vw}td input[data-v-68f1643b]{width:14.5rem}@-webkit-keyframes lineInsertedIn-68f1643b{0%{height:0}to{height:23rem}}@keyframes lineInsertedIn-68f1643b{0%{height:0}to{height:23rem}}a[data-v-f3fce040]{text-decoration:none}h2[data-v-f3fce040],p[data-v-f3fce040]{text-align:left}a[data-v-31c3b6b4]{text-decoration:none}h2[data-v-31c3b6b4],p[data-v-31c3b6b4]{text-align:left}main[data-v-590e11e2]{width:80vw}table[data-v-590e11e2]{margin-left:auto;margin-right:auto}.elipsis[data-v-590e11e2]{font-size:3rem}.page[data-v-590e11e2]{border:none;padding:1rem;margin:.2rem}footer[data-v-590e11e2]{display:flex;padding:.2rem;align-items:center}button[data-v-3cf27bac]{margin-top:.5rem}label[data-v-3cf27bac]{margin:.2rem;text-align:left}input[data-v-3cf27bac]{border:none}input[data-v-3cf27bac],input[data-v-3cf27bac]:hover{background-color:rgba(0,0,0,.1)}main[data-v-3cf27bac]{width:20rem;align-self:center;align-content:center}div[data-v-3cf27bac],main[data-v-3cf27bac]{display:flex;flex-direction:column}

BIN
front/dist/favicon.ico vendored

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -1 +0,0 @@
<!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"><link rel="icon" href="/favicon.ico"><script src="https://kit.fontawesome.com/08db035529.js" crossorigin="anonymous"></script><title>Aldon's Home</title><script defer="defer" src="/js/chunk-vendors.6bc46727.js"></script><script defer="defer" src="/js/app.10597b7c.js"></script><link href="/css/app.f7195e87.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but Aldon's Home doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div></body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -85,7 +85,6 @@ body {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
margin-top: 5rem;
} }
.router { .router {

View File

@@ -4,18 +4,18 @@
<button class="page" v-for="index in $store.getters.getBookCurrentPage + 1" :key="index">{{ index }}</button> <button class="page" v-for="index in $store.getters.getBookCurrentPage + 1" :key="index">{{ index }}</button>
</footer> </footer>
<footer v-else> <footer v-else>
<button @click="$store.dispatch('fetchPageBooks', {page: 0, nooption: false})" class="page"> {{ '<<' }} </button> <button @click="$store.dispatch('fetchPageBooks', {page: 0, nooption: false, ...$route.query})" class="page"> {{ '<<' }} </button>
<button <button
class="page" class="page"
:class="{ 'red': $store.getters.getBookCurrentPage - Math.max(0, Math.min($store.getters.getBookCurrentPage - 2, $store.getters.getBookPageNb - 5)) === index}" :class="{ 'red': $store.getters.getBookCurrentPage - Math.max(0, Math.min($store.getters.getBookCurrentPage - 2, $store.getters.getBookPageNb - 5)) === index}"
v-for="(n, index) in (Math.max(5, Math.min($store.getters.getBookCurrentPage + 2, $store.getters.getBookPageNb)) - Math.max(0, Math.min($store.getters.getBookCurrentPage - 2, $store.getters.getBookPageNb - 5)))" v-for="(n, index) in (Math.max(5, Math.min($store.getters.getBookCurrentPage + 2, $store.getters.getBookPageNb)) - Math.max(0, Math.min($store.getters.getBookCurrentPage - 2, $store.getters.getBookPageNb - 5)))"
:key="index" :key="index"
@click="$store.dispatch('fetchPageBooks', {page: Math.max(0, Math.min($store.getters.getBookCurrentPage - 2, $store.getters.getBookPageNb - 5)) + index, nooption: false})"> @click="$store.dispatch('fetchPageBooks', {page: Math.max(0, Math.min($store.getters.getBookCurrentPage - 2, $store.getters.getBookPageNb - 5)) + index, nooption: false, ...$route.query})">
{{ Math.max(0, Math.min($store.getters.getBookCurrentPage - 2, $store.getters.getBookPageNb - 5)) + index }} {{ Math.max(0, Math.min($store.getters.getBookCurrentPage - 2, $store.getters.getBookPageNb - 5)) + index }}
</button> </button>
<p v-if="$store.getters.getBookCurrentPage !== $store.getters.getBookPageNb" class="elipsis">...</p> <p v-if="$store.getters.getBookCurrentPage !== $store.getters.getBookPageNb" class="elipsis">...</p>
<button v-if="$store.getters.getBookCurrentPage !== $store.getters.getBookPageNb" class="page" @click="$store.dispatch('fetchPageBooks', {page: $store.getters.getBookPageNb, nooption: false})">{{ $store.getters.getBookPageNb }}</button> <button v-if="$store.getters.getBookCurrentPage !== $store.getters.getBookPageNb" class="page" @click="$store.dispatch('fetchPageBooks', {page: $store.getters.getBookPageNb, nooption: false, ...$route.query})">{{ $store.getters.getBookPageNb }}</button>
<button @click="$store.dispatch('fetchPageBooks', {page: $store.getters.getBookPageNb, nooption: false})" class="page"> >> </button> <button @click="$store.dispatch('fetchPageBooks', {page: $store.getters.getBookPageNb, nooption: false, ...$route.query})" class="page"> >> </button>
</footer> </footer>
</div> </div>
</template> </template>
@@ -26,7 +26,7 @@
.page { .page {
border: none; border: none;
padding: 1rem; padding: 0.5rem;
margin: 0.2rem; margin: 0.2rem;
} }
@@ -34,6 +34,7 @@ footer {
display: flex; display: flex;
padding: 0.2rem; padding: 0.2rem;
align-items: center; align-items: center;
max-width: 100%;
} }

View File

@@ -5,6 +5,7 @@
</div> </div>
</template> </template>
<script> <script>
import router from '@/router';
export default { export default {
props: { props: {
page: String, page: String,
@@ -12,6 +13,7 @@ export default {
watch: { watch: {
search: function() { search: function() {
this.$emit('changeSearch', this.search); this.$emit('changeSearch', this.search);
router.push({ path: 'books', query: { search: this.search }})
this.$store.dispatch('fetchPage'+this.page, {page: 0, nooption: false, search: this.search, order: this.order}) this.$store.dispatch('fetchPage'+this.page, {page: 0, nooption: false, search: this.search, order: this.order})
} }
}, },
@@ -35,7 +37,7 @@ export default {
.search { .search {
display: flex; display: flex;
min-width: 95%; width: 100vw;
margin-top: 1rem; margin-top: 1rem;
margin-bottom: 1rem; margin-bottom: 1rem;
} }

View File

@@ -1,7 +1,7 @@
<template> <template>
<table> <table>
<tr> <tr>
<th v-bind:style="{ 'min-width': this.width }" v-for="head in headings" :key="head">{{ head }}</th> <th v-for="head in headings" :key="head">{{ head }}</th>
</tr> </tr>
<slot></slot> <slot></slot>
</table> </table>
@@ -19,5 +19,25 @@ export default {
<style scoped> <style scoped>
th { th {
text-align: left; text-align: left;
width: 20%;
} }
tr {
display: flex;
flex-direction: row;
justify-content: flex-start;
}
table {
display: flex;
flex-direction: column;
}
@media (max-width: 30em) {
tr {
width: 100vw;
}
}
</style> </style>

View File

@@ -1,5 +1,4 @@
const ROOT_FQDN = 'https://bookshelf.aldon.fr/api'; const ROOT_FQDN = process.env.VUE_APP_ROOT_FQDN
// const ROOT_FQDN = 'http://localhost:8000/api';
export { export {
ROOT_FQDN ROOT_FQDN

View File

@@ -20,7 +20,7 @@
</button> </button>
</form> </form>
<SearchHeader page="Books" @changeSearch="(s) => {editSearch(s)}"/> <SearchHeader page="Books" @changeSearch="(s) => {editSearch(s)}"/>
<TableView ref="table" :headings="headings" width="15rem"> <TableView ref="table" :headings="headings">
<tr v-for="book in this.$store.getters.getBooks" :key="book"> <tr v-for="book in this.$store.getters.getBooks" :key="book">
<td v-if="editRow === book.biblio_Index"> <td v-if="editRow === book.biblio_Index">
<DropDown <DropDown
@@ -28,7 +28,7 @@
field="Auteur" field="Auteur"
:inputValue="book.Auteur" :inputValue="book.Auteur"
@selected="(n) => {formEdit.author = n}" @selected="(n) => {formEdit.author = n}"
/> />
</td> </td>
<td v-else>{{ book.Auteur }}</td> <td v-else>{{ book.Auteur }}</td>
<td v-if="editRow === book.biblio_Index"> <td v-if="editRow === book.biblio_Index">
@@ -37,7 +37,7 @@
field="Titre" field="Titre"
:inputValue="book.Titre" :inputValue="book.Titre"
@selected="(n) => {formEdit.title = n}" @selected="(n) => {formEdit.title = n}"
/> />
</td> </td>
<td v-else>{{ book.Titre }}</td> <td v-else>{{ book.Titre }}</td>
<td v-if="editRow === book.biblio_Index"> <td v-if="editRow === book.biblio_Index">
@@ -46,7 +46,7 @@
field="Editeur" field="Editeur"
:inputValue="book.Editeur" :inputValue="book.Editeur"
@selected="(n) => {formEdit.editor = n}" @selected="(n) => {formEdit.editor = n}"
/> />
</td> </td>
<td v-else>{{ book.Editeur }}</td> <td v-else>{{ book.Editeur }}</td>
<td v-if="editRow === book.biblio_Index"> <td v-if="editRow === book.biblio_Index">
@@ -55,7 +55,7 @@
field="Type" field="Type"
:inputValue="book.Type" :inputValue="book.Type"
@selected="(n) => {formEdit.type = n}" @selected="(n) => {formEdit.type = n}"
/> />
</td> </td>
<td v-else>{{ book.Type }}</td> <td v-else>{{ book.Type }}</td>
<div class="button-panel"> <div class="button-panel">
@@ -87,7 +87,7 @@ export default {
}, },
data() { data() {
return { return {
headings: ['Author', 'Title', 'Editor', 'Type'], headings: ['Author', 'Title', 'Editor', 'Type', 'Control'],
editMode: false, editMode: false,
editRow: undefined, editRow: undefined,
addMode: false, addMode: false,
@@ -188,11 +188,45 @@ export default {
</script> </script>
<style scoped> <style scoped>
main { main {
max-width: 60.5vw; width: 80vw
} }
td input { header {
width: 14.5rem; width: 80vw;
}
tr {
display: flex;
flex-direction: row;
justify-content: flex-start;
width: 80vw;
align-items: center;
}
td {
width: 20%;
}
.button-panel {
width: 20%;
display: flex;
flex-direction: flex-end;
}
@media (max-width: 30em) {
main {
width: 100vw;
}
tr {
width: 100vw;
}
td {
overflow-x: hidden;
white-space: nowrap
}
} }
@keyframes lineInsertedIn { @keyframes lineInsertedIn {