Split the uncomfortably big admin view into smaller admin views

This commit is contained in:
Gregoo 2025-01-24 10:09:12 +01:00
parent e2bcd61ace
commit 798226932f
19 changed files with 472 additions and 353 deletions

View File

@ -12,7 +12,13 @@ from bricktracker.navbar import Navbar
from bricktracker.sql import close
from bricktracker.version import __version__
from bricktracker.views.add import add_page
from bricktracker.views.admin import admin_page
from bricktracker.views.admin.admin import admin_page
from bricktracker.views.admin.checkbox import admin_checkbox_page
from bricktracker.views.admin.database import admin_database_page
from bricktracker.views.admin.image import admin_image_page
from bricktracker.views.admin.instructions import admin_instructions_page
from bricktracker.views.admin.retired import admin_retired_page
from bricktracker.views.admin.theme import admin_theme_page
from bricktracker.views.error import error_404
from bricktracker.views.index import index_page
from bricktracker.views.instructions import instructions_page
@ -60,9 +66,8 @@ def setup_app(app: Flask) -> None:
# Register errors
app.register_error_handler(404, error_404)
# Register routes
# Register app routes
app.register_blueprint(add_page)
app.register_blueprint(admin_page)
app.register_blueprint(index_page)
app.register_blueprint(instructions_page)
app.register_blueprint(login_page)
@ -71,6 +76,15 @@ def setup_app(app: Flask) -> None:
app.register_blueprint(set_page)
app.register_blueprint(wish_page)
# Register admin routes
app.register_blueprint(admin_page)
app.register_blueprint(admin_checkbox_page)
app.register_blueprint(admin_database_page)
app.register_blueprint(admin_image_page)
app.register_blueprint(admin_instructions_page)
app.register_blueprint(admin_retired_page)
app.register_blueprint(admin_theme_page)
# An helper to make global variables available to the
# request
@app.before_request

19
bricktracker/reload.py Normal file
View File

@ -0,0 +1,19 @@
from .instructions_list import BrickInstructionsList
from .retired_list import BrickRetiredList
from .theme_list import BrickThemeList
# Reload everything related to a database after an operation
def reload() -> None:
# Failsafe
try:
# Reload the instructions
BrickInstructionsList(force=True)
# Reload retired sets
BrickRetiredList(force=True)
# Reload themes
BrickThemeList(force=True)
except Exception:
pass

View File

