14 Commits

236 changed files with 14549 additions and 21638 deletions

5
.gitignore vendored
View File

@ -26,3 +26,8 @@ _templates
# javascript # javascript
package-lock.json package-lock.json
.vscode/symbols.json .vscode/symbols.json
apps/static/assets/node_modules
apps/static/assets/yarn.lock
apps/static/assets/.temp

View File

@ -1,5 +1,39 @@
# Change Log # Change Log
## [1.0.4] 2021-11-10
### Improvements
- Bump Codebase: [Flask Dashboard](https://github.com/app-generator/boilerplate-code-flask-dashboard) v2.0.0
- Dependencies update (all packages)
- Flask==2.0.1 (latest stable version)
- Better Code formatting
- Improved Files organization
- Optimize imports
- Docker Scripts Update
- Gulp Tooling (SASS Compilation)
- Fixes:
- Import error caused by WTForms
## [1.0.3] 2021-05-16
### Dependencies Update
- Bump Codebase: [Flask Dashboard](https://github.com/app-generator/boilerplate-code-flask-dashboard) v1.0.6
- Freeze used versions in `requirements.txt`
- jinja2 = 2.11.3
## [1.0.2] 2021-05-11
### Improvements
- Bump Codebase: [Flask Dashboard](https://github.com/app-generator/boilerplate-code-flask-dashboard) v1.0.5
- Freeze used versions in `requirements.txt`
- flask_sqlalchemy = 2.4.4
- sqlalchemy = 1.3.23
## [1.0.1] 2021-02-10
### Improvements
- Bump UI: [Jinja Template Black](https://github.com/app-generator/jinja-black-dashboard) v1.0.1
- Bump Codebase: [Flask Dashboard](https://github.com/app-generator/boilerplate-code-flask-dashboard) v1.0.4
## [1.0.0] 2020-08-21 ## [1.0.0] 2020-08-21
### Initial Release ### Initial Release

View File

@ -1,11 +1,14 @@
FROM python:3.6 FROM python:3.9
ENV FLASK_APP run.py COPY . .
COPY run.py gunicorn-cfg.py requirements.txt config.py .env ./ # set environment variables
COPY app app ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
RUN pip install -r requirements.txt # install python dependencies
RUN pip install --upgrade pip
RUN pip install --no-cache-dir -r requirements.txt
EXPOSE 5005 # gunicorn
CMD ["gunicorn", "--config", "gunicorn-cfg.py", "run:app"] CMD ["gunicorn", "--config", "gunicorn-cfg.py", "run:app"]

View File

@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2019 - present Creative-Tim / AppSeed.us Copyright (c) 2019 - present Creative-Tim / [AppSeed.us](https://appseed.us)
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

136
README.md
View File

@ -1,6 +1,6 @@
# [Black Dashboard Flask](https://www.creative-tim.com/product/black-dashboard-flask) [![Tweet](https://img.shields.io/twitter/url/http/shields.io.svg?style=social&logo=twitter)](https://twitter.com/home?status=Material%20Dashboard,%20a%20free%20Material%20Bootstrap%204%20Admin%20Template%20%E2%9D%A4%EF%B8%8F%20https%3A//bit.ly/2Lyat1Y%20%23bootstrap%20%23material%20%23design%20%23developers%20%23freebie%20%20via%20%40CreativeTim) # [Black Dashboard Flask](https://www.creative-tim.com/product/black-dashboard-flask) [![Tweet](https://img.shields.io/twitter/url/http/shields.io.svg?style=social&logo=twitter)](https://twitter.com/home?status=Material%20Dashboard,%20a%20free%20Material%20Bootstrap%204%20Admin%20Template%20%E2%9D%A4%EF%B8%8F%20https%3A//bit.ly/2Lyat1Y%20%23bootstrap%20%23material%20%23design%20%23developers%20%23freebie%20%20via%20%40CreativeTim)
![version](https://img.shields.io/badge/version-1.0.0-blue.svg) [![GitHub issues open](https://img.shields.io/github/issues/creativetimofficial/black-dashboard-flask.svg?maxAge=2592000)](https://github.com/creativetimofficial/black-dashboard-flask/issues?q=is%3Aopen+is%3Aissue) [![GitHub issues closed](https://img.shields.io/github/issues-closed-raw/creativetimofficial/black-dashboard-flask.svg?maxAge=2592000)](https://github.com/creativetimofficial/black-dashboard-flask/issues?q=is%3Aissue+is%3Aclosed) [![Join the chat at https://gitter.im/NIT-dgp/General](https://badges.gitter.im/NIT-dgp/General.svg)](https://gitter.im/creative-tim-general/Lobby) [![Chat](https://img.shields.io/badge/chat-on%20discord-7289da.svg)](https://discord.gg/E4aHAQy) ![version](https://img.shields.io/badge/version-1.0.1-blue.svg) [![GitHub issues open](https://img.shields.io/github/issues/creativetimofficial/black-dashboard-flask.svg?maxAge=2592000)](https://github.com/creativetimofficial/black-dashboard-flask/issues?q=is%3Aopen+is%3Aissue) [![GitHub issues closed](https://img.shields.io/github/issues-closed-raw/creativetimofficial/black-dashboard-flask.svg?maxAge=2592000)](https://github.com/creativetimofficial/black-dashboard-flask/issues?q=is%3Aissue+is%3Aclosed) [![Join the chat at https://gitter.im/NIT-dgp/General](https://badges.gitter.im/NIT-dgp/General.svg)](https://gitter.im/creative-tim-general/Lobby) [![Chat](https://img.shields.io/badge/chat-on%20discord-7289da.svg)](https://discord.gg/E4aHAQy)
![Black Dashboard Flask - Admin Dashboard coded in Django.](https://github.com/creativetimofficial/black-dashboard-flask/blob/master/media/black-dashboard-flask-intro.gif) ![Black Dashboard Flask - Admin Dashboard coded in Django.](https://github.com/creativetimofficial/black-dashboard-flask/blob/master/media/black-dashboard-flask-intro.gif)
@ -8,8 +8,10 @@
> Free product - **Flask Dashboard** starter project - Features: > Free product - **Flask Dashboard** starter project - Features:
- Up-to-date [dependencies](./requirements.txt): **Flask 2.0.1**
- [SCSS compilation](#recompile-css) via **Gulp**
- UI Kit: **Black Dashboard** (Free Version) provided by **[Creative-Tim](https://www.creative-tim.com/)** - UI Kit: **Black Dashboard** (Free Version) provided by **[Creative-Tim](https://www.creative-tim.com/)**
- [Codebase](https://github.com/app-generator/boilerplate-code-flask-dashboard) - provided by **[AppSeed](https://appseed.us/)** - Flask Codebase - provided by **[AppSeed](https://appseed.us/)**
- SQLite, PostgreSQL, SQLAlchemy ORM - SQLite, PostgreSQL, SQLAlchemy ORM
- Alembic (DB schema migrations) - Alembic (DB schema migrations)
- Modular design with **Blueprints** - Modular design with **Blueprints**
@ -22,6 +24,7 @@
## Table of Contents ## Table of Contents
* [Demo](#demo) * [Demo](#demo)
* [Docker Support](#docker-support)
* [Quick Start](#quick-start) * [Quick Start](#quick-start)
* [Documentation](#documentation) * [Documentation](#documentation)
* [File Structure](#file-structure) * [File Structure](#file-structure)
@ -42,6 +45,27 @@
<br /> <br />
## Docker Support
> Get the code
```bash
$ git clone https://github.com/app-generator/black-dashboard-flask.git
$ cd black-dashboard-flask
```
> Start the app in Docker
```bash
$ docker-compose pull # download dependencies
$ docker-compose build # local set up
$ docker-compose up -d # start the app
```
Visit `http://localhost:85` in your browser. The app should be up & running.
<br />
## Quick start ## Quick start
> UNZIP the sources or clone the private repository. After getting the code, open a terminal and navigate to the working directory, with product source code. > UNZIP the sources or clone the private repository. After getting the code, open a terminal and navigate to the working directory, with product source code.
@ -98,34 +122,53 @@ Within the download you'll find the following directories and files:
```bash ```bash
< PROJECT ROOT > < PROJECT ROOT >
| |
|-- app/ |-- apps/
| |-- home/ # Home Blueprint - serve app pages (private area) | |
| |-- base/ # Base Blueprint - handles the authentication | |-- home/ # A simple app that serve HTML files
| | |-- routes.py # Define app routes
| |
| |-- authentication/ # Handles auth routes (login and register)
| | |-- routes.py # Define authentication routes
| | |-- models.py # Defines models
| | |-- forms.py # Define auth forms (login and register)
| |
| |-- static/ | |-- static/
| | |-- <css, JS, images> # CSS files, Javascripts files | | |-- <css, JS, images> # CSS files, Javascripts files
| | | |
| |-- templates/ # Templates used to render pages | |-- templates/ # Templates used to render pages
| | |-- includes/ # HTML chunks and components
| | | |-- navigation.html # Top menu component
| | | |-- sidebar.html # Sidebar component
| | | |-- footer.html # App Footer
| | | |-- scripts.html # Scripts common to all pages
| | |
| | |-- layouts/ # Master pages
| | | |-- base-fullscreen.html # Used by Authentication pages
| | | |-- base.html # Used by common pages
| | |
| | |-- accounts/ # Authentication pages
| | | |-- login.html # Login page
| | | |-- register.html # Register page
| | |
| | |-- home/ # UI Kit Pages
| | |-- index.html # Index page
| | |-- 404-page.html # 404 page
| | |-- *.html # All other pages
| | | |
| |-- includes/ # | config.py # Set up the app
| | |-- navigation.html # Top menu component | __init__.py # Initialize the app
| | |-- sidebar.html # Sidebar component
| | |-- footer.html # App Footer
| | |-- scripts.html # Scripts common to all pages
| |
| |-- layouts/ # Master pages
| | |-- base-fullscreen.html # Used by Authentication pages
| | |-- base.html # Used by common pages
| |
| |-- accounts/ # Authentication pages
| |-- login.html # Login page
| |-- register.html # Registration page
| |
|-- requirements.txt # Development modules - SQLite storage |-- requirements.txt # Development modules - SQLite storage
|-- requirements-mysql.txt # Production modules - Mysql DMBS |-- requirements-mysql.txt # Production modules - Mysql DMBS
|-- requirements-pqsql.txt # Production modules - PostgreSql DMBS |-- requirements-pqsql.txt # Production modules - PostgreSql DMBS
| |
|-- Dockerfile # Deployment
|-- docker-compose.yml # Deployment
|-- gunicorn-cfg.py # Deployment
|-- nginx # Deployment
| |-- appseed-app.conf # Deployment
|
|-- .env # Inject Configuration via Environment |-- .env # Inject Configuration via Environment
|-- config.py # Set up the app
|-- run.py # Start the app - WSGI gateway |-- run.py # Start the app - WSGI gateway
| |
|-- ************************************************************************ |-- ************************************************************************
@ -133,6 +176,61 @@ Within the download you'll find the following directories and files:
<br /> <br />
> The bootstrap flow
- `run.py` loads the `.env` file
- Initialize the app using the specified profile: *Debug* or *Production*
- If env.DEBUG is set to *True* the SQLite storage is used
- If env.DEBUG is set to *False* the specified DB driver is used (MySql, PostgreSQL)
- Call the app factory method `create_app` defined in app/__init__.py
- Redirect the guest users to Login page
- Unlock the pages served by *home* blueprint for authenticated users
<br />
## Recompile CSS
To recompile SCSS files, follow this setup:
<br />
**Step #1** - Install tools
- [NodeJS](https://nodejs.org/en/) 12.x or higher
- [Gulp](https://gulpjs.com/) - globally
- `npm install -g gulp-cli`
- [Yarn](https://yarnpkg.com/) (optional)
<br />
**Step #2** - Change the working directory to `assets` folder
```bash
$ cd apps/static/assets
```
<br />
**Step #3** - Install modules (this will create a classic `node_modules` directory)
```bash
$ npm install
// OR
$ yarn
```
<br />
**Step #4** - Edit & Recompile SCSS files
```bash
$ gulp scss
```
The generated file is saved in `static/assets/css` directory.
<br />
## Browser Support ## Browser Support
At present, we officially aim to support the last two versions of the following browsers: At present, we officially aim to support the last two versions of the following browsers:

View File

@ -1,19 +0,0 @@
# -*- encoding: utf-8 -*-
"""
Copyright (c) 2019 - present AppSeed.us
"""
from flask_wtf import FlaskForm
from wtforms import TextField, PasswordField
from wtforms.validators import InputRequired, Email, DataRequired
## login and registration
class LoginForm(FlaskForm):
username = TextField ('Username', id='username_login' , validators=[DataRequired()])
password = PasswordField('Password', id='pwd_login' , validators=[DataRequired()])
class CreateAccountForm(FlaskForm):
username = TextField('Username' , id='username_create' , validators=[DataRequired()])
email = TextField('Email' , id='email_create' , validators=[DataRequired(), Email()])
password = PasswordField('Password' , id='pwd_create' , validators=[DataRequired()])

View File

@ -1,120 +0,0 @@
# -*- encoding: utf-8 -*-
"""
Copyright (c) 2019 - present AppSeed.us
"""
from flask import jsonify, render_template, redirect, request, url_for
from flask_login import (
current_user,
login_required,
login_user,
logout_user
)
from app import db, login_manager
from app.base import blueprint
from app.base.forms import LoginForm, CreateAccountForm
from app.base.models import User
from app.base.util import verify_pass
@blueprint.route('/')
def route_default():
return redirect(url_for('base_blueprint.login'))
## Login & Registration
@blueprint.route('/login', methods=['GET', 'POST'])
def login():
login_form = LoginForm(request.form)
if 'login' in request.form:
# read form data
username = request.form['username']
password = request.form['password']
# Locate user
user = User.query.filter_by(username=username).first()
# Check the password
if user and verify_pass( password, user.password):
login_user(user)
return redirect(url_for('base_blueprint.route_default'))
# Something (user or pass) is not ok
return render_template( 'accounts/login.html', msg='Wrong user or password', form=login_form)
if not current_user.is_authenticated:
return render_template( 'accounts/login.html',
form=login_form)
return redirect(url_for('home_blueprint.index'))
@blueprint.route('/register', methods=['GET', 'POST'])
def register():
login_form = LoginForm(request.form)
create_account_form = CreateAccountForm(request.form)
if 'register' in request.form:
username = request.form['username']
email = request.form['email' ]
# Check usename exists
user = User.query.filter_by(username=username).first()
if user:
return render_template( 'accounts/register.html',
msg='Username already registered',
success=False,
form=create_account_form)
# Check email exists
user = User.query.filter_by(email=email).first()
if user:
return render_template( 'accounts/register.html',
msg='Email already registered',
success=False,
form=create_account_form)
# else we can create the user
user = User(**request.form)
db.session.add(user)
db.session.commit()
return render_template( 'accounts/register.html',
msg='User created please <a href="/login">login</a>',
success=True,
form=create_account_form)
else:
return render_template( 'accounts/register.html', form=create_account_form)
@blueprint.route('/logout')
def logout():
logout_user()
return redirect(url_for('base_blueprint.login'))
@blueprint.route('/shutdown')
def shutdown():
func = request.environ.get('werkzeug.server.shutdown')
if func is None:
raise RuntimeError('Not running with the Werkzeug Server')
func()
return 'Server shutting down...'
## Errors
@login_manager.unauthorized_handler
def unauthorized_handler():
return render_template('page-403.html'), 403
@blueprint.errorhandler(403)
def access_forbidden(error):
return render_template('page-403.html'), 403
@blueprint.errorhandler(404)
def not_found_error(error):
return render_template('page-404.html'), 404
@blueprint.errorhandler(500)
def internal_error(error):
return render_template('page-500.html'), 500

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
{"version":3,"sources":["_site_dashboard_free/assets/js/dashboard-free.js"],"names":["transparent","transparentDemo","fixedTop","navbar_initialized","backgroundOrange","sidebar_mini_active","toggle_initialized","$html","$","$body","$navbar_minimize_fixed","$collapse","$navbar","$tagsinput","$selectpicker","$navbar_color","$full_screen_map","$datetimepicker","$datepicker","$timepicker","seq","delays","durations","seq2","delays2","durations2","hexToRGB","hex","alpha","r","parseInt","slice","g","b","navigator","platform","indexOf","length","PerfectScrollbar","wheelSpeed","wheelPropagation","minScrollbarLength","suppressScrollX","each","this","addClass","document","ready","offset","window","scroll","scrollTop","css","top","on","closest","removeClass","blackDashboard","initMinimizeSidebar","scroll_distance","attr","checkScrollForTransparentNavbar","parent","$this","data_on_label","data","data_off_label","bootstrapSwitch","onText","offText","$toggle","misc","navbar_menu_visible","setTimeout","remove","appendTo","click","resize","isExpanded","find","hasClass","width","showSidebarMessage","simulateWindowResize","setInterval","dispatchEvent","Event","clearInterval","message","notify","icon","type","timer","placement","from","align","e","console","log"],"mappings":"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"}

View File

@ -1,299 +0,0 @@
.white-content{
background: $light-bg;
.navbar.navbar-transparent .navbar-brand{
color: $black-states;
}
.navbar.navbar-transparent .navbar-toggler-bar{
background: $black-states;
}
.navbar.navbar-transparent .navbar-nav li a:not(.dropdown-item){
color: $black-states;
& i{
color: $black-states;
}
}
.navbar.navbar-transparent .navbar-minimize button i{
color: $black-states;
}
.navbar.navbar-transparent .search-bar.input-group i{
color: $black-states;
}
.navbar.navbar-transparent .search-bar.input-group .form-control{
color: $default;
&::placeholder{
color: $dark-gray;
}
}
.sidebar{
box-shadow: 0 2px 22px 0 rgba(0,0,0,.1), 0 4px 20px 0 rgba(0,0,0,.15);
p{
color: $opacity-8;
}
}
.main-panel{
background: $light-bg;
}
h1, h2, h3, h4, h5, h6, p, ol li, ul li, pre {
color: $black-states;
}
.font-icon-detail i{
color: $black;
}
.btn:not([data-action]):hover{
box-shadow: 0 4px 6px rgba(50,50,93,.11), 0 1px 3px rgba(0,0,0,.08);
}
.btn-neutral.btn-link{
color: rgba($black, 0.7);
}
//style for inputs
@include form-control-placeholder(rgba($black,0.4), 1);
.has-danger{
.form-control, .input-group-prepend .input-group-text{
border-color: $danger-states;
}
}
.input-group-prepend .input-group-text{
border-color: rgba($black-states,0.5);
color: $black-states;
}
.form-control{
color: $black;
border-color: rgba($black-states,0.5);
&:focus{
border-color: $primary;
}
}
.form-group.no-border,
.input-group.no-border{
.form-control,
.form-control + .input-group-prepend .input-group-text,
.form-control + .input-group-append .input-group-text,
.input-group-prepend .input-group-text,
.input-group-append .input-group-text{
background-color: $opacity-gray-3;
&:focus,
&:active,
&:active{
background-color: $opacity-gray-5;
}
}
.form-control{
&:focus{
& + .input-group-prepend .input-group-text,
& + .input-group-append .input-group-text{
background-color: $transparent-bg;
}
}
}
}
.input-group[disabled]{
.input-group-prepend .input-group-text,
.input-group-append .input-group-text{
background-color: $black;
}
}
.form-control[disabled], .form-control[readonly], fieldset[disabled] .form-control{
background: $light-gray;
border-color: rgba($black-states,0.3);
}
.input-group-focus{
.input-group-prepend .input-group-text,
.input-group-append .input-group-text,
.form-control{
border-color: $primary;
}
&.no-border{
.input-group-prepend .input-group-text,
.input-group-append .input-group-text{
background-color: $opacity-gray-5;
}
}
}
.input-group-prepend .input-group-text {
border-right: none;
}
.input-group-append .input-group-text {
border-left: none;
}
.has-danger .form-control:focus, .has-success.input-group-focus .input-group-append .input-group-text, .has-success.input-group-focus .input-group-prepend .input-group-text {
border-color: $danger-states;
}
.has-success .form-control:focus, .has-success.input-group-focus .input-group-append .input-group-text, .has-success.input-group-focus .input-group-prepend .input-group-text {
border-color: darken($success, 10%);
}
.btn.btn-link{
&:hover,&:active, &:focus{
color: $dark-gray !important;
}
}
.btn-group .btn.active{
box-shadow: 0 4px 6px rgba(50,50,93,.11), 0 1px 3px rgba(0,0,0,.08);
}
.card:not(.card-white){
background: $white;
box-shadow: 0 1px 15px 0 rgba(, , , 0.05);
.card-header{
color: $black;
a[data-toggle="collapse"]{
color: $black;
}
}
.card-header .card-title, .card-body .card-title{
color: $black-states;
}
.card-body{
.card-category, .card-description{
color: $black-states;
}
}
label:not(.btn){
color: $default;
}
&.nav-pills .nav-item .nav-link {
color: $nav-gray;
background-color: hsla(0,0%,87%,.3);
&:not(.active):hover{
background: hsla(0,0%,87%,.5);
}
&.active{
color: $white;
}
}
.tab-content .tab-pane {
color: $nav-gray;
}
.card{
box-shadow: none;
}
&.card-plain{
background: $transparent-bg;
box-shadow: none;
}
&.card-tasks{
.card-body{
i{
color: rgba(34,42,66,0.7);
&:hover{
color: $black;
}
}
}
}
}
.table{
> tbody > tr > td {
color: rgba($black, 0.7) !important;
}
> thead > tr > th,
> tbody > tr > th,
> tfoot > tr > th,
> thead > tr > td,
> tbody > tr > td,
> tfoot > tr > td{
border-color: rgba($black, 0.2);
padding: 12px 7px;
vertical-align: middle;
}
> thead > tr > th, button.btn-neutral.btn-link{
color: rgba($black, 0.7);
}
}
.footer ul li a{
color: $black-states;
}
.footer .copyright{
color: $black-states;
}
.progress-container, .progress-container.progress.sm{
.progress{
background: rgba($black,0.1);
box-shadow: 0 0 0 3px rgba($black,0.1);
.progress-value{
color: $default;
}
}
.progress-badge{
color: $black-states;
}
}
.full-page{
background: $light-bg;
h1, h2, h3, h4, h5, h6, p, ol li, ul li, pre {
color: $black-states;
}
.description{
color: $dark-gray;
}
.footer ul li a{
color: $black-states;
}
.footer .copyright{
color: $black-states;
}
}
.nav-pills .nav-item .nav-link:not(.active){
background: darken($light-bg,10%);
color: $black-states;
&:hover{
background: darken($white,20%);
}
}
}

View File

@ -1,70 +0,0 @@
{% extends "layouts/base.html" %}
{% block title %} Login {% endblock %}
<!-- Specific Page CSS goes HERE -->
{% block stylesheets %}{% endblock stylesheets %}
{% block content %}
<div class="row">
<div class="col-md-8">
<div class="card">
<form role="form" method="post" action="">
{{ form.hidden_tag() }}
<div class="card-header">
<h5 class="title">Login</h5>
<h6 class="card-category">
{% if msg %}
{{ msg | safe }}
{% else %}
Default Credentials: test / pass
{% endif %}
</h6>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-3 px-md-1">
<div class="form-group">
<label>Username</label>
{{ form.username(placeholder="Username", class="form-control") }}
</div>
</div>
</div>
<div class="row">
<div class="col-md-3 px-md-1">
<div class="form-group">
<label>Password</label>
{{ form.password(placeholder="Password", class="form-control", type="password") }}
</div>
</div>
</div>
</div>
<div class="card-footer">
<button type="submit" name="login" class="btn btn-fill btn-primary">Login</button>
&nbsp; &nbsp;
Don't have an account? <a href="{{ url_for('base_blueprint.register') }}" class="text-primary">Create</a>
</div>
</form>
</div>
</div>
</div>
{% endblock content %}
<!-- Specific Page JS goes HERE -->
{% block javascripts %}{% endblock javascripts %}

View File

@ -1,74 +0,0 @@
{% extends "layouts/base.html" %}
{% block title %} Register {% endblock %}
<!-- Specific Page CSS goes HERE -->
{% block stylesheets %}{% endblock stylesheets %}
{% block content %}
<div class="row">
<div class="col-md-8">
<div class="card">
<form role="form" method="post" action="">
<div class="card-header">
<h5 class="title">Register</h5>
<h6 class="card-category">
{% if msg %}
{{ msg | safe }}
{% else %}
Add your credentials
{% endif %}
</h6>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-3 px-md-1">
<div class="form-group">
<label>Username</label>
{{ form.username(placeholder="Username", class="form-control") }}
</div>
</div>
</div>
<div class="row">
<div class="col-md-3 px-md-1">
<div class="form-group">
<label>Email</label>
{{ form.email(placeholder="Email", class="input form-control", type="email") }}
</div>
</div>
</div>
<div class="row">
<div class="col-md-3 px-md-1">
<div class="form-group">
<label>Password</label>
{{ form.password(placeholder="Password", class="form-control", type="password") }}
</div>
</div>
</div>
</div>
<div class="card-footer">
<button type="submit" name="register" class="btn btn-fill btn-primary">Register</button>
&nbsp; &nbsp;
Have an account? <a href="{{ url_for('base_blueprint.login') }}" class="text-primary">Login</a>
</div>
</form>
</div>
</div>
</div>
{% endblock content %}
<!-- Specific Page JS goes HERE -->
{% block javascripts %}{% endblock javascripts %}

View File

@ -1,82 +0,0 @@
<!-- Navbar -->
<nav class="navbar navbar-expand-lg navbar-absolute navbar-transparent">
<div class="container-fluid">
<div class="navbar-wrapper">
<div class="navbar-toggle d-inline">
<button type="button" class="navbar-toggler">
<span class="navbar-toggler-bar bar1"></span>
<span class="navbar-toggler-bar bar2"></span>
<span class="navbar-toggler-bar bar3"></span>
</button>
</div>
<a class="navbar-brand" href="javascript:void(0)">Dashboard</a>
</div>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navigation" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-bar navbar-kebab"></span>
<span class="navbar-toggler-bar navbar-kebab"></span>
<span class="navbar-toggler-bar navbar-kebab"></span>
</button>
<div class="collapse navbar-collapse" id="navigation">
{% if current_user.is_authenticated %}
<ul class="navbar-nav ml-auto">
<li class="search-bar input-group">
<button class="btn btn-link" id="search-button" data-toggle="modal" data-target="#searchModal"><i class="tim-icons icon-zoom-split" ></i>
<span class="d-lg-none d-md-block">Search</span>
</button>
</li>
<li class="dropdown nav-item">
<a href="javascript:void(0)" class="dropdown-toggle nav-link" data-toggle="dropdown">
<div class="notification d-none d-lg-block d-xl-block"></div>
<i class="tim-icons icon-sound-wave"></i>
<p class="d-lg-none">
Notifications
</p>
</a>
<ul class="dropdown-menu dropdown-menu-right dropdown-navbar">
<li class="nav-link"><a href="#" class="nav-item dropdown-item">Mike John responded to your email</a></li>
<li class="nav-link"><a href="javascript:void(0)" class="nav-item dropdown-item">You have 5 more tasks</a></li>
<li class="nav-link"><a href="javascript:void(0)" class="nav-item dropdown-item">Your friend Michael is in town</a></li>
<li class="nav-link"><a href="javascript:void(0)" class="nav-item dropdown-item">Another notification</a></li>
<li class="nav-link"><a href="javascript:void(0)" class="nav-item dropdown-item">Another one</a></li>
</ul>
</li>
<li class="dropdown nav-item">
<a href="#" class="dropdown-toggle nav-link" data-toggle="dropdown">
<div class="photo">
<img src="/static/assets/img/anime3.png" alt="Profile Photo">
</div>
<b class="caret d-none d-lg-block d-xl-block"></b>
<p class="d-lg-none">
Log out
</p>
</a>
<ul class="dropdown-menu dropdown-navbar">
<li class="nav-link"><a href="/page-user.html" class="nav-item dropdown-item">Profile</a></li>
<li class="dropdown-divider"></li>
<li class="nav-link"><a href="{{ url_for('base_blueprint.logout') }}" class="nav-item dropdown-item">Log out</a></li>
</ul>
</li>
<li class="separator d-lg-none"></li>
</ul>
{% endif %}
</div>
</div>
</nav>
<div class="modal modal-search fade" id="searchModal" tabindex="-1" role="dialog" aria-labelledby="searchModal" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<input type="text" class="form-control" id="inlineFormInputGroup" placeholder="SEARCH">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<i class="tim-icons icon-simple-remove"></i>
</button>
</div>
</div>
</div>
</div>
<!-- End Navbar -->

View File

@ -1,52 +0,0 @@
# -*- encoding: utf-8 -*-
"""
Copyright (c) 2019 - present AppSeed.us
"""
from app.home import blueprint
from flask import render_template, redirect, url_for, request
from flask_login import login_required, current_user
from app import login_manager
from jinja2 import TemplateNotFound
@blueprint.route('/index')
@login_required
def index():
return render_template('index.html', segment='index')
@blueprint.route('/<template>')
@login_required
def route_template(template):
try:
if not template.endswith( '.html' ):
template += '.html'
# Detect the current page
segment = get_segment( request )
# Serve the file (if exists) from app/templates/FILE.html
return render_template( template, segment=segment )
except TemplateNotFound:
return render_template('page-404.html'), 404
except:
return render_template('page-500.html'), 500
# Helper - Extract current page name from request
def get_segment( request ):
try:
segment = request.path.split('/')[-1]
if segment == '':
segment = 'index'
return segment
except:
return None

View File

@ -3,25 +3,27 @@
Copyright (c) 2019 - present AppSeed.us Copyright (c) 2019 - present AppSeed.us
""" """
from flask import Flask, url_for from flask import Flask
from flask_login import LoginManager from flask_login import LoginManager
from flask_sqlalchemy import SQLAlchemy from flask_sqlalchemy import SQLAlchemy
from importlib import import_module from importlib import import_module
from logging import basicConfig, DEBUG, getLogger, StreamHandler
from os import path
db = SQLAlchemy() db = SQLAlchemy()
login_manager = LoginManager() login_manager = LoginManager()
def register_extensions(app): def register_extensions(app):
db.init_app(app) db.init_app(app)
login_manager.init_app(app) login_manager.init_app(app)
def register_blueprints(app): def register_blueprints(app):
for module_name in ('base', 'home'): for module_name in ('authentication', 'home'):
module = import_module('app.{}.routes'.format(module_name)) module = import_module('apps.{}.routes'.format(module_name))
app.register_blueprint(module.blueprint) app.register_blueprint(module.blueprint)
def configure_database(app): def configure_database(app):
@app.before_first_request @app.before_first_request
@ -32,8 +34,9 @@ def configure_database(app):
def shutdown_session(exception=None): def shutdown_session(exception=None):
db.session.remove() db.session.remove()
def create_app(config): def create_app(config):
app = Flask(__name__, static_folder='base/static') app = Flask(__name__)
app.config.from_object(config) app.config.from_object(config)
register_extensions(app) register_extensions(app)
register_blueprints(app) register_blueprints(app)

View File

@ -6,9 +6,7 @@ Copyright (c) 2019 - present AppSeed.us
from flask import Blueprint from flask import Blueprint
blueprint = Blueprint( blueprint = Blueprint(
'base_blueprint', 'authentication_blueprint',
__name__, __name__,
url_prefix='', url_prefix=''
template_folder='templates',
static_folder='static'
) )

View File

@ -0,0 +1,31 @@
# -*- encoding: utf-8 -*-
"""
Copyright (c) 2019 - present AppSeed.us
"""
from flask_wtf import FlaskForm
from wtforms import TextField, PasswordField
from wtforms.validators import Email, DataRequired
# login and registration
class LoginForm(FlaskForm):
username = TextField('Username',
id='username_login',
validators=[DataRequired()])
password = PasswordField('Password',
id='pwd_login',
validators=[DataRequired()])
class CreateAccountForm(FlaskForm):
username = TextField('Username',
id='username_create',
validators=[DataRequired()])
email = TextField('Email',
id='email_create',
validators=[DataRequired(), Email()])
password = PasswordField('Password',
id='pwd_create',
validators=[DataRequired()])

View File

@ -4,20 +4,19 @@ Copyright (c) 2019 - present AppSeed.us
""" """
from flask_login import UserMixin from flask_login import UserMixin
from sqlalchemy import Binary, Column, Integer, String
from app import db, login_manager from apps import db, login_manager
from app.base.util import hash_pass from apps.authentication.util import hash_pass
class User(db.Model, UserMixin): class Users(db.Model, UserMixin):
__tablename__ = 'User' __tablename__ = 'Users'
id = Column(Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
username = Column(String, unique=True) username = db.Column(db.String(64), unique=True)
email = Column(String, unique=True) email = db.Column(db.String(64), unique=True)
password = Column(Binary) password = db.Column(db.LargeBinary)
def __init__(self, **kwargs): def __init__(self, **kwargs):
for property, value in kwargs.items(): for property, value in kwargs.items():
@ -39,10 +38,11 @@ class User(db.Model, UserMixin):
@login_manager.user_loader @login_manager.user_loader
def user_loader(id): def user_loader(id):
return User.query.filter_by(id=id).first() return Users.query.filter_by(id=id).first()
@login_manager.request_loader @login_manager.request_loader
def request_loader(request): def request_loader(request):
username = request.form.get('username') username = request.form.get('username')
user = User.query.filter_by(username=username).first() user = Users.query.filter_by(username=username).first()
return user if user else None return user if user else None

View File

@ -0,0 +1,127 @@
# -*- encoding: utf-8 -*-
"""
Copyright (c) 2019 - present AppSeed.us
"""
from flask import render_template, redirect, request, url_for
from flask_login import (
current_user,
login_user,
logout_user
)
from apps import db, login_manager
from apps.authentication import blueprint
from apps.authentication.forms import LoginForm, CreateAccountForm
from apps.authentication.models import Users
from apps.authentication.util import verify_pass
@blueprint.route('/')
def route_default():
return redirect(url_for('authentication_blueprint.login'))
# Login & Registration
@blueprint.route('/login', methods=['GET', 'POST'])
def login():
login_form = LoginForm(request.form)
if 'login' in request.form:
# read form data
username = request.form['username']
password = request.form['password']
# Locate user
user = Users.query.filter_by(username=username).first()
# Check the password
if user and verify_pass(password, user.password):
login_user(user)
return redirect(url_for('authentication_blueprint.route_default'))
# Something (user or pass) is not ok
return render_template('accounts/login.html',
segment = 'login',
msg='Wrong user or password',
form=login_form)
if not current_user.is_authenticated:
return render_template('accounts/login.html',
segment = 'login',
form=login_form)
return redirect(url_for('home_blueprint.index'))
@blueprint.route('/register', methods=['GET', 'POST'])
def register():
create_account_form = CreateAccountForm(request.form)
if 'register' in request.form:
username = request.form['username']
email = request.form['email']
# Check usename exists
user = Users.query.filter_by(username=username).first()
if user:
return render_template('accounts/register.html',
msg='Username already registered',
segment = 'register',
success=False,
form=create_account_form)
# Check email exists
user = Users.query.filter_by(email=email).first()
if user:
return render_template('accounts/register.html',
msg='Email already registered',
segment = 'register',
success=False,
form=create_account_form)
# else we can create the user
user = Users(**request.form)
db.session.add(user)
db.session.commit()
return render_template('accounts/register.html',
msg='User created please <a href="/login">login</a>',
segment = 'register',
success=True,
form=create_account_form)
else:
return render_template( 'accounts/register.html',
segment = 'register',
form=create_account_form)
@blueprint.route('/logout')
def logout():
logout_user()
return redirect(url_for('authentication_blueprint.login'))
# Errors
@login_manager.unauthorized_handler
def unauthorized_handler():
return render_template('home/page-403.html'), 403
@blueprint.errorhandler(403)
def access_forbidden(error):
return render_template('home/page-403.html'), 403
@blueprint.errorhandler(404)
def not_found_error(error):
return render_template('home/page-404.html'), 404
@blueprint.errorhandler(500)
def internal_error(error):
return render_template('home/page-500.html'), 500

View File

@ -3,20 +3,26 @@
Copyright (c) 2019 - present AppSeed.us Copyright (c) 2019 - present AppSeed.us
""" """
import hashlib, binascii, os import os
import hashlib
import binascii
# Inspiration -> https://www.vitoshacademy.com/hashing-passwords-in-python/ # Inspiration -> https://www.vitoshacademy.com/hashing-passwords-in-python/
def hash_pass(password): def hash_pass(password):
"""Hash a password for storing.""" """Hash a password for storing."""
salt = hashlib.sha256(os.urandom(60)).hexdigest().encode('ascii') salt = hashlib.sha256(os.urandom(60)).hexdigest().encode('ascii')
pwdhash = hashlib.pbkdf2_hmac('sha512', password.encode('utf-8'), pwdhash = hashlib.pbkdf2_hmac('sha512', password.encode('utf-8'),
salt, 100000) salt, 100000)
pwdhash = binascii.hexlify(pwdhash) pwdhash = binascii.hexlify(pwdhash)
return (salt + pwdhash) # return bytes return (salt + pwdhash) # return bytes
def verify_pass(provided_password, stored_password): def verify_pass(provided_password, stored_password):
"""Verify a stored password against one provided by user""" """Verify a stored password against one provided by user"""
stored_password = stored_password.decode('ascii') stored_password = stored_password.decode('ascii')
salt = stored_password[:64] salt = stored_password[:64]
stored_password = stored_password[64:] stored_password = stored_password[64:]
@ -26,4 +32,3 @@ def verify_pass(provided_password, stored_password):
100000) 100000)
pwdhash = binascii.hexlify(pwdhash).decode('ascii') pwdhash = binascii.hexlify(pwdhash).decode('ascii')
return pwdhash == stored_password return pwdhash == stored_password

View File

@ -17,6 +17,7 @@ class Config(object):
SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'db.sqlite3') SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'db.sqlite3')
SQLALCHEMY_TRACK_MODIFICATIONS = False SQLALCHEMY_TRACK_MODIFICATIONS = False
class ProductionConfig(Config): class ProductionConfig(Config):
DEBUG = False DEBUG = False
@ -35,9 +36,11 @@ class ProductionConfig(Config):
config('DB_NAME', default='appseed-flask') config('DB_NAME', default='appseed-flask')
) )
class DebugConfig(Config): class DebugConfig(Config):
DEBUG = True DEBUG = True
# Load all possible configurations # Load all possible configurations
config_dict = { config_dict = {
'Production': ProductionConfig, 'Production': ProductionConfig,

View File

@ -8,7 +8,5 @@ from flask import Blueprint
blueprint = Blueprint( blueprint = Blueprint(
'home_blueprint', 'home_blueprint',
__name__, __name__,
url_prefix='', url_prefix=''
template_folder='templates',
static_folder='static'
) )

53
apps/home/routes.py Normal file
View File

@ -0,0 +1,53 @@
# -*- encoding: utf-8 -*-
"""
Copyright (c) 2019 - present AppSeed.us
"""
from apps.home import blueprint
from flask import render_template, request
from flask_login import login_required
from jinja2 import TemplateNotFound
@blueprint.route('/index')
@login_required
def index():
return render_template('home/index.html', segment='index')
@blueprint.route('/<template>')
@login_required
def route_template(template):
try:
if not template.endswith('.html'):
pass
# Detect the current page
segment = get_segment(request)
# Serve the file (if exists) from app/templates/home/FILE.html
return render_template("home/" + template, segment=segment)
except TemplateNotFound:
return render_template('home/page-404.html'), 404
except:
return render_template('home/page-500.html'), 500
# Helper - Extract current page name from request
def get_segment(request):
try:
segment = request.path.split('/')[-1]
if segment == '':
segment = 'index'
return segment
except:
return None

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,59 @@
/*
=========================================================
* AppSeed - Simple SCSS compiler via Gulp
=========================================================
*/
var autoprefixer = require('gulp-autoprefixer');
var browserSync = require('browser-sync').create();
var cleanCss = require('gulp-clean-css');
var gulp = require('gulp');
const npmDist = require('gulp-npm-dist');
var sass = require('gulp-sass')(require('node-sass'));
var wait = require('gulp-wait');
var sourcemaps = require('gulp-sourcemaps');
var rename = require("gulp-rename");
// Define COMMON paths
const paths = {
src: {
base: './',
css: './css',
scss: './scss',
node_modules: './node_modules/',
vendor: './vendor'
}
};
// Compile SCSS
gulp.task('scss', function() {
return gulp.src([paths.src.scss + '/black-dashboard.scss'])
.pipe(wait(500))
.pipe(sourcemaps.init())
.pipe(sass().on('error', sass.logError))
.pipe(autoprefixer({
overrideBrowserslist: ['> 1%']
}))
.pipe(sourcemaps.write('.'))
.pipe(gulp.dest(paths.src.css))
.pipe(browserSync.stream());
});
// Minify CSS
gulp.task('minify:css', function() {
return gulp.src([
paths.src.css + '/black-dashboard.css'
])
.pipe(cleanCss())
.pipe(rename(function(path) {
// Updates the object in-place
path.extname = ".min.css";
}))
.pipe(gulp.dest(paths.src.css))
});
// Default Task: Compile SCSS and minify the result
gulp.task('default', gulp.series('scss', 'minify:css'));

View File

Before

Width:  |  Height:  |  Size: 1.6 MiB

After

Width:  |  Height:  |  Size: 1.6 MiB

View File

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 37 KiB

View File

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 39 KiB

View File

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

Before

Width:  |  Height:  |  Size: 208 KiB

After

Width:  |  Height:  |  Size: 208 KiB

View File

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

Before

Width:  |  Height:  |  Size: 84 KiB

After

Width:  |  Height:  |  Size: 84 KiB

View File

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

Before

Width:  |  Height:  |  Size: 655 KiB

After

Width:  |  Height:  |  Size: 655 KiB

View File

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 38 KiB

View File

Before

Width:  |  Height:  |  Size: 73 KiB

After

Width:  |  Height:  |  Size: 73 KiB

View File

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 75 KiB

View File

Before

Width:  |  Height:  |  Size: 8.0 KiB

After

Width:  |  Height:  |  Size: 8.0 KiB

View File

@ -1,2 +1 @@
var transparent=!0,transparentDemo=!0,fixedTop=!1,navbar_initialized=!1,backgroundOrange=!1,sidebar_mini_active=!1,toggle_initialized=!1,$html=$("html"),$body=$("body"),$navbar_minimize_fixed=$(".navbar-minimize-fixed"),$collapse=$(".collapse"),$navbar=$(".navbar"),$tagsinput=$(".tagsinput"),$selectpicker=$(".selectpicker"),$navbar_color=$(".navbar[color-on-scroll]"),$full_screen_map=$(".full-screen-map"),$datetimepicker=$(".datetimepicker"),$datepicker=$(".datepicker"),$timepicker=$(".timepicker"),seq=0,delays=80,durations=500,seq2=0,delays2=80,durations2=500;function hexToRGB(a,e){var i=parseInt(a.slice(1,3),16),n=parseInt(a.slice(3,5),16),s=parseInt(a.slice(5,7),16);return e?"rgba("+i+", "+n+", "+s+", "+e+")":"rgb("+i+", "+n+", "+s+")"}!function(){if(-1<navigator.platform.indexOf("Win")){if(0!=$(".main-panel").length)new PerfectScrollbar(".main-panel",{wheelSpeed:2,wheelPropagation:!0,minScrollbarLength:20,suppressScrollX:!0});if(0!=$(".sidebar .sidebar-wrapper").length){new PerfectScrollbar(".sidebar .sidebar-wrapper");$(".table-responsive").each(function(){new PerfectScrollbar($(this)[0])})}$html.addClass("perfect-scrollbar-on")}else $html.addClass("perfect-scrollbar-off")}(),$(document).ready(function(){var a=$(".row").offset();(-1<navigator.platform.indexOf("Win")?$(".ps"):$(window)).scroll(function(){50<$(this).scrollTop()?$(".navbar-minimize-fixed").css("opacity","1"):$(".navbar-minimize-fixed").css("opacity","0")}),$(document).scroll(function(){$(this).scrollTop()>a.top?$(".navbar-minimize-fixed").css("opacity","1"):$(".navbar-minimize-fixed").css("opacity","0")}),0==$(".full-screen-map").length&&0==$(".bd-docs").length&&$(".collapse").on("show.bs.collapse",function(){$(this).closest(".navbar").removeClass("navbar-transparent").addClass("bg-white")}).on("hide.bs.collapse",function(){$(this).closest(".navbar").addClass("navbar-transparent").removeClass("bg-white")}),blackDashboard.initMinimizeSidebar(),$navbar=$(".navbar[color-on-scroll]"),scroll_distance=$navbar.attr("color-on-scroll")||500,0!=$(".navbar[color-on-scroll]").length&&(blackDashboard.checkScrollForTransparentNavbar(),$(window).on("scroll",blackDashboard.checkScrollForTransparentNavbar)),$(".form-control").on("focus",function(){$(this).parent(".input-group").addClass("input-group-focus")}).on("blur",function(){$(this).parent(".input-group").removeClass("input-group-focus")}),$(".bootstrap-switch").each(function(){$this=$(this),data_on_label=$this.data("on-label")||"",data_off_label=$this.data("off-label")||"",$this.bootstrapSwitch({onText:data_on_label,offText:data_off_label})})}),$(document).on("click",".navbar-toggle",function(){var a=$(this);if(1==blackDashboard.misc.navbar_menu_visible)$html.removeClass("nav-open"),blackDashboard.misc.navbar_menu_visible=0,setTimeout(function(){a.removeClass("toggled"),$(".bodyClick").remove()},550);else{setTimeout(function(){a.addClass("toggled")},580);$('<div class="bodyClick"></div>').appendTo("body").click(function(){$html.removeClass("nav-open"),blackDashboard.misc.navbar_menu_visible=0,setTimeout(function(){a.removeClass("toggled"),$(".bodyClick").remove()},550)}),$html.addClass("nav-open"),blackDashboard.misc.navbar_menu_visible=1}}),$(window).resize(function(){if(seq=seq2=0,0==$full_screen_map.length&&0==$(".bd-docs").length){var a=$navbar.find('[data-toggle="collapse"]').attr("aria-expanded");$navbar.hasClass("bg-white")&&991<$(window).width()?$navbar.removeClass("bg-white").addClass("navbar-transparent"):$navbar.hasClass("navbar-transparent")&&$(window).width()<991&&"false"!=a&&$navbar.addClass("bg-white").removeClass("navbar-transparent")}}),blackDashboard={misc:{navbar_menu_visible:0},initMinimizeSidebar:function(){0!=$(".sidebar-mini").length&&(sidebar_mini_active=!0),$("#minimizeSidebar").click(function(){$(this);1==sidebar_mini_active?($("body").removeClass("sidebar-mini"),sidebar_mini_active=!1,blackDashboard.showSidebarMessage("Sidebar mini deactivated...")):($("body").addClass("sidebar-mini"),sidebar_mini_active=!0,blackDashboard.showSidebarMessage("Sidebar mini activated..."));var a=setInterval(function(){window.dispatchEvent(new Event("resize"))},180);setTimeout(function(){clearInterval(a)},1e3)})},showSidebarMessage:function(a){try{$.notify({icon:"tim-icons ui-1_bell-53",message:a},{type:"info",timer:4e3,placement:{from:"top",align:"right"}})}catch(a){console.log("Notify library is missing, please make sure you have the notifications library added.")}}}; var transparent=!0,transparentDemo=!0,fixedTop=!1,navbar_initialized=!1,backgroundOrange=!1,sidebar_mini_active=!1,toggle_initialized=!1,$html=$("html"),$body=$("body"),$navbar_minimize_fixed=$(".navbar-minimize-fixed"),$collapse=$(".collapse"),$navbar=$(".navbar"),$tagsinput=$(".tagsinput"),$selectpicker=$(".selectpicker"),$navbar_color=$(".navbar[color-on-scroll]"),$full_screen_map=$(".full-screen-map"),$datetimepicker=$(".datetimepicker"),$datepicker=$(".datepicker"),$timepicker=$(".timepicker"),seq=0,delays=80,durations=500,seq2=0,delays2=80,durations2=500;function hexToRGB(a,e){var i=parseInt(a.slice(1,3),16),n=parseInt(a.slice(3,5),16),s=parseInt(a.slice(5,7),16);return e?"rgba("+i+", "+n+", "+s+", "+e+")":"rgb("+i+", "+n+", "+s+")"}!function(){if(-1<navigator.platform.indexOf("Win")){if(0!=$(".main-panel").length)new PerfectScrollbar(".main-panel",{wheelSpeed:2,wheelPropagation:!0,minScrollbarLength:20,suppressScrollX:!0});if(0!=$(".sidebar .sidebar-wrapper").length){new PerfectScrollbar(".sidebar .sidebar-wrapper");$(".table-responsive").each(function(){new PerfectScrollbar($(this)[0])})}$html.addClass("perfect-scrollbar-on")}else $html.addClass("perfect-scrollbar-off")}(),$(document).ready(function(){var a=$(".row").offset();(-1<navigator.platform.indexOf("Win")?$(".ps"):$(window)).scroll(function(){50<$(this).scrollTop()?$(".navbar-minimize-fixed").css("opacity","1"):$(".navbar-minimize-fixed").css("opacity","0")}),$(document).scroll(function(){$(this).scrollTop()>a.top?$(".navbar-minimize-fixed").css("opacity","1"):$(".navbar-minimize-fixed").css("opacity","0")}),0==$(".full-screen-map").length&&0==$(".bd-docs").length&&$(".collapse").on("show.bs.collapse",function(){$(this).closest(".navbar").removeClass("navbar-transparent").addClass("bg-white")}).on("hide.bs.collapse",function(){$(this).closest(".navbar").addClass("navbar-transparent").removeClass("bg-white")}),blackDashboard.initMinimizeSidebar(),$navbar=$(".navbar[color-on-scroll]"),scroll_distance=$navbar.attr("color-on-scroll")||500,0!=$(".navbar[color-on-scroll]").length&&(blackDashboard.checkScrollForTransparentNavbar(),$(window).on("scroll",blackDashboard.checkScrollForTransparentNavbar)),$(".form-control").on("focus",function(){$(this).parent(".input-group").addClass("input-group-focus")}).on("blur",function(){$(this).parent(".input-group").removeClass("input-group-focus")}),$(".bootstrap-switch").each(function(){$this=$(this),data_on_label=$this.data("on-label")||"",data_off_label=$this.data("off-label")||"",$this.bootstrapSwitch({onText:data_on_label,offText:data_off_label})})}),$(document).on("click",".navbar-toggle",function(){var a=$(this);if(1==blackDashboard.misc.navbar_menu_visible)$html.removeClass("nav-open"),blackDashboard.misc.navbar_menu_visible=0,setTimeout(function(){a.removeClass("toggled"),$(".bodyClick").remove()},550);else{setTimeout(function(){a.addClass("toggled")},580);$('<div class="bodyClick"></div>').appendTo("body").click(function(){$html.removeClass("nav-open"),blackDashboard.misc.navbar_menu_visible=0,setTimeout(function(){a.removeClass("toggled"),$(".bodyClick").remove()},550)}),$html.addClass("nav-open"),blackDashboard.misc.navbar_menu_visible=1}}),$(window).resize(function(){if(seq=seq2=0,0==$full_screen_map.length&&0==$(".bd-docs").length){var a=$navbar.find('[data-toggle="collapse"]').attr("aria-expanded");$navbar.hasClass("bg-white")&&991<$(window).width()?$navbar.removeClass("bg-white").addClass("navbar-transparent"):$navbar.hasClass("navbar-transparent")&&$(window).width()<991&&"false"!=a&&$navbar.addClass("bg-white").removeClass("navbar-transparent")}}),blackDashboard={misc:{navbar_menu_visible:0},initMinimizeSidebar:function(){0!=$(".sidebar-mini").length&&(sidebar_mini_active=!0),$("#minimizeSidebar").click(function(){$(this);1==sidebar_mini_active?($("body").removeClass("sidebar-mini"),sidebar_mini_active=!1,blackDashboard.showSidebarMessage("Sidebar mini deactivated...")):($("body").addClass("sidebar-mini"),sidebar_mini_active=!0,blackDashboard.showSidebarMessage("Sidebar mini activated..."));var a=setInterval(function(){window.dispatchEvent(new Event("resize"))},180);setTimeout(function(){clearInterval(a)},1e3)})},showSidebarMessage:function(a){try{$.notify({icon:"tim-icons ui-1_bell-53",message:a},{type:"info",timer:4e3,placement:{from:"top",align:"right"}})}catch(a){console.log("Notify library is missing, please make sure you have the notifications library added.")}}};
//# sourceMappingURL=_site_dashboard_free/assets/js/dashboard-free.js.map

View File

@ -0,0 +1,42 @@
{
"name": "generic-compilation-script",
"version": "1.0.0",
"description": "Generic Builder",
"main": "gulpfile.js",
"author": "AppSeed",
"keywords": [
"css",
"sass",
"gulp",
"web"
],
"homepage": "https://appseed.us",
"repository": {
"type": "git",
"url": "https://github.com/app-generator"
},
"bugs": {
"email": "support@appseed.us"
},
"license": "MIT License",
"devDependencies": {
"browser-sync": "^2.27.4",
"del": "^6.0.0",
"gulp": "^4.0.2",
"gulp-autoprefixer": "^8.0.0",
"gulp-clean-css": "^4.3.0",
"gulp-cssbeautify": "^3.0.0",
"node-sass": "^6.0.1",
"gulp-file-include": "^2.3.0",
"gulp-header": "^2.0.9",
"gulp-htmlmin": "^5.0.1",
"gulp-npm-dist": "^1.0.3",
"gulp-plumber": "^1.2.1",
"gulp-rename": "^2.0.0",
"gulp-sass": "^5.0.0",
"gulp-sourcemaps": "^3.0.0",
"gulp-uglify": "^3.0.2",
"gulp-wait": "^0.0.2",
"merge-stream": "^2.0.0"
}
}

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