-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
[Theme] New theme: FomanticUI [beta] #2643
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
princerb
wants to merge
45
commits into
pallets-eco:master
Choose a base branch
from
princerb:f-theme-fomanticui
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
45 commits
Select commit
Hold shift + click to select a range
39c3933
New theme added to theme.py: FomanticUI
princerb 9c4b4e4
Merge branch 'master' into f-theme-fomanticui
princerb 3fa4e1f
Merge branch 'master' of https://github.com/princerb/flask-admin into…
princerb 63af33b
Merge branch 'master' of https://github.com/princerb/flask-admin into…
princerb c05a77a
Theme FomanticUI templates: add index.html
princerb cf2113b
Theme FomanticUI templates: add master.html
princerb 82c22a8
Theme FomanticUI templates: add static.html
princerb 490f13c
Theme FomanticUI templates: add layout.html
princerb cc40388
Theme FomanticUI templates: add actions.html
princerb ea2ea43
Theme FomanticUI: add actions.js
princerb 29c701f
Theme FomanticUI templates: add model/layout.html
princerb 7b7891c
Theme FomanticUI templates: add model/list.html
princerb dad7c54
Theme FomanticUI templates: add inline_form.html
princerb 331b7be
Theme FomanticUI templates: add inline_list_base.html
princerb 0fa05bd
Theme FomanticUI templates: add row_actions.html
princerb e6a23a1
Add type="button" to Inline Field adder button element & format doc
princerb e83306e
Theme FomanticUI: add RedisCLI templates
princerb dd70021
Theme FomanticUI: add initial base.html for testing
princerb 8debbf6
Refactor list template: remove inline styles, improve modal handling,…
princerb 0f316c5
Update row actions: add modal support and fix icon classes
princerb 6ebf954
Add FomanticUI admin library with form rendering and pagination macros
princerb 03f360e
Add breadcrumb utility for admin navigation
princerb 08eab7f
Add footer template with menu-based navigation links
princerb ca9b4fe
Add modal templates for create, edit, and details views
princerb 1eaa8e8
Add modal functionality for form handling
princerb 7a01d61
Add model CRUD templates for create, edit, and details views
princerb 0c12609
Add file management templates with modal support
princerb accfd07
Add helper functions for confirm dialogs and UI components
princerb a83c6cb
Add sidebar toggle functionality with localStorage persistence
princerb 65e8855
Add advanced filtering functionality for admin lists
princerb 113c3f7
Add form handling and validation JavaScript
princerb 51949db
Add details view filtering functionality
princerb cb1a3cc
FomanticUI: Add Redis CLI interface JavaScript
princerb 7f0ab13
Theme FomanticUI: main CSS styles for admin interface - FomanticUI
princerb e2ab9b4
Show loading state during modal form request
princerb a51a6ac
Show loading state from beginning & apply _gettext for button texts
princerb f3804bc
Replace is_modal param with modal_action_type for getting action name…
princerb 7dc581b
Remove bs4-editable script/style includings from lib.html
princerb 4fd36ec
Add an optimal and universal max-width to admin forms
princerb d91c8a0
Remove unnecessary cases and comments from form.js
princerb 411f643
Merge branch 'master' into f-theme-fomanticui
princerb 56a839d
Merge branch 'master' into f-theme-fomanticui
princerb 6e4e93f
Add an example fomanticui-test
princerb de7c988
Add an example fomanticui [Refactor mypy test]
princerb f5b9ce3
Merge branch 'master' into f-theme-fomanticui
princerb File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| 3.10 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| # FomanticUI Theme | ||
| This example contains different types of implementations of each section/feature of flask-admin to test FomanticUI theme. Can also be used to test other upcoming new themes. | ||
|
|
||
| ## How to run this example | ||
|
|
||
| Clone the repository and navigate to this example: | ||
|
|
||
| ```shell | ||
| git clone https://github.com/pallets-eco/flask-admin.git | ||
| cd flask-admin/examples/fomanticui | ||
| ``` | ||
|
|
||
| > This example uses [`uv`](https://docs.astral.sh/uv/) to manage its dependencies and developer environment. | ||
|
|
||
| Run the example using `uv`, which will manage the environment and dependencies automatically: | ||
|
|
||
| ```shell | ||
| uv run main.py | ||
| ``` |
Empty file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| from flask import Blueprint | ||
| from flask import redirect | ||
| from flask import render_template_string | ||
| from flask import url_for | ||
|
|
||
| bp = Blueprint("main", __name__, template_folder="my_admin/templates") | ||
|
|
||
|
|
||
| @bp.route("/") | ||
| def index(): | ||
| return render_template_string(""" | ||
| <h1>Hello, World!</h1> | ||
| <ul> | ||
| <li> | ||
| <a href="{{ url_for('basicadmin.index') }}"> | ||
| Basic Admin Panel | ||
| </a> | ||
| </li> | ||
| <li> | ||
| <a href="{{ url_for('superadmin.index') }}"> | ||
| Super Admin Panel | ||
| </a> | ||
| </li> | ||
| <li> | ||
| <a href="{{ url_for('proadmin.index') }}"> | ||
| Pro Admin Panel | ||
| </a> | ||
| </li> | ||
| </ul> | ||
| """) | ||
|
|
||
|
|
||
| @bp.app_errorhandler(404) | ||
| def not_found(error): | ||
| return redirect(url_for("main.index")) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| import os | ||
|
|
||
|
|
||
| class MyConfig: | ||
| DEBUG = True | ||
| SECRET_KEY = os.environ.get("SECRET_KEY", os.urandom(16)) | ||
| DATABASE_URL = os.environ.get("DATABASE_URL", "sqlite:///sqlite3-fomanticui.sqlite") | ||
| SQLALCHEMY_DATABASE_URI = DATABASE_URL | ||
| SQLALCHEMY_TRACK_MODIFICATIONS = False |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,272 @@ | ||
| from random import choice | ||
| from random import randint | ||
|
|
||
| from faker import Faker | ||
| from models import Address | ||
| from models import Category | ||
| from models import Comment | ||
| from models import db | ||
| from models import Department | ||
| from models import Employee | ||
| from models import Order | ||
| from models import OrderItem | ||
| from models import OrderStatus | ||
| from models import Payment | ||
| from models import PaymentMethod | ||
| from models import Post | ||
| from models import Product | ||
| from models import Profile | ||
| from models import Project | ||
| from models import Role | ||
| from models import Tag | ||
| from models import User | ||
|
|
||
| fake = Faker() | ||
|
|
||
|
|
||
| def generate_fake_data(app): | ||
| """Populate the database with a rich set of example data covering all models.""" | ||
| with app.app_context(): | ||
| db.create_all() | ||
| db.create_all() | ||
| # ----- Roles ----- | ||
| if Role.query.count() == 0: | ||
| for name in ("admin", "editor", "user"): | ||
| db.session.add(Role(name=name)) | ||
| db.session.commit() | ||
|
|
||
| roles = Role.query.all() | ||
|
|
||
| # ----- Categories ----- | ||
| while Category.query.count() < 5: | ||
| db.session.add( | ||
| Category(name=fake.unique.word(), description=fake.sentence()) | ||
| ) | ||
| db.session.commit() | ||
| categories = Category.query.all() | ||
|
|
||
| # ----- Tags ----- | ||
| while Tag.query.count() < 10: | ||
| db.session.add(Tag(name=fake.unique.word())) | ||
| db.session.commit() | ||
| tags = Tag.query.all() | ||
|
|
||
| # ----- Products ----- | ||
| while Product.query.count() < 20: | ||
| db.session.add( | ||
| Product( | ||
| name=fake.unique.word().title(), | ||
| description=fake.text(max_nb_chars=200), | ||
| price=round( | ||
| fake.pyfloat( | ||
| left_digits=3, | ||
| right_digits=2, | ||
| positive=True, | ||
| min_value=5, | ||
| max_value=200, | ||
| ), | ||
| 2, | ||
| ), | ||
| stock=randint(0, 100), | ||
| data={ | ||
| "color": fake.color_name(), | ||
| "size": choice(["S", "M", "L", "XL"]), | ||
| }, | ||
| ) | ||
| ) | ||
| db.session.commit() | ||
| products = Product.query.all() | ||
|
|
||
| # ----- Users ----- | ||
| while User.query.count() < 50: | ||
| user = User( | ||
| email=fake.unique.email(), | ||
| name=fake.name(), | ||
| age=randint(18, 65), | ||
| active=choice([True, False]), | ||
| preferences={"newsletter": choice([True, False])}, | ||
| balance=round( | ||
| fake.pyfloat(left_digits=3, right_digits=2, positive=True), 2 | ||
| ), | ||
| last_login=fake.date_time_this_year(), | ||
| ) | ||
| # assign 1-3 random roles | ||
| user.roles.extend( | ||
| fake.random_elements(elements=roles, length=randint(1, 3), unique=True) | ||
| ) | ||
| db.session.add(user) | ||
| db.session.commit() | ||
|
|
||
| users = User.query.all() | ||
|
|
||
| # ----- Addresses & Profiles ----- | ||
| for user in users: | ||
| if not user.addresses: | ||
| addr = Address( | ||
| street=fake.street_address(), | ||
| city=fake.city(), | ||
| state=fake.state(), | ||
| postal_code=fake.postcode(), | ||
| country=fake.country(), | ||
| is_primary=True, | ||
| user_id=user.id, | ||
| ) | ||
| db.session.add(addr) | ||
| if not user.profile: | ||
| profile = Profile(bio=fake.sentence(), avatar=None, user_id=user.id) | ||
| db.session.add(profile) | ||
| db.session.commit() | ||
|
|
||
| # ----- Posts ----- | ||
| while Post.query.count() < 100: | ||
| author = choice(users) | ||
| post = Post( | ||
| author_id=author.id, | ||
| category=choice(categories), | ||
| title=fake.sentence(), | ||
| body=fake.text(max_nb_chars=800), | ||
| ) | ||
| post.tags = fake.random_elements( | ||
| elements=tags, length=randint(1, 4), unique=True | ||
| ) | ||
| db.session.add(post) | ||
| db.session.commit() | ||
|
|
||
| posts = Post.query.all() | ||
|
|
||
| # ----- Comments ----- | ||
| while Comment.query.count() < 300: | ||
| db.session.add( | ||
| Comment( | ||
| post_id=choice(posts).id, | ||
| user_id=choice(users).id, | ||
| body=fake.sentence(), | ||
| ) | ||
| ) | ||
| db.session.commit() | ||
|
|
||
| # ----- Orders, Items, Payments ----- | ||
| while Order.query.count() < 200: | ||
| user = choice(users) | ||
| order = Order( | ||
| user_id=user.id, | ||
| status=choice(list(OrderStatus)), | ||
| total_price=0, # will update after adding items | ||
| ) | ||
| db.session.add(order) | ||
| db.session.commit() | ||
|
|
||
| orders = Order.query.all() | ||
|
|
||
| for order in orders: | ||
| if not order.items: | ||
| chosen_products = fake.random_elements( # type: ignore | ||
| elements=products, length=randint(1, 5), unique=True | ||
| ) | ||
| total = 0 | ||
| for prod in chosen_products: | ||
| qty = randint(1, 3) | ||
| total += prod.price * qty | ||
| db.session.add( | ||
| OrderItem( | ||
| order_id=order.id, | ||
| product_id=prod.id, | ||
| quantity=qty, | ||
| price=prod.price, | ||
| ) | ||
| ) | ||
| order.total_price = round(total, 2) | ||
| db.session.add( | ||
| Payment( | ||
| order_id=order.id, | ||
| amount=order.total_price, | ||
| paid_at=fake.date_time_this_year(), | ||
| method=choice(list(PaymentMethod)), | ||
| ) | ||
| ) | ||
| db.session.commit() | ||
|
|
||
| # ----- Departments ----- | ||
| while Department.query.count() < 5: | ||
| db.session.add( | ||
| Department( | ||
| name=fake.unique.company(), | ||
| budget=round( | ||
| fake.pyfloat( | ||
| left_digits=7, | ||
| right_digits=2, | ||
| positive=True, | ||
| min_value=10000, | ||
| max_value=1000000, | ||
| ), | ||
| 2, | ||
| ), | ||
| ) | ||
| ) | ||
| db.session.commit() | ||
| departments = Department.query.all() | ||
|
|
||
| # ----- Projects ----- | ||
| while Project.query.count() < 10: | ||
| db.session.add( | ||
| Project( | ||
| name=fake.unique.catch_phrase(), | ||
| deadline=fake.date_this_year(after_today=True), | ||
| description=fake.text(max_nb_chars=200), | ||
| budget=round( | ||
| fake.pyfloat( | ||
| left_digits=6, | ||
| right_digits=2, | ||
| positive=True, | ||
| min_value=5000, | ||
| max_value=500000, | ||
| ), | ||
| 2, | ||
| ), | ||
| ) | ||
| ) | ||
| db.session.commit() | ||
| projects = Project.query.all() | ||
|
|
||
| # ----- Employees ----- | ||
| while Employee.query.count() < 100: | ||
| dept = choice(departments) | ||
| emp = Employee( | ||
| department_id=dept.id, | ||
| first_name=fake.first_name(), | ||
| last_name=fake.last_name(), | ||
| salary=round( | ||
| fake.pyfloat( | ||
| left_digits=6, | ||
| right_digits=2, | ||
| positive=True, | ||
| min_value=30000, | ||
| max_value=150000, | ||
| ), | ||
| 2, | ||
| ), | ||
| hire_date=fake.date_between(start_date="-10y", end_date="today"), | ||
| shift_start=fake.time_object(), | ||
| is_full_time=choice([True, False]), | ||
| rating=round( | ||
| fake.pyfloat( | ||
| left_digits=1, | ||
| right_digits=2, | ||
| positive=True, | ||
| min_value=1, | ||
| max_value=5, | ||
| ), | ||
| 2, | ||
| ), | ||
| ) | ||
| db.session.add(emp) | ||
| db.session.commit() | ||
|
|
||
| employees = Employee.query.all() | ||
|
|
||
| for emp in employees: | ||
| if not emp.projects: | ||
| emp.projects = fake.random_elements( | ||
| elements=projects, length=randint(1, 4), unique=True | ||
| ) | ||
| db.session.commit() | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| from bp import bp | ||
| from config import MyConfig | ||
| from fake import generate_fake_data | ||
| from flask import Flask | ||
| from models import db_init | ||
| from my_admin.basic_admin import admin as basic_admin | ||
| from my_admin.pro_admin import admin as pro_admin | ||
| from my_admin.super_admin import admin as super_admin | ||
|
|
||
| app = Flask(__name__) | ||
|
|
||
| app.config.from_object(MyConfig) | ||
| db_init(app) | ||
| generate_fake_data(app) | ||
|
|
||
| # Init admin instances | ||
|
|
||
| basic_admin.init_app(app) | ||
| super_admin.init_app(app) | ||
| pro_admin.init_app(app) | ||
|
|
||
|
|
||
| app.register_blueprint(bp) | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| app.run(debug=True) |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed in #2854