@ -1,334 +0,0 @@
from datetime import datetime
import logging
import os
from flask import (
Blueprint,
current_app,
g,
redirect,
request,
render_template,
send_file,
url_for,
)
from flask_login import login_required
from werkzeug.wrappers.response import Response
from ..configuration_list import BrickConfigurationList
from .exceptions import exception_handler
from ..instructions_list import BrickInstructionsList
from ..minifigure import BrickMinifigure
from ..part import BrickPart
from ..rebrickable_image import RebrickableImage
from ..retired_list import BrickRetiredList
from ..set import BrickSet
from ..sql_counter import BrickCounter
from ..sql_migration_list import BrickSQLMigrationList
from ..sql import BrickSQL
from ..theme_list import BrickThemeList
from .upload import upload_helper
logger = logging.getLogger(__name__)
admin_page = Blueprint('admin', __name__, url_prefix='/admin')
# Admin
@admin_page.route('/', methods=['GET'])
@login_required
@exception_handler(__file__)
def admin() -> str:
database_counters: list[BrickCounter] = []
database_exception: Exception | None = None
database_upgrade_needed: bool = False
database_version: int = -1
nil_minifigure_name: str = ''
nil_minifigure_url: str = ''
nil_part_name: str = ''
nil_part_url: str = ''
# This view needs to be protected against SQL errors
try:
database = BrickSQL(failsafe=True)
database_upgrade_needed = database.upgrade_needed()
database_version = database.version
if not database_upgrade_needed:
database_counters = BrickSQL().count_records()
except Exception as e:
database_exception = e
# Warning
logger.warning('A database exception occured while loading the admin page: {exception}'.format( # noqa: E501
exception=str(e),
))
nil_minifigure_name = RebrickableImage.nil_minifigure_name()
nil_minifigure_url = RebrickableImage.static_url(
nil_minifigure_name,
'MINIFIGURES_FOLDER'
)
nil_part_name = RebrickableImage.nil_name()
nil_part_url = RebrickableImage.static_url(
nil_part_name,
'PARTS_FOLDER'
)
open_image = request.args.get('open_image', None)
open_instructions = request.args.get('open_instructions', None)
open_logout = request.args.get('open_logout', None)
open_retired = request.args.get('open_retired', None)
open_theme = request.args.get('open_theme', None)
open_database = (
open_image is None and
open_instructions is None and
open_logout is None and
open_retired is None and
open_theme is None
)
return render_template(
'admin.html',
configuration=BrickConfigurationList.list(),
database_counters=database_counters,
database_error=request.args.get('error'),
database_exception=database_exception,
database_upgrade_needed=database_upgrade_needed,
database_version=database_version,
instructions=BrickInstructionsList(),
nil_minifigure_name=nil_minifigure_name,
nil_minifigure_url=nil_minifigure_url,
nil_part_name=nil_part_name,
nil_part_url=nil_part_url,
open_database=open_database,
open_image=open_image,
open_instructions=open_instructions,
open_logout=open_logout,
open_retired=open_retired,
open_theme=open_theme,
retired=BrickRetiredList(),
theme=BrickThemeList(),
)
# Delete the database
@admin_page.route('/delete-database', methods=['GET'])
@login_required
@exception_handler(__file__)
def delete_database() -> str:
return render_template(
'admin.html',
delete_database=True,
error=request.args.get('error')
)
# Actually delete the database
@admin_page.route('/delete-database', methods=['POST'])
@login_required
@exception_handler(__file__, post_redirect='admin.delete_database')
def do_delete_database() -> Response:
BrickSQL.delete()
# Reload the instructions
BrickInstructionsList(force=True)
return redirect(url_for('admin.admin'))
# Download the database
@admin_page.route('/download-database', methods=['GET'])
@login_required
@exception_handler(__file__)
def download_database() -> Response:
# Create a file name with a timestamp embedded
name, extension = os.path.splitext(
os.path.basename(current_app.config['DATABASE_PATH'])
)
# Info
logger.info('The database has been downloaded')
return send_file(
current_app.config['DATABASE_PATH'],
as_attachment=True,
download_name='{name}-v{version}-{timestamp}{extension}'.format(
name=name,
version=BrickSQL(failsafe=True).version,
timestamp=datetime.now().astimezone(g.timezone).strftime(
current_app.config['DATABASE_TIMESTAMP_FORMAT']
),
extension=extension
)
)
# Drop the database
@admin_page.route('/drop-database', methods=['GET'])
@login_required
@exception_handler(__file__)
def drop_database() -> str:
return render_template(
'admin.html',
drop_database=True,
error=request.args.get('error')
)
# Actually drop the database
@admin_page.route('/drop-database', methods=['POST'])
@login_required
@exception_handler(__file__, post_redirect='admin.drop_database')
def do_drop_database() -> Response:
BrickSQL.drop()
# Reload the instructions
BrickInstructionsList(force=True)
return redirect(url_for('admin.admin'))
# Actually upgrade the database
@admin_page.route('/upgrade-database', methods=['POST'])
@login_required
@exception_handler(__file__, post_redirect='admin.upgrade_database')
def do_upgrade_database() -> Response:
BrickSQL(failsafe=True).upgrade()
return redirect(url_for('admin.admin'))
# Import a database
@admin_page.route('/import-database', methods=['GET'])
@login_required
@exception_handler(__file__)
def import_database() -> str:
return render_template(
'admin.html',
import_database=True,
error=request.args.get('error')
)
# Actually import a database
@admin_page.route('/import-database', methods=['POST'])
@login_required
@exception_handler(__file__, post_redirect='admin.import_database')
def do_import_database() -> Response:
file = upload_helper(
'database',
'admin.import_database',
extensions=['.db'],
)
if isinstance(file, Response):
return file
BrickSQL.upload(file)
# Reload the instructions
BrickInstructionsList(force=True)
return redirect(url_for('admin.admin'))
# Refresh the instructions cache
@admin_page.route('/refresh-instructions', methods=['GET'])
@login_required
@exception_handler(__file__)
def refresh_instructions() -> Response:
BrickInstructionsList(force=True)
return redirect(url_for('admin.admin', open_instructions=True))
# Refresh the retired sets cache
@admin_page.route('/refresh-retired', methods=['GET'])
@login_required
@exception_handler(__file__)
def refresh_retired() -> Response:
BrickRetiredList(force=True)
return redirect(url_for('admin.admin', open_retired=True))
# Refresh the themes cache
@admin_page.route('/refresh-themes', methods=['GET'])
@login_required
@exception_handler(__file__)
def refresh_themes() -> Response:
BrickThemeList(force=True)
return redirect(url_for('admin.admin', open_theme=True))
# Update the default images
@admin_page.route('/update-image', methods=['GET'])
@login_required
@exception_handler(__file__)
def update_image() -> Response:
# Abusing the object to create a 'nil' minifigure
RebrickableImage(
BrickSet(),
minifigure=BrickMinifigure(record={
'set_img_url': None,
})
).download()
# Abusing the object to create a 'nil' part
RebrickableImage(
BrickSet(),
part=BrickPart(record={
'part_img_url': None,
'part_img_url_id': None
})
).download()
return redirect(url_for('admin.admin', open_image=True))
# Update the themes file
@admin_page.route('/update-retired', methods=['GET'])
@login_required
@exception_handler(__file__)
def update_retired() -> Response:
BrickRetiredList().update()
BrickRetiredList(force=True)
return redirect(url_for('admin.admin', open_retired=True))
# Update the themes file
@admin_page.route('/update-themes', methods=['GET'])
@login_required
@exception_handler(__file__)
def update_themes() -> Response:
BrickThemeList().update()
BrickThemeList(force=True)
return redirect(url_for('admin.admin', open_theme=True))
# Upgrade the database
@admin_page.route('/upgrade-database', methods=['GET'])
@login_required
@exception_handler(__file__, post_redirect='admin.admin')
def upgrade_database() -> str | Response:
database = BrickSQL(failsafe=True)
if not database.upgrade_needed():
return redirect(url_for('admin.admin'))
return render_template(
'admin.html',
upgrade_database=True,
migrations=BrickSQLMigrationList().pending(
database.version
),
error=request.args.get('error')
)

View File

View File

@ -0,0 +1,104 @@
import logging
from flask import Blueprint, request, render_template
from flask_login import login_required
from ...configuration_list import BrickConfigurationList
from ..exceptions import exception_handler
from ...instructions_list import BrickInstructionsList
from ...rebrickable_image import RebrickableImage
from ...retired_list import BrickRetiredList
from ...set_checkbox import BrickSetCheckbox
from ...set_checkbox_list import BrickSetCheckboxList
from ...sql_counter import BrickCounter
from ...sql import BrickSQL
from ...theme_list import BrickThemeList
logger = logging.getLogger(__name__)
admin_page = Blueprint('admin', __name__, url_prefix='/admin')
# Admin
@admin_page.route('/', methods=['GET'])
@login_required
@exception_handler(__file__)
def admin() -> str:
brickset_checkboxes: list[BrickSetCheckbox] = []
database_counters: list[BrickCounter] = []
database_exception: Exception | None = None
database_upgrade_needed: bool = False
database_version: int = -1
nil_minifigure_name: str = ''
nil_minifigure_url: str = ''
nil_part_name: str = ''
nil_part_url: str = ''
# This view needs to be protected against SQL errors
try:
database = BrickSQL(failsafe=True)
database_upgrade_needed = database.upgrade_needed()
database_version = database.version
database_counters = BrickSQL().count_records()
brickset_checkboxes = BrickSetCheckboxList().list(all=True)
except Exception as e:
database_exception = e
# Warning
logger.warning('A database exception occured while loading the admin page: {exception}'.format( # noqa: E501
exception=str(e),
))
nil_minifigure_name = RebrickableImage.nil_minifigure_name()
nil_minifigure_url = RebrickableImage.static_url(
nil_minifigure_name,
'MINIFIGURES_FOLDER'
)
nil_part_name = RebrickableImage.nil_name()
nil_part_url = RebrickableImage.static_url(
nil_part_name,
'PARTS_FOLDER'
)
open_checkbox = request.args.get('open_checkbox', None)
open_image = request.args.get('open_image', None)
open_instructions = request.args.get('open_instructions', None)
open_logout = request.args.get('open_logout', None)
open_retired = request.args.get('open_retired', None)
open_theme = request.args.get('open_theme', None)
open_database = (
open_checkbox is None and
open_image is None and
open_instructions is None and
open_logout is None and
open_retired is None and
open_theme is None
)
return render_template(
'admin.html',
configuration=BrickConfigurationList.list(),
brickset_checkboxes=brickset_checkboxes,
database_counters=database_counters,
database_error=request.args.get('error'),
database_exception=database_exception,
database_upgrade_needed=database_upgrade_needed,
database_version=database_version,
instructions=BrickInstructionsList(),
nil_minifigure_name=nil_minifigure_name,
nil_minifigure_url=nil_minifigure_url,
nil_part_name=nil_part_name,
nil_part_url=nil_part_url,
open_checkbox=open_checkbox,
open_database=open_database,
open_image=open_image,
open_instructions=open_instructions,
open_logout=open_logout,
open_retired=open_retired,
open_theme=open_theme,
retired=BrickRetiredList(),
theme=BrickThemeList(),
)

View File

@ -0,0 +1,170 @@
from datetime import datetime
import logging
import os
from flask import (
Blueprint,
current_app,
g,
redirect,
request,
render_template,
send_file,
url_for,
)
from flask_login import login_required
from werkzeug.wrappers.response import Response
from ..exceptions import exception_handler
from ...reload import reload
from ...sql_migration_list import BrickSQLMigrationList
from ...sql import BrickSQL
from ..upload import upload_helper
logger = logging.getLogger(__name__)
admin_database_page = Blueprint(
'admin_database',
__name__,
url_prefix='/admin/database'
)
# Delete the database
@admin_database_page.route('/delete', methods=['GET'])
@login_required
@exception_handler(__file__)
def delete() -> str:
return render_template(
'admin.html',
delete_database=True,
error=request.args.get('error')
)
# Actually delete the database
@admin_database_page.route('/delete', methods=['POST'])
@login_required
@exception_handler(__file__, post_redirect='admin_database.delete')
def do_delete() -> Response:
BrickSQL.delete()
reload()
return redirect(url_for('admin.admin'))
# Download the database
@admin_database_page.route('/download', methods=['GET'])
@login_required
@exception_handler(__file__)
def download() -> Response:
# Create a file name with a timestamp embedded
name, extension = os.path.splitext(
os.path.basename(current_app.config['DATABASE_PATH'])
)
# Info
logger.info('The database has been downloaded')
return send_file(
current_app.config['DATABASE_PATH'],
as_attachment=True,
download_name='{name}-v{version}-{timestamp}{extension}'.format(
name=name,
version=BrickSQL(failsafe=True).version,
timestamp=datetime.now().astimezone(g.timezone).strftime(
current_app.config['DATABASE_TIMESTAMP_FORMAT']
),
extension=extension
)
)
# Drop the database
@admin_database_page.route('/drop', methods=['GET'])
@login_required
@exception_handler(__file__)
def drop() -> str:
return render_template(
'admin.html',
drop_database=True,
error=request.args.get('error')
)
# Actually drop the database
@admin_database_page.route('/drop', methods=['POST'])
@login_required
@exception_handler(__file__, post_redirect='admin_database.drop')
def do_drop() -> Response:
BrickSQL.drop()
reload()
return redirect(url_for('admin.admin'))
# Actually upgrade the database
@admin_database_page.route('/upgrade', methods=['POST'])
@login_required
@exception_handler(__file__, post_redirect='admin_database.upgrade')
def do_upgrade() -> Response:
BrickSQL(failsafe=True).upgrade()
reload()
return redirect(url_for('admin.admin'))
# Import a database
@admin_database_page.route('/import', methods=['GET'])
@login_required
@exception_handler(__file__)
def upload() -> str:
return render_template(
'admin.html',
import_database=True,
error=request.args.get('error')
)
# Actually import a database
@admin_database_page.route('/import', methods=['POST'])
@login_required
@exception_handler(__file__, post_redirect='admin_database.upload')
def do_upload() -> Response:
file = upload_helper(
'database',
'admin_database.upload',
extensions=['.db'],
)
if isinstance(file, Response):
return file
BrickSQL.upload(file)
reload()
return redirect(url_for('admin.admin'))
# Upgrade the database
@admin_database_page.route('/upgrade', methods=['GET'])
@login_required
@exception_handler(__file__, post_redirect='admin.admin')
def upgrade() -> str | Response:
database = BrickSQL(failsafe=True)
if not database.upgrade_needed():
return redirect(url_for('admin.admin'))
return render_template(
'admin.html',
upgrade_database=True,
migrations=BrickSQLMigrationList().pending(
database.version
),
error=request.args.get('error')
)

View File

@ -0,0 +1,44 @@
import logging
from flask import Blueprint, redirect, url_for
from flask_login import login_required
from werkzeug.wrappers.response import Response
from ..exceptions import exception_handler
from ...minifigure import BrickMinifigure
from ...part import BrickPart
from ...rebrickable_image import RebrickableImage
from ...set import BrickSet
logger = logging.getLogger(__name__)
admin_image_page = Blueprint(
'admin_image',
__name__,
url_prefix='/admin/image'
)
# Update the default images
@admin_image_page.route('/update', methods=['GET'])
@login_required
@exception_handler(__file__)
def update() -> Response:
# Abusing the object to create a 'nil' minifigure
RebrickableImage(
BrickSet(),
minifigure=BrickMinifigure(record={
'set_img_url': None,
})
).download()
# Abusing the object to create a 'nil' part
RebrickableImage(
BrickSet(),
part=BrickPart(record={
'part_img_url': None,
'part_img_url_id': None
})
).download()
return redirect(url_for('admin.admin', open_image=True))

View File

@ -0,0 +1,26 @@
import logging
from flask import Blueprint, redirect, url_for
from flask_login import login_required
from werkzeug.wrappers.response import Response
from ..exceptions import exception_handler
from ...instructions_list import BrickInstructionsList
logger = logging.getLogger(__name__)
admin_instructions_page = Blueprint(
'admin_instructions',
__name__,
url_prefix='/admin/instructions'
)
# Refresh the instructions cache
@admin_instructions_page.route('/refresh', methods=['GET'])
@login_required
@exception_handler(__file__)
def refresh() -> Response:
BrickInstructionsList(force=True)
return redirect(url_for('admin.admin', open_instructions=True))

View File

@ -0,0 +1,38 @@
import logging
from flask import Blueprint, redirect, url_for
from flask_login import login_required
from werkzeug.wrappers.response import Response
from ..exceptions import exception_handler
from ...retired_list import BrickRetiredList
logger = logging.getLogger(__name__)
admin_retired_page = Blueprint(
'admin_retired',
__name__,
url_prefix='/admin/retired'
)
# Refresh the retired sets cache
@admin_retired_page.route('/refresh', methods=['GET'])
@login_required
@exception_handler(__file__)
def refresh() -> Response:
BrickRetiredList(force=True)
return redirect(url_for('admin.admin', open_retired=True))
# Update the retired sets
@admin_retired_page.route('/update', methods=['GET'])
@login_required
@exception_handler(__file__)
def update() -> Response:
BrickRetiredList().update()
BrickRetiredList(force=True)
return redirect(url_for('admin.admin', open_retired=True))

View File

@ -0,0 +1,38 @@
import logging
from flask import Blueprint, redirect, url_for
from flask_login import login_required
from werkzeug.wrappers.response import Response
from ..exceptions import exception_handler
from ...theme_list import BrickThemeList
logger = logging.getLogger(__name__)
admin_theme_page = Blueprint(
'admin_theme',
__name__,
url_prefix='/admin/theme'
)
# Refresh the themes cache
@admin_theme_page.route('/refresh', methods=['GET'])
@login_required
@exception_handler(__file__)
def refresh() -> Response:
BrickThemeList(force=True)
return redirect(url_for('admin.admin', open_theme=True))
# Update the themes file
@admin_theme_page.route('/update', methods=['GET'])
@login_required
@exception_handler(__file__)
def update() -> Response:
BrickThemeList().update()
BrickThemeList(force=True)
return redirect(url_for('admin.admin', open_theme=True))

View File

@ -9,13 +9,13 @@
<p>Your database needs to be upgraded.</p>
<hr>
<div class="text-end">
<a href="{{ url_for('admin.upgrade_database') }}" class="btn btn-warning" role="button"><i class="ri-arrow-up-double-line"></i> Upgrade the database</a>
<a href="{{ url_for('admin_database.upgrade') }}" class="btn btn-warning" role="button"><i class="ri-arrow-up-double-line"></i> Upgrade the database</a>
</div>
</div>
{% endif %}
<p>The database file is: <code>{{ config['DATABASE_PATH'] }}</code> at version <span class="badge rounded-pill text-bg-light border fw-normal"><i class="ri-hashtag"></i>{{ database_version }}</span></p>
<p>
<a href="{{ url_for('admin.download_database') }}" class="btn btn-primary" role="button"><i class="ri-download-line"></i> Download the database file</a>
<a href="{{ url_for('admin_database.download') }}" class="btn btn-primary" role="button"><i class="ri-download-line"></i> Download the database file</a>
</p>
{% if database_counters %}
<h5 class="border-bottom">Records</h5>
@ -37,7 +37,7 @@
{{ accordion.header('Database danger zone', 'database-danger', 'admin', danger=true, class='text-end') }}
{% if error %}<div class="alert alert-danger text-start" role="alert"><strong>Error:</strong> {{ error }}.</div>{% endif %}
<a href="{{ url_for('admin.import_database') }}" class="btn btn-warning" role="button"><i class="ri-upload-line"></i> Import a database file</a>
<a href="{{ url_for('admin.drop_database') }}" class="btn btn-danger" role="button"><i class="ri-close-line"></i> Drop the database</a>
<a href="{{ url_for('admin.delete_database') }}" class="btn btn-danger" role="button"><i class="ri-delete-bin-2-line"></i> Delete the database file</a>
<a href="{{ url_for('admin_database.upload') }}" class="btn btn-warning" role="button"><i class="ri-upload-line"></i> Import a database file</a>
<a href="{{ url_for('admin_database.drop') }}" class="btn btn-danger" role="button"><i class="ri-close-line"></i> Drop the database</a>
<a href="{{ url_for('admin_database.delete') }}" class="btn btn-danger" role="button"><i class="ri-delete-bin-2-line"></i> Delete the database file</a>
{{ accordion.footer() }}

View File

@ -1,7 +1,7 @@
{% import 'macro/accordion.html' as accordion %}
{{ accordion.header('Database danger zone', 'database-danger', 'admin', expanded=true, danger=true, class='text-end') }}
<form action="{{ url_for('admin.do_delete_database') }}" method="post">
<form action="{{ url_for('admin_database.do_delete') }}" method="post">
{% if error %}<div class="alert alert-danger text-start" role="alert"><strong>Error:</strong> {{ error }}.</div>{% endif %}
<div class="alert alert-danger text-center" role="alert">You are about to <strong>delete the database file</strong>. This action is irreversible.</div>
<a class="btn btn-danger" href="{{ url_for('admin.admin') }}" role="button"><i class="ri-arrow-left-long-line"></i> Back to the admin</a>

View File

@ -1,7 +1,7 @@
{% import 'macro/accordion.html' as accordion %}
{{ accordion.header('Database danger zone', 'database-danger', 'admin', expanded=true, danger=true, class='text-end') }}
<form action="{{ url_for('admin.do_drop_database') }}" method="post">
<form action="{{ url_for('admin_database.do_drop') }}" method="post">
{% if error %}<div class="alert alert-danger text-start" role="alert"><strong>Error:</strong> {{ error }}.</div>{% endif %}
<div class="alert alert-danger text-center" role="alert">You are about to <strong>drop all the tables from the database</strong>. This action is irreversible.</div>
<a class="btn btn-danger" href="{{ url_for('admin.admin') }}" role="button"><i class="ri-arrow-left-long-line"></i> Back to the admin</a>

View File

@ -1,7 +1,7 @@
{% import 'macro/accordion.html' as accordion %}
{{ accordion.header('Database danger zone', 'database-danger', 'admin', expanded=true, danger=true) }}
<form action="{{ url_for('admin.do_import_database') }}" method="post" enctype="multipart/form-data">
<form action="{{ url_for('admin_database.do_upload') }}" method="post" enctype="multipart/form-data">
{% if error %}<div class="alert alert-danger text-start" role="alert"><strong>Error:</strong> {{ error }}.</div>{% endif %}
<div class="alert alert-warning text-center" role="alert">You are about to <strong>import a database file</strong>. This will replace the <strong>whole content</strong> of the database. This action is irreversible.</div>
<div class="mb-3">

View File

@ -1,7 +1,7 @@
{% import 'macro/accordion.html' as accordion %}
{{ accordion.header('Database', 'database', 'admin', expanded=true, icon='database-2-line') }}
<form action="{{ url_for('admin.do_upgrade_database') }}" method="post">
<form action="{{ url_for('admin_database.do_upgrade') }}" method="post">
{% if error %}<div class="alert alert-danger text-start" role="alert"><strong>Error:</strong> {{ error }}.</div>{% endif %}
<div class="alert alert-warning text-center" role="alert">
You are about to <strong>upgrade your database file</strong>. This action is irreversible.<br>
@ -22,7 +22,7 @@
</ul>
<div class="text-end">
<a class="btn btn-danger" href="{{ url_for('admin.admin') }}" role="button"><i class="ri-arrow-left-long-line"></i> Back to the admin</a>
<a href="{{ url_for('admin.download_database') }}" class="btn btn-primary" role="button"><i class="ri-download-line"></i> Download the database file</a>
<a href="{{ url_for('admin_database.download') }}" class="btn btn-primary" role="button"><i class="ri-download-line"></i> Download the database file</a>
<button type="submit" class="btn btn-warning"><i class="ri-arrow-up-double-line"></i> Upgrade the database</button>
</div>
</form>

View File

@ -9,6 +9,6 @@
<img class="table-img border mx-1" src="{{ nil_part_url }}" alt="{{ nil_part_name }}">
</div>
<p>
<a href="{{ url_for('admin.update_image') }}" class="btn btn-primary" role="button"><i class="ri-download-line"></i> Update the images</a>
<a href="{{ url_for('admin_image.update') }}" class="btn btn-primary" role="button"><i class="ri-download-line"></i> Update the images</a>
</p>
{{ accordion.footer() }}

View File

@ -27,6 +27,6 @@
</p>
<h5 class="border-bottom">Refresh</h5>
<p>
<a href="{{ url_for('admin.refresh_instructions') }}" class="btn btn-primary" role="button"><i class="ri-refresh-line"></i> Refresh the instructions cache</a>
<a href="{{ url_for('admin_instructions.refresh') }}" class="btn btn-primary" role="button"><i class="ri-refresh-line"></i> Refresh the instructions cache</a>
</p>
{{ accordion.footer() }}

View File

@ -20,7 +20,7 @@
</p>
<h5 class="border-bottom">Refresh</h5>
<p>
<a href="{{ url_for('admin.refresh_retired') }}" class="btn btn-primary" role="button"><i class="ri-refresh-line"></i> Refresh the retired sets cache</a>
<a href="{{ url_for('admin.update_retired') }}" class="btn btn-primary" role="button"><i class="ri-download-line"></i> Update the retired sets file</a>
<a href="{{ url_for('admin_retired.refresh') }}" class="btn btn-primary" role="button"><i class="ri-refresh-line"></i> Refresh the retired sets cache</a>
<a href="{{ url_for('admin_retired.update') }}" class="btn btn-primary" role="button"><i class="ri-download-line"></i> Update the retired sets file</a>
</p>
{{ accordion.footer() }}

View File

@ -20,7 +20,7 @@
</p>
<h5 class="border-bottom">Refresh</h5>
<p>
<a href="{{ url_for('admin.refresh_themes') }}" class="btn btn-primary" role="button"><i class="ri-refresh-line"></i> Refresh the themes cache</a>
<a href="{{ url_for('admin.update_themes') }}" class="btn btn-primary" role="button"><i class="ri-download-line"></i> Update the themes file</a>
<a href="{{ url_for('admin_theme.refresh') }}" class="btn btn-primary" role="button"><i class="ri-refresh-line"></i> Refresh the themes cache</a>
<a href="{{ url_for('admin_theme.update') }}" class="btn btn-primary" role="button"><i class="ri-download-line"></i> Update the themes file</a>
</p>
{{ accordion.footer() }}