diff options
author | Navan Chauhan <navanchauhan@gmail.com> | 2023-11-15 20:34:41 -0700 |
---|---|---|
committer | Navan Chauhan <navanchauhan@gmail.com> | 2023-11-15 20:34:41 -0700 |
commit | 3743f7056dcdbe1a16a00418ea10ebef2669cf61 (patch) | |
tree | 298657931ead09efbcf53ccb08bfc3ee501623c1 /app | |
parent | 81834656747ead193334a36d8f49045cc214b53e (diff) |
Diffstat (limited to 'app')
-rw-r--r-- | app/__init__.py | 73 | ||||
-rw-r--r-- | app/auth/__init__.py | 5 | ||||
-rw-r--r-- | app/auth/routes.py | 55 | ||||
-rw-r--r-- | app/extensions.py | 6 | ||||
-rw-r--r-- | app/main/__init__.py | 5 | ||||
-rw-r--r-- | app/main/routes.py | 12 | ||||
-rw-r--r-- | app/models/user.py | 13 | ||||
-rw-r--r-- | app/static/css/input.css | 3 | ||||
-rw-r--r-- | app/static/css/output.css | 1857 | ||||
-rw-r--r-- | app/templates/auth/login.html | 61 | ||||
-rw-r--r-- | app/templates/auth/signup.html | 65 | ||||
-rw-r--r-- | app/templates/base.html | 70 | ||||
-rw-r--r-- | app/templates/index.html | 50 | ||||
-rw-r--r-- | app/templates/main/dashboard.html | 19 |
14 files changed, 2294 insertions, 0 deletions
diff --git a/app/__init__.py b/app/__init__.py new file mode 100644 index 0000000..070c02a --- /dev/null +++ b/app/__init__.py @@ -0,0 +1,73 @@ +import click + +from flask import Flask +from flask.cli import AppGroup +from config import Config +from app.extensions import db, Migrate, LoginManager + +import flask_login +from flask_admin import Admin +from flask_admin.contrib.sqla import ModelView + +class ModelView(ModelView): + def is_accessible(self): + is_admin = False + try: + is_admin = flask_login.current_user.admin + except AttributeError: + is_admin = False + return is_admin + +def create_app(config_class=Config): + app = Flask(__name__) + app.config.from_object(config_class) + + # Flask Extensions + db.init_app(app) + migrate = Migrate(app, db) + + login_manager = LoginManager() + login_manager.login_view = 'auth.login' + login_manager.login_message = "You need to be logged in to view the page..." + login_manager.init_app(app) + + from app.models.user import User + @login_manager.user_loader + def load_user(user_id): + return User.query.get(int(user_id)) + + admin = Admin(app, name="Flask-App", template_mode="bootstrap4") + admin.add_view(ModelView(User, db.session)) + + # CLI Commands + database_cli = AppGroup('database') + + @database_cli.command('create') + def create_database(): + db.create_all() + + @database_cli.command('delete') + def delete_database(): + db.drop_all() + + @database_cli.command('superuser') + @click.argument('user_email') + def make_superuser(user_email): + user = User.query.filter_by(email=user_email).first() + if user: + print(f"Making {user.name} into admin") + user.admin = True + db.session.commit() + else: + print("User not found with email f{user_email}") + + app.cli.add_command(database_cli) + # Blueprints + + from app.auth import bp as auth_routes_bp + app.register_blueprint(auth_routes_bp) + + from app.main import bp as main_bp + app.register_blueprint(main_bp) + + return app diff --git a/app/auth/__init__.py b/app/auth/__init__.py new file mode 100644 index 0000000..088b033 --- /dev/null +++ b/app/auth/__init__.py @@ -0,0 +1,5 @@ +from flask import Blueprint + +bp = Blueprint('auth', __name__) + +from app.auth import routes diff --git a/app/auth/routes.py b/app/auth/routes.py new file mode 100644 index 0000000..792626e --- /dev/null +++ b/app/auth/routes.py @@ -0,0 +1,55 @@ +from flask import render_template, request, url_for, redirect, flash +from werkzeug.security import generate_password_hash, check_password_hash +from flask_login import login_user, current_user, logout_user, login_required + +from app.models.user import User +from app.extensions import db +from app.auth import bp + +@bp.route('/login') +def login(): + return render_template('auth/login.html') + +@bp.route('/login', methods=['POST']) +def login_post(): + email = request.form.get('email') + password = request.form.get('password') + remember = True if request.form.get('remember') else False + + user = User.query.filter_by(email=email).first() + + if not user or not check_password_hash(user.password, password): + flash('Please check your login details and try again.') + return redirect(url_for('auth.login')) + + login_user(user, remember=remember) + return redirect(url_for('main.dashboard')) + +@bp.route('/signup') +def signup(): + return render_template('auth/signup.html') + +@bp.route('/signup', methods=['POST']) +def signup_post(): + email = request.form.get('email') + name = request.form.get('name') + password = request.form.get('password') + + user = User.query.filter_by(email=email).first() + + if user: + flash('Email address already exists') + return redirect(url_for('auth.signup')) + + new_user = User(email=email, name=name, password=generate_password_hash(password, method='pbkdf2')) + + db.session.add(new_user) + db.session.commit() + + return redirect(url_for('auth.login')) + +@bp.route('/logout') +@login_required +def logout(): + logout_user() + return redirect(url_for('auth.login')) diff --git a/app/extensions.py b/app/extensions.py new file mode 100644 index 0000000..ecdff8d --- /dev/null +++ b/app/extensions.py @@ -0,0 +1,6 @@ +from flask_sqlalchemy import SQLAlchemy +from flask_migrate import Migrate +from flask_bcrypt import Bcrypt +from flask_login import LoginManager + +db = SQLAlchemy() diff --git a/app/main/__init__.py b/app/main/__init__.py new file mode 100644 index 0000000..3b580b0 --- /dev/null +++ b/app/main/__init__.py @@ -0,0 +1,5 @@ +from flask import Blueprint + +bp = Blueprint('main', __name__) + +from app.main import routes diff --git a/app/main/routes.py b/app/main/routes.py new file mode 100644 index 0000000..8657ddb --- /dev/null +++ b/app/main/routes.py @@ -0,0 +1,12 @@ +from flask import render_template +from flask_login import login_required, current_user +from app.main import bp + +@bp.route('/') +def index(): + return render_template('index.html') + +@bp.route('/dashboard') +@login_required +def dashboard(): + return render_template('main/dashboard.html', name=current_user.name) diff --git a/app/models/user.py b/app/models/user.py new file mode 100644 index 0000000..1156515 --- /dev/null +++ b/app/models/user.py @@ -0,0 +1,13 @@ +from flask_login import UserMixin +from app.extensions import db + +class User(UserMixin, db.Model): + id = db.Column(db.Integer(), primary_key=True) + name = db.Column(db.String(), nullable=False) + email = db.Column(db.String(), nullable=False) + password = db.Column(db.String(), nullable=False) + admin = db.Column(db.Boolean(), nullable=False, default=False) + email_confirmed = db.Column(db.Boolean(), nullable=False, default=False) + + def __repr__(self): + return f'<User {self.name}>' diff --git a/app/static/css/input.css b/app/static/css/input.css new file mode 100644 index 0000000..bd6213e --- /dev/null +++ b/app/static/css/input.css @@ -0,0 +1,3 @@ +@tailwind base; +@tailwind components; +@tailwind utilities;
\ No newline at end of file diff --git a/app/static/css/output.css b/app/static/css/output.css new file mode 100644 index 0000000..e31126a --- /dev/null +++ b/app/static/css/output.css @@ -0,0 +1,1857 @@ +/* +! tailwindcss v3.3.5 | MIT License | https://tailwindcss.com +*/ + +/* +1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4) +2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116) +*/ + +*, +::before, +::after { + box-sizing: border-box; + /* 1 */ + border-width: 0; + /* 2 */ + border-style: solid; + /* 2 */ + border-color: #e5e7eb; + /* 2 */ +} + +::before, +::after { + --tw-content: ''; +} + +/* +1. Use a consistent sensible line-height in all browsers. +2. Prevent adjustments of font size after orientation changes in iOS. +3. Use a more readable tab size. +4. Use the user's configured `sans` font-family by default. +5. Use the user's configured `sans` font-feature-settings by default. +6. Use the user's configured `sans` font-variation-settings by default. +*/ + +html { + line-height: 1.5; + /* 1 */ + -webkit-text-size-adjust: 100%; + /* 2 */ + -moz-tab-size: 4; + /* 3 */ + -o-tab-size: 4; + tab-size: 4; + /* 3 */ + font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + /* 4 */ + font-feature-settings: normal; + /* 5 */ + font-variation-settings: normal; + /* 6 */ +} + +/* +1. Remove the margin in all browsers. +2. Inherit line-height from `html` so users can set them as a class directly on the `html` element. +*/ + +body { + margin: 0; + /* 1 */ + line-height: inherit; + /* 2 */ +} + +/* +1. Add the correct height in Firefox. +2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655) +3. Ensure horizontal rules are visible by default. +*/ + +hr { + height: 0; + /* 1 */ + color: inherit; + /* 2 */ + border-top-width: 1px; + /* 3 */ +} + +/* +Add the correct text decoration in Chrome, Edge, and Safari. +*/ + +abbr:where([title]) { + -webkit-text-decoration: underline dotted; + text-decoration: underline dotted; +} + +/* +Remove the default font size and weight for headings. +*/ + +h1, +h2, +h3, +h4, +h5, +h6 { + font-size: inherit; + font-weight: inherit; +} + +/* +Reset links to optimize for opt-in styling instead of opt-out. +*/ + +a { + color: inherit; + text-decoration: inherit; +} + +/* +Add the correct font weight in Edge and Safari. +*/ + +b, +strong { + font-weight: bolder; +} + +/* +1. Use the user's configured `mono` font family by default. +2. Correct the odd `em` font sizing in all browsers. +*/ + +code, +kbd, +samp, +pre { + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + /* 1 */ + font-size: 1em; + /* 2 */ +} + +/* +Add the correct font size in all browsers. +*/ + +small { + font-size: 80%; +} + +/* +Prevent `sub` and `sup` elements from affecting the line height in all browsers. +*/ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +/* +1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297) +2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016) +3. Remove gaps between table borders by default. +*/ + +table { + text-indent: 0; + /* 1 */ + border-color: inherit; + /* 2 */ + border-collapse: collapse; + /* 3 */ +} + +/* +1. Change the font styles in all browsers. +2. Remove the margin in Firefox and Safari. +3. Remove default padding in all browsers. +*/ + +button, +input, +optgroup, +select, +textarea { + font-family: inherit; + /* 1 */ + font-feature-settings: inherit; + /* 1 */ + font-variation-settings: inherit; + /* 1 */ + font-size: 100%; + /* 1 */ + font-weight: inherit; + /* 1 */ + line-height: inherit; + /* 1 */ + color: inherit; + /* 1 */ + margin: 0; + /* 2 */ + padding: 0; + /* 3 */ +} + +/* +Remove the inheritance of text transform in Edge and Firefox. +*/ + +button, +select { + text-transform: none; +} + +/* +1. Correct the inability to style clickable types in iOS and Safari. +2. Remove default button styles. +*/ + +button, +[type='button'], +[type='reset'], +[type='submit'] { + -webkit-appearance: button; + /* 1 */ + background-color: transparent; + /* 2 */ + background-image: none; + /* 2 */ +} + +/* +Use the modern Firefox focus style for all focusable elements. +*/ + +:-moz-focusring { + outline: auto; +} + +/* +Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737) +*/ + +:-moz-ui-invalid { + box-shadow: none; +} + +/* +Add the correct vertical alignment in Chrome and Firefox. +*/ + +progress { + vertical-align: baseline; +} + +/* +Correct the cursor style of increment and decrement buttons in Safari. +*/ + +::-webkit-inner-spin-button, +::-webkit-outer-spin-button { + height: auto; +} + +/* +1. Correct the odd appearance in Chrome and Safari. +2. Correct the outline style in Safari. +*/ + +[type='search'] { + -webkit-appearance: textfield; + /* 1 */ + outline-offset: -2px; + /* 2 */ +} + +/* +Remove the inner padding in Chrome and Safari on macOS. +*/ + +::-webkit-search-decoration { + -webkit-appearance: none; +} + +/* +1. Correct the inability to style clickable types in iOS and Safari. +2. Change font properties to `inherit` in Safari. +*/ + +::-webkit-file-upload-button { + -webkit-appearance: button; + /* 1 */ + font: inherit; + /* 2 */ +} + +/* +Add the correct display in Chrome and Safari. +*/ + +summary { + display: list-item; +} + +/* +Removes the default spacing and border for appropriate elements. +*/ + +blockquote, +dl, +dd, +h1, +h2, +h3, +h4, +h5, +h6, +hr, +figure, +p, +pre { + margin: 0; +} + +fieldset { + margin: 0; + padding: 0; +} + +legend { + padding: 0; +} + +ol, +ul, +menu { + list-style: none; + margin: 0; + padding: 0; +} + +/* +Reset default styling for dialogs. +*/ + +dialog { + padding: 0; +} + +/* +Prevent resizing textareas horizontally by default. +*/ + +textarea { + resize: vertical; +} + +/* +1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300) +2. Set the default placeholder color to the user's configured gray 400 color. +*/ + +input::-moz-placeholder, textarea::-moz-placeholder { + opacity: 1; + /* 1 */ + color: #9ca3af; + /* 2 */ +} + +input::placeholder, +textarea::placeholder { + opacity: 1; + /* 1 */ + color: #9ca3af; + /* 2 */ +} + +/* +Set the default cursor for buttons. +*/ + +button, +[role="button"] { + cursor: pointer; +} + +/* +Make sure disabled buttons don't get the pointer cursor. +*/ + +:disabled { + cursor: default; +} + +/* +1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14) +2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210) + This can trigger a poorly considered lint error in some tools but is included by design. +*/ + +img, +svg, +video, +canvas, +audio, +iframe, +embed, +object { + display: block; + /* 1 */ + vertical-align: middle; + /* 2 */ +} + +/* +Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14) +*/ + +img, +video { + max-width: 100%; + height: auto; +} + +/* Make elements with the HTML hidden attribute stay hidden by default */ + +[hidden] { + display: none; +} + +[type='text'],input:where(:not([type])),[type='email'],[type='url'],[type='password'],[type='number'],[type='date'],[type='datetime-local'],[type='month'],[type='search'],[type='tel'],[type='time'],[type='week'],[multiple],textarea,select { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background-color: #fff; + border-color: #6b7280; + border-width: 1px; + border-radius: 0px; + padding-top: 0.5rem; + padding-right: 0.75rem; + padding-bottom: 0.5rem; + padding-left: 0.75rem; + font-size: 1rem; + line-height: 1.5rem; + --tw-shadow: 0 0 #0000; +} + +[type='text']:focus, input:where(:not([type])):focus, [type='email']:focus, [type='url']:focus, [type='password']:focus, [type='number']:focus, [type='date']:focus, [type='datetime-local']:focus, [type='month']:focus, [type='search']:focus, [type='tel']:focus, [type='time']:focus, [type='week']:focus, [multiple]:focus, textarea:focus, select:focus { + outline: 2px solid transparent; + outline-offset: 2px; + --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/); + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: #2563eb; + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); + border-color: #2563eb; +} + +input::-moz-placeholder, textarea::-moz-placeholder { + color: #6b7280; + opacity: 1; +} + +input::placeholder,textarea::placeholder { + color: #6b7280; + opacity: 1; +} + +::-webkit-datetime-edit-fields-wrapper { + padding: 0; +} + +::-webkit-date-and-time-value { + min-height: 1.5em; + text-align: inherit; +} + +::-webkit-datetime-edit { + display: inline-flex; +} + +::-webkit-datetime-edit,::-webkit-datetime-edit-year-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute-field,::-webkit-datetime-edit-second-field,::-webkit-datetime-edit-millisecond-field,::-webkit-datetime-edit-meridiem-field { + padding-top: 0; + padding-bottom: 0; +} + +select { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e"); + background-position: right 0.5rem center; + background-repeat: no-repeat; + background-size: 1.5em 1.5em; + padding-right: 2.5rem; + -webkit-print-color-adjust: exact; + print-color-adjust: exact; +} + +[multiple],[size]:where(select:not([size="1"])) { + background-image: initial; + background-position: initial; + background-repeat: unset; + background-size: initial; + padding-right: 0.75rem; + -webkit-print-color-adjust: unset; + print-color-adjust: unset; +} + +[type='checkbox'],[type='radio'] { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + padding: 0; + -webkit-print-color-adjust: exact; + print-color-adjust: exact; + display: inline-block; + vertical-align: middle; + background-origin: border-box; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; + flex-shrink: 0; + height: 1rem; + width: 1rem; + color: #2563eb; + background-color: #fff; + border-color: #6b7280; + border-width: 1px; + --tw-shadow: 0 0 #0000; +} + +[type='checkbox'] { + border-radius: 0px; +} + +[type='radio'] { + border-radius: 100%; +} + +[type='checkbox']:focus,[type='radio']:focus { + outline: 2px solid transparent; + outline-offset: 2px; + --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/); + --tw-ring-offset-width: 2px; + --tw-ring-offset-color: #fff; + --tw-ring-color: #2563eb; + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); +} + +[type='checkbox']:checked,[type='radio']:checked { + border-color: transparent; + background-color: currentColor; + background-size: 100% 100%; + background-position: center; + background-repeat: no-repeat; +} + +[type='checkbox']:checked { + background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e"); +} + +@media (forced-colors: active) { + [type='checkbox']:checked { + -webkit-appearance: auto; + -moz-appearance: auto; + appearance: auto; + } +} + +[type='radio']:checked { + background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e"); +} + +@media (forced-colors: active) { + [type='radio']:checked { + -webkit-appearance: auto; + -moz-appearance: auto; + appearance: auto; + } +} + +[type='checkbox']:checked:hover,[type='checkbox']:checked:focus,[type='radio']:checked:hover,[type='radio']:checked:focus { + border-color: transparent; + background-color: currentColor; +} + +[type='checkbox']:indeterminate { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3e%3cpath stroke='white' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3e%3c/svg%3e"); + border-color: transparent; + background-color: currentColor; + background-size: 100% 100%; + background-position: center; + background-repeat: no-repeat; +} + +@media (forced-colors: active) { + [type='checkbox']:indeterminate { + -webkit-appearance: auto; + -moz-appearance: auto; + appearance: auto; + } +} + +[type='checkbox']:indeterminate:hover,[type='checkbox']:indeterminate:focus { + border-color: transparent; + background-color: currentColor; +} + +[type='file'] { + background: unset; + border-color: inherit; + border-width: 0; + border-radius: 0; + padding: 0; + font-size: unset; + line-height: inherit; +} + +[type='file']:focus { + outline: 1px solid ButtonText; + outline: 1px auto -webkit-focus-ring-color; +} + +*, ::before, ::after { + --tw-border-spacing-x: 0; + --tw-border-spacing-y: 0; + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-pan-x: ; + --tw-pan-y: ; + --tw-pinch-zoom: ; + --tw-scroll-snap-strictness: proximity; + --tw-gradient-from-position: ; + --tw-gradient-via-position: ; + --tw-gradient-to-position: ; + --tw-ordinal: ; + --tw-slashed-zero: ; + --tw-numeric-figure: ; + --tw-numeric-spacing: ; + --tw-numeric-fraction: ; + --tw-ring-inset: ; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgb(59 130 246 / 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + --tw-blur: ; + --tw-brightness: ; + --tw-contrast: ; + --tw-grayscale: ; + --tw-hue-rotate: ; + --tw-invert: ; + --tw-saturate: ; + --tw-sepia: ; + --tw-drop-shadow: ; + --tw-backdrop-blur: ; + --tw-backdrop-brightness: ; + --tw-backdrop-contrast: ; + --tw-backdrop-grayscale: ; + --tw-backdrop-hue-rotate: ; + --tw-backdrop-invert: ; + --tw-backdrop-opacity: ; + --tw-backdrop-saturate: ; + --tw-backdrop-sepia: ; +} + +::backdrop { + --tw-border-spacing-x: 0; + --tw-border-spacing-y: 0; + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-pan-x: ; + --tw-pan-y: ; + --tw-pinch-zoom: ; + --tw-scroll-snap-strictness: proximity; + --tw-gradient-from-position: ; + --tw-gradient-via-position: ; + --tw-gradient-to-position: ; + --tw-ordinal: ; + --tw-slashed-zero: ; + --tw-numeric-figure: ; + --tw-numeric-spacing: ; + --tw-numeric-fraction: ; + --tw-ring-inset: ; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgb(59 130 246 / 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + --tw-blur: ; + --tw-brightness: ; + --tw-contrast: ; + --tw-grayscale: ; + --tw-hue-rotate: ; + --tw-invert: ; + --tw-saturate: ; + --tw-sepia: ; + --tw-drop-shadow: ; + --tw-backdrop-blur: ; + --tw-backdrop-brightness: ; + --tw-backdrop-contrast: ; + --tw-backdrop-grayscale: ; + --tw-backdrop-hue-rotate: ; + --tw-backdrop-invert: ; + --tw-backdrop-opacity: ; + --tw-backdrop-saturate: ; + --tw-backdrop-sepia: ; +} + +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border-width: 0; +} + +.static { + position: static; +} + +.absolute { + position: absolute; +} + +.relative { + position: relative; +} + +.-inset-0 { + inset: -0px; +} + +.-inset-0\.5 { + inset: -0.125rem; +} + +.inset-0 { + inset: 0px; +} + +.-inset-x-20 { + left: -5rem; + right: -5rem; +} + +.inset-x-0 { + left: 0px; + right: 0px; +} + +.inset-y-0 { + top: 0px; + bottom: 0px; +} + +.bottom-0 { + bottom: 0px; +} + +.left-0 { + left: 0px; +} + +.left-1 { + left: 0.25rem; +} + +.left-\[calc\(50\%-4rem\)\] { + left: calc(50% - 4rem); +} + +.top-1 { + top: 0.25rem; +} + +.top-10 { + top: 2.5rem; +} + +.isolate { + isolation: isolate; +} + +.-z-10 { + z-index: -10; +} + +.order-first { + order: -9999; +} + +.col-span-2 { + grid-column: span 2 / span 2; +} + +.col-start-2 { + grid-column-start: 2; +} + +.mx-auto { + margin-left: auto; + margin-right: auto; +} + +.mb-6 { + margin-bottom: 1.5rem; +} + +.mb-\[-12\%\] { + margin-bottom: -12%; +} + +.mt-1 { + margin-top: 0.25rem; +} + +.mt-10 { + margin-top: 2.5rem; +} + +.mt-16 { + margin-top: 4rem; +} + +.mt-2 { + margin-top: 0.5rem; +} + +.mt-24 { + margin-top: 6rem; +} + +.mt-32 { + margin-top: 8rem; +} + +.mt-6 { + margin-top: 1.5rem; +} + +.mt-8 { + margin-top: 2rem; +} + +.ml-3 { + margin-left: 0.75rem; +} + +.block { + display: block; +} + +.inline { + display: inline; +} + +.flex { + display: flex; +} + +.inline-flex { + display: inline-flex; +} + +.grid { + display: grid; +} + +.hidden { + display: none; +} + +.aspect-\[1108\/632\] { + aspect-ratio: 1108/632; +} + +.h-10 { + height: 2.5rem; +} + +.h-11 { + height: 2.75rem; +} + +.h-16 { + height: 4rem; +} + +.h-5 { + height: 1.25rem; +} + +.h-6 { + height: 1.5rem; +} + +.h-full { + height: 100%; +} + +.max-h-12 { + max-height: 3rem; +} + +.min-h-full { + min-height: 100%; +} + +.w-10 { + width: 2.5rem; +} + +.w-5 { + width: 1.25rem; +} + +.w-6 { + width: 1.5rem; +} + +.w-\[69\.25rem\] { + width: 69.25rem; +} + +.w-\[76rem\] { + width: 76rem; +} + +.w-auto { + width: auto; +} + +.w-full { + width: 100%; +} + +.max-w-2xl { + max-width: 42rem; +} + +.max-w-3xl { + max-width: 48rem; +} + +.max-w-7xl { + max-width: 80rem; +} + +.max-w-lg { + max-width: 32rem; +} + +.max-w-xl { + max-width: 36rem; +} + +.flex-1 { + flex: 1 1 0%; +} + +.flex-auto { + flex: 1 1 auto; +} + +.flex-none { + flex: none; +} + +.flex-shrink-0 { + flex-shrink: 0; +} + +.transform-gpu { + transform: translate3d(var(--tw-translate-x), var(--tw-translate-y), 0) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.list-disc { + list-style-type: disc; +} + +.grid-cols-1 { + grid-template-columns: repeat(1, minmax(0, 1fr)); +} + +.grid-cols-4 { + grid-template-columns: repeat(4, minmax(0, 1fr)); +} + +.flex-col { + flex-direction: column; +} + +.items-center { + align-items: center; +} + +.justify-center { + justify-content: center; +} + +.justify-between { + justify-content: space-between; +} + +.gap-x-6 { + -moz-column-gap: 1.5rem; + column-gap: 1.5rem; +} + +.gap-x-8 { + -moz-column-gap: 2rem; + column-gap: 2rem; +} + +.gap-y-10 { + row-gap: 2.5rem; +} + +.gap-y-16 { + row-gap: 4rem; +} + +.gap-y-3 { + row-gap: 0.75rem; +} + +.space-x-2 > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(0.5rem * var(--tw-space-x-reverse)); + margin-left: calc(0.5rem * calc(1 - var(--tw-space-x-reverse))); +} + +.space-x-4 > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(1rem * var(--tw-space-x-reverse)); + margin-left: calc(1rem * calc(1 - var(--tw-space-x-reverse))); +} + +.space-x-6 > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(1.5rem * var(--tw-space-x-reverse)); + margin-left: calc(1.5rem * calc(1 - var(--tw-space-x-reverse))); +} + +.space-y-1 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(0.25rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(0.25rem * var(--tw-space-y-reverse)); +} + +.space-y-6 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(1.5rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(1.5rem * var(--tw-space-y-reverse)); +} + +.overflow-hidden { + overflow: hidden; +} + +.overflow-visible { + overflow: visible; +} + +.rounded-full { + border-radius: 9999px; +} + +.rounded-lg { + border-radius: 0.5rem; +} + +.rounded-md { + border-radius: 0.375rem; +} + +.rounded-xl { + border-radius: 0.75rem; +} + +.border-0 { + border-width: 0px; +} + +.border-l { + border-left-width: 1px; +} + +.border-t { + border-top-width: 1px; +} + +.border-white\/10 { + border-color: rgb(255 255 255 / 0.1); +} + +.bg-gray-800 { + --tw-bg-opacity: 1; + background-color: rgb(31 41 55 / var(--tw-bg-opacity)); +} + +.bg-gray-900 { + --tw-bg-opacity: 1; + background-color: rgb(17 24 39 / var(--tw-bg-opacity)); +} + +.bg-indigo-500 { + --tw-bg-opacity: 1; + background-color: rgb(99 102 241 / var(--tw-bg-opacity)); +} + +.bg-indigo-500\/10 { + background-color: rgb(99 102 241 / 0.1); +} + +.bg-indigo-600 { + --tw-bg-opacity: 1; + background-color: rgb(79 70 229 / var(--tw-bg-opacity)); +} + +.bg-white { + --tw-bg-opacity: 1; + background-color: rgb(255 255 255 / var(--tw-bg-opacity)); +} + +.bg-white\/5 { + background-color: rgb(255 255 255 / 0.05); +} + +.bg-red-50 { + --tw-bg-opacity: 1; + background-color: rgb(254 242 242 / var(--tw-bg-opacity)); +} + +.bg-gradient-to-r { + background-image: linear-gradient(to right, var(--tw-gradient-stops)); +} + +.bg-gradient-to-t { + background-image: linear-gradient(to top, var(--tw-gradient-stops)); +} + +.from-\[\#80caff\] { + --tw-gradient-from: #80caff var(--tw-gradient-from-position); + --tw-gradient-to: rgb(128 202 255 / 0) var(--tw-gradient-to-position); + --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to); +} + +.from-gray-900 { + --tw-gradient-from: #111827 var(--tw-gradient-from-position); + --tw-gradient-to: rgb(17 24 39 / 0) var(--tw-gradient-to-position); + --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to); +} + +.to-\[\#4f46e5\] { + --tw-gradient-to: #4f46e5 var(--tw-gradient-to-position); +} + +.fill-gray-800\/20 { + fill: rgb(31 41 55 / 0.2); +} + +.stroke-white\/10 { + stroke: rgb(255 255 255 / 0.1); +} + +.object-contain { + -o-object-fit: contain; + object-fit: contain; +} + +.p-2 { + padding: 0.5rem; +} + +.p-4 { + padding: 1rem; +} + +.px-2 { + padding-left: 0.5rem; + padding-right: 0.5rem; +} + +.px-3 { + padding-left: 0.75rem; + padding-right: 0.75rem; +} + +.px-3\.5 { + padding-left: 0.875rem; + padding-right: 0.875rem; +} + +.px-6 { + padding-left: 1.5rem; + padding-right: 1.5rem; +} + +.py-1 { + padding-top: 0.25rem; + padding-bottom: 0.25rem; +} + +.py-1\.5 { + padding-top: 0.375rem; + padding-bottom: 0.375rem; +} + +.py-12 { + padding-top: 3rem; + padding-bottom: 3rem; +} + +.py-2 { + padding-top: 0.5rem; + padding-bottom: 0.5rem; +} + +.py-2\.5 { + padding-top: 0.625rem; + padding-bottom: 0.625rem; +} + +.py-32 { + padding-top: 8rem; + padding-bottom: 8rem; +} + +.px-4 { + padding-left: 1rem; + padding-right: 1rem; +} + +.py-10 { + padding-top: 2.5rem; + padding-bottom: 2.5rem; +} + +.pb-24 { + padding-bottom: 6rem; +} + +.pb-3 { + padding-bottom: 0.75rem; +} + +.pb-8 { + padding-bottom: 2rem; +} + +.pl-6 { + padding-left: 1.5rem; +} + +.pl-9 { + padding-left: 2.25rem; +} + +.pt-10 { + padding-top: 2.5rem; +} + +.pt-16 { + padding-top: 4rem; +} + +.pt-2 { + padding-top: 0.5rem; +} + +.pt-4 { + padding-top: 1rem; +} + +.pt-8 { + padding-top: 2rem; +} + +.pt-\[7\%\] { + padding-top: 7%; +} + +.pl-5 { + padding-left: 1.25rem; +} + +.text-center { + text-align: center; +} + +.text-2xl { + font-size: 1.5rem; + line-height: 2rem; +} + +.text-3xl { + font-size: 1.875rem; + line-height: 2.25rem; +} + +.text-4xl { + font-size: 2.25rem; + line-height: 2.5rem; +} + +.text-base { + font-size: 1rem; + line-height: 1.5rem; +} + +.text-lg { + font-size: 1.125rem; + line-height: 1.75rem; +} + +.text-sm { + font-size: 0.875rem; + line-height: 1.25rem; +} + +.text-xs { + font-size: 0.75rem; + line-height: 1rem; +} + +.font-bold { + font-weight: 700; +} + +.font-medium { + font-weight: 500; +} + +.font-semibold { + font-weight: 600; +} + +.leading-5 { + line-height: 1.25rem; +} + +.leading-6 { + line-height: 1.5rem; +} + +.leading-7 { + line-height: 1.75rem; +} + +.leading-8 { + line-height: 2rem; +} + +.leading-9 { + line-height: 2.25rem; +} + +.leading-tight { + line-height: 1.25; +} + +.tracking-tight { + letter-spacing: -0.025em; +} + +.text-gray-300 { + --tw-text-opacity: 1; + color: rgb(209 213 219 / var(--tw-text-opacity)); +} + +.text-gray-400 { + --tw-text-opacity: 1; + color: rgb(156 163 175 / var(--tw-text-opacity)); +} + +.text-gray-500 { + --tw-text-opacity: 1; + color: rgb(107 114 128 / var(--tw-text-opacity)); +} + +.text-gray-900 { + --tw-text-opacity: 1; + color: rgb(17 24 39 / var(--tw-text-opacity)); +} + +.text-indigo-400 { + --tw-text-opacity: 1; + color: rgb(129 140 248 / var(--tw-text-opacity)); +} + +.text-indigo-500 { + --tw-text-opacity: 1; + color: rgb(99 102 241 / var(--tw-text-opacity)); +} + +.text-indigo-600 { + --tw-text-opacity: 1; + color: rgb(79 70 229 / var(--tw-text-opacity)); +} + +.text-white { + --tw-text-opacity: 1; + color: rgb(255 255 255 / var(--tw-text-opacity)); +} + +.text-red-400 { + --tw-text-opacity: 1; + color: rgb(248 113 113 / var(--tw-text-opacity)); +} + +.text-red-700 { + --tw-text-opacity: 1; + color: rgb(185 28 28 / var(--tw-text-opacity)); +} + +.text-red-800 { + --tw-text-opacity: 1; + color: rgb(153 27 27 / var(--tw-text-opacity)); +} + +.opacity-20 { + opacity: 0.2; +} + +.shadow-2xl { + --tw-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.25); + --tw-shadow-colored: 0 25px 50px -12px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} + +.shadow-sm { + --tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05); + --tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} + +.ring-1 { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); +} + +.ring-inset { + --tw-ring-inset: inset; +} + +.ring-gray-300 { + --tw-ring-opacity: 1; + --tw-ring-color: rgb(209 213 219 / var(--tw-ring-opacity)); +} + +.ring-indigo-500\/20 { + --tw-ring-color: rgb(99 102 241 / 0.2); +} + +.ring-white\/10 { + --tw-ring-color: rgb(255 255 255 / 0.1); +} + +.blur-3xl { + --tw-blur: blur(64px); + filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); +} + +.\[mask-image\:radial-gradient\(100\%_100\%_at_top_right\2c white\2c transparent\)\] { + -webkit-mask-image: radial-gradient(100% 100% at top right,white,transparent); + mask-image: radial-gradient(100% 100% at top right,white,transparent); +} + +.placeholder\:text-gray-400::-moz-placeholder { + --tw-text-opacity: 1; + color: rgb(156 163 175 / var(--tw-text-opacity)); +} + +.placeholder\:text-gray-400::placeholder { + --tw-text-opacity: 1; + color: rgb(156 163 175 / var(--tw-text-opacity)); +} + +.hover\:bg-gray-100:hover { + --tw-bg-opacity: 1; + background-color: rgb(243 244 246 / var(--tw-bg-opacity)); +} + +.hover\:bg-gray-700:hover { + --tw-bg-opacity: 1; + background-color: rgb(55 65 81 / var(--tw-bg-opacity)); +} + +.hover\:bg-indigo-400:hover { + --tw-bg-opacity: 1; + background-color: rgb(129 140 248 / var(--tw-bg-opacity)); +} + +.hover\:bg-indigo-500:hover { + --tw-bg-opacity: 1; + background-color: rgb(99 102 241 / var(--tw-bg-opacity)); +} + +.hover\:text-gray-400:hover { + --tw-text-opacity: 1; + color: rgb(156 163 175 / var(--tw-text-opacity)); +} + +.hover\:text-indigo-500:hover { + --tw-text-opacity: 1; + color: rgb(99 102 241 / var(--tw-text-opacity)); +} + +.hover\:text-white:hover { + --tw-text-opacity: 1; + color: rgb(255 255 255 / var(--tw-text-opacity)); +} + +.focus\:outline-none:focus { + outline: 2px solid transparent; + outline-offset: 2px; +} + +.focus\:ring-2:focus { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); +} + +.focus\:ring-inset:focus { + --tw-ring-inset: inset; +} + +.focus\:ring-indigo-600:focus { + --tw-ring-opacity: 1; + --tw-ring-color: rgb(79 70 229 / var(--tw-ring-opacity)); +} + +.focus\:ring-white:focus { + --tw-ring-opacity: 1; + --tw-ring-color: rgb(255 255 255 / var(--tw-ring-opacity)); +} + +.focus-visible\:outline:focus-visible { + outline-style: solid; +} + +.focus-visible\:outline-2:focus-visible { + outline-width: 2px; +} + +.focus-visible\:outline-offset-2:focus-visible { + outline-offset: 2px; +} + +.focus-visible\:outline-indigo-400:focus-visible { + outline-color: #818cf8; +} + +.focus-visible\:outline-indigo-600:focus-visible { + outline-color: #4f46e5; +} + +.focus-visible\:outline-white:focus-visible { + outline-color: #fff; +} + +@media (min-width: 640px) { + .sm\:left-\[calc\(50\%-18rem\)\] { + left: calc(50% - 18rem); + } + + .sm\:col-start-2 { + grid-column-start: 2; + } + + .sm\:col-start-auto { + grid-column-start: auto; + } + + .sm\:mx-auto { + margin-left: auto; + margin-right: auto; + } + + .sm\:ml-6 { + margin-left: 1.5rem; + } + + .sm\:mt-16 { + margin-top: 4rem; + } + + .sm\:mt-20 { + margin-top: 5rem; + } + + .sm\:mt-24 { + margin-top: 6rem; + } + + .sm\:mt-32 { + margin-top: 8rem; + } + + .sm\:mt-56 { + margin-top: 14rem; + } + + .sm\:block { + display: block; + } + + .sm\:hidden { + display: none; + } + + .sm\:w-full { + width: 100%; + } + + .sm\:max-w-5xl { + max-width: 64rem; + } + + .sm\:max-w-sm { + max-width: 24rem; + } + + .sm\:max-w-xl { + max-width: 36rem; + } + + .sm\:grid-cols-2 { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .sm\:grid-cols-6 { + grid-template-columns: repeat(6, minmax(0, 1fr)); + } + + .sm\:items-stretch { + align-items: stretch; + } + + .sm\:justify-start { + justify-content: flex-start; + } + + .sm\:gap-x-10 { + -moz-column-gap: 2.5rem; + column-gap: 2.5rem; + } + + .sm\:gap-y-16 { + row-gap: 4rem; + } + + .sm\:px-6 { + padding-left: 1.5rem; + padding-right: 1.5rem; + } + + .sm\:py-40 { + padding-top: 10rem; + padding-bottom: 10rem; + } + + .sm\:pb-40 { + padding-bottom: 10rem; + } + + .sm\:pb-32 { + padding-bottom: 8rem; + } + + .sm\:text-center { + text-align: center; + } + + .sm\:text-4xl { + font-size: 2.25rem; + line-height: 2.5rem; + } + + .sm\:text-6xl { + font-size: 3.75rem; + line-height: 1; + } + + .sm\:text-sm { + font-size: 0.875rem; + line-height: 1.25rem; + } + + .sm\:leading-6 { + line-height: 1.5rem; + } +} + +@media (min-width: 768px) { + .md\:order-1 { + order: 1; + } + + .md\:order-2 { + order: 2; + } + + .md\:mt-0 { + margin-top: 0px; + } + + .md\:mt-24 { + margin-top: 6rem; + } + + .md\:flex { + display: flex; + } + + .md\:items-center { + align-items: center; + } + + .md\:justify-between { + justify-content: space-between; + } +} + +@media (min-width: 1024px) { + .lg\:left-48 { + left: 12rem; + } + + .lg\:top-\[calc\(50\%-30rem\)\] { + top: calc(50% - 30rem); + } + + .lg\:col-span-1 { + grid-column: span 1 / span 1; + } + + .lg\:mx-0 { + margin-left: 0px; + margin-right: 0px; + } + + .lg\:ml-10 { + margin-left: 2.5rem; + } + + .lg\:mr-0 { + margin-right: 0px; + } + + .lg\:mt-0 { + margin-top: 0px; + } + + .lg\:mt-16 { + margin-top: 4rem; + } + + .lg\:mt-24 { + margin-top: 6rem; + } + + .lg\:flex { + display: flex; + } + + .lg\:max-w-none { + max-width: none; + } + + .lg\:max-w-xl { + max-width: 36rem; + } + + .lg\:flex-none { + flex: none; + } + + .lg\:grid-cols-3 { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } + + .lg\:grid-cols-4 { + grid-template-columns: repeat(4, minmax(0, 1fr)); + } + + .lg\:grid-cols-5 { + grid-template-columns: repeat(5, minmax(0, 1fr)); + } + + .lg\:gap-x-8 { + -moz-column-gap: 2rem; + column-gap: 2rem; + } + + .lg\:gap-y-16 { + row-gap: 4rem; + } + + .lg\:px-8 { + padding-left: 2rem; + padding-right: 2rem; + } + + .lg\:py-40 { + padding-top: 10rem; + padding-bottom: 10rem; + } + + .lg\:pt-40 { + padding-top: 10rem; + } + + .lg\:pt-8 { + padding-top: 2rem; + } +} + +@media (min-width: 1280px) { + .xl\:left-\[calc\(50\%-24rem\)\] { + left: calc(50% - 24rem); + } + + .xl\:ml-32 { + margin-left: 8rem; + } +}
\ No newline at end of file diff --git a/app/templates/auth/login.html b/app/templates/auth/login.html new file mode 100644 index 0000000..aa9f7d6 --- /dev/null +++ b/app/templates/auth/login.html @@ -0,0 +1,61 @@ +{% extends 'base.html' %} + +{% block content %} + <!--{% block title %} Login {% endblock %}--> +{% with messages = get_flashed_messages() %} + {% if messages %} + <div class="rounded-md bg-red-50 p-4"> + <div class="flex"> + <div class="flex-shrink-0"> + <svg class="h-5 w-5 text-red-400" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"> + <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.28 7.22a.75.75 0 00-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 101.06 1.06L10 11.06l1.72 1.72a.75.75 0 101.06-1.06L11.06 10l1.72-1.72a.75.75 0 00-1.06-1.06L10 8.94 8.28 7.22z" clip-rule="evenodd" /> + </svg> + </div> + <div class="ml-3"> + <h3 class="text-sm font-medium text-red-800">{{ messages[0] }}</h3> + </div> + </div> +</div> + {% endif %} +{% endwith %} +<div class="flex min-h-full flex-col justify-center px-6 py-12 lg:px-8"> + <div class="sm:mx-auto sm:w-full sm:max-w-sm"> + <img class="mx-auto h-10 w-auto" src="https://tailwindui.com/img/logos/mark.svg?color=indigo&shade=600" alt="Your Company"> + <h2 class="mt-10 text-center text-2xl font-bold leading-9 tracking-tight text-gray-900">Sign in to your account</h2> + </div> + + <div class="mt-10 sm:mx-auto sm:w-full sm:max-w-sm"> + <form class="space-y-6" action="/login" method="POST"> + <div> + <label for="email" class="block text-sm font-medium leading-6 text-gray-900">Email address</label> + <div class="mt-2"> + <input id="email" name="email" type="email" autocomplete="email" required class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"> + </div> + </div> + + <div> + <div class="flex items-center justify-between"> + <label for="password" class="block text-sm font-medium leading-6 text-gray-900">Password</label> + <!-- + <div class="text-sm"> + <a href="#" class="font-semibold text-indigo-600 hover:text-indigo-500">Forgot password?</a> + </div>--> + </div> + <div class="mt-2"> + <input id="password" name="password" type="password" autocomplete="current-password" required class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"> + </div> + </div> + <div class="field"> + <label class="checkbox"> + <input type="checkbox" name="remember"> + Remember me + </label> + </div> + <div> + <button type="submit" class="flex w-full justify-center rounded-md bg-indigo-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600">Sign in</button> + </div> + </form> + </div> +</div> +{% endblock %} + diff --git a/app/templates/auth/signup.html b/app/templates/auth/signup.html new file mode 100644 index 0000000..666bdeb --- /dev/null +++ b/app/templates/auth/signup.html @@ -0,0 +1,65 @@ +{% extends 'base.html' %} + +{% block content %} + <!--<span class="title"><h1>{% block title %} Sign Up{% endblock %}</h1></span>--> +{% with messages = get_flashed_messages() %} + {% if messages %} + <div class="rounded-md bg-red-50 p-4"> + <div class="flex"> + <div class="flex-shrink-0"> + <svg class="h-5 w-5 text-red-400" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"> + <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.28 7.22a.75.75 0 00-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 101.06 1.06L10 11.06l1.72 1.72a.75.75 0 101.06-1.06L11.06 10l1.72-1.72a.75.75 0 00-1.06-1.06L10 8.94 8.28 7.22z" clip-rule="evenodd" /> + </svg> + </div> + <div class="ml-3"> + <h3 class="text-sm font-medium text-red-800">{{ messages[0] }}</h3> + </div> + </div> +</div> + {% endif %} +{% endwith %} + + +<div class="flex min-h-full flex-col justify-center px-6 py-12 lg:px-8"> + <div class="sm:mx-auto sm:w-full sm:max-w-sm"> + <img class="mx-auto h-10 w-auto" src="https://tailwindui.com/img/logos/mark.svg?color=indigo&shade=600" alt="Your Company"> + <h2 class="mt-10 text-center text-2xl font-bold leading-9 tracking-tight text-gray-900">Register for an Account</h2> + </div> + + <div class="mt-10 sm:mx-auto sm:w-full sm:max-w-sm"> + <form class="space-y-6" action="/signup" method="POST"> + <div> + <label for="email" class="block text-sm font-medium leading-6 text-gray-900">Email address</label> + <div class="mt-2"> + <input id="email" name="email" type="email" autocomplete="email" required class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"> + </div> + </div> + + <div> + <label for="name" class="block text-sm font-medium leading-6 text-gray-900">Name</label> + <div class="mt-2"> + <input id="name" name="name" type="text" required class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"> + </div> + </div> + + <div> + <div class="flex items-center justify-between"> + <label for="password" class="block text-sm font-medium leading-6 text-gray-900">Password</label> + <!-- + <div class="text-sm"> + <a href="#" class="font-semibold text-indigo-600 hover:text-indigo-500">Forgot password?</a> + </div>--> + </div> + <div class="mt-2"> + <input id="password" name="password" type="password" autocomplete="current-password" required class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"> + </div> + </div> + <div> + <button type="submit" class="flex w-full justify-center rounded-md bg-indigo-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600">Sign up</button> + </div> + </form> + </div> +</div> +{% endblock %} + + diff --git a/app/templates/base.html b/app/templates/base.html new file mode 100644 index 0000000..dfe0b87 --- /dev/null +++ b/app/templates/base.html @@ -0,0 +1,70 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8"> + <title>{% block title %} {% endblock %} - Flask-App</title> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <link href="{{url_for('static',filename='css/output.css')}}" rel="stylesheet"> + </head> + <body> +<nav class="bg-gray-800"> + <div class="mx-auto max-w-7xl px-2 sm:px-6 lg:px-8"> + <div class="relative flex h-16 items-center justify-between"> + <div class="absolute inset-y-0 left-0 flex items-center sm:hidden"> + <!-- Mobile menu button--> + <button type="button" class="relative inline-flex items-center justify-center rounded-md p-2 text-gray-400 hover:bg-gray-700 hover:text-white focus:outline-none focus:ring-2 focus:ring-inset focus:ring-white" aria-controls="mobile-menu" aria-expanded="false"> + <span class="absolute -inset-0.5"></span> + <span class="sr-only">Open main menu</span> + <!-- + Icon when menu is closed. + + Menu open: "hidden", Menu closed: "block" + --> + <svg class="block h-6 w-6" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true"> + <path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" /> + </svg> + <!-- + Icon when menu is open. + + Menu open: "block", Menu closed: "hidden" + --> + <svg class="hidden h-6 w-6" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true"> + <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" /> + </svg> + </button> + </div> + <div class="flex flex-1 items-center justify-center sm:items-stretch sm:justify-start"> + <div class="hidden sm:ml-6 sm:block"> + <div class="flex space-x-4"> + <!-- Current: "bg-gray-900 text-white", Default: "text-gray-300 hover:bg-gray-700 hover:text-white" --> + <a href="{{ url_for('main.index')}}" class="text-gray-300 hover:bg-gray-700 hover:text-white rounded-md px-3 py-2 text-sm font-medium">Home</a> + {% if not current_user.is_authenticated %} + <a href="{{ url_for('auth.login')}}" class="text-gray-300 hover:bg-gray-700 hover:text-white rounded-md px-3 py-2 text-sm font-medium">Login</a> + <a href="{{ url_for('auth.signup')}}" class="text-gray-300 hover:bg-gray-700 hover:text-white rounded-md px-3 py-2 text-sm font-medium">Register</a> + {% endif %} + {% if current_user.is_authenticated %} + <a href="{{ url_for('main.dashboard')}}" class="text-gray-300 hover:bg-gray-700 hover:text-white rounded-md px-3 py-2 text-sm font-medium" aria-current="page">Dashboard</a> + <a href="{{ url_for('auth.logout')}}" class="text-gray-300 hover:bg-gray-700 hover:text-white rounded-md px-3 py-2 text-sm font-medium">Logout</a> + {% endif %} + </div> + </div> + </div> + </div> + </div> + + <!-- Mobile menu, show/hide based on menu state. --> + <div class="sm:hidden" id="mobile-menu"> + <div class="space-y-1 px-2 pb-3 pt-2"> + <!-- Current: "bg-gray-900 text-white", Default: "text-gray-300 hover:bg-gray-700 hover:text-white" --> + <a href="#" class="text-gray-300 hover:bg-gray-700 hover:text-white block rounded-md px-3 py-2 text-base font-medium">Home</a> + <a href="#" class="text-gray-300 hover:bg-gray-700 hover:text-white block rounded-md px-3 py-2 text-base font-medium">Dashboard</a> + <a href="#" class="text-gray-300 hover:bg-gray-700 hover:text-white block rounded-md px-3 py-2 text-base font-medium">Log Out</a> + <a href="#" class="text-gray-300 hover:bg-gray-700 hover:text-white block rounded-md px-3 py-2 text-base font-medium">Login</a> + <a href="#" class="text-gray-300 hover:bg-gray-700 hover:text-white block rounded-md px-3 py-2 text-base font-medium">Register</a> + </div> + </div> +</nav> + {% block content %}{% endblock %} + </body> +</html> + diff --git a/app/templates/index.html b/app/templates/index.html new file mode 100644 index 0000000..5ea3220 --- /dev/null +++ b/app/templates/index.html @@ -0,0 +1,50 @@ +{% extends 'base.html' %} + +{% block content %} + <!--{% block title %} The Home Page of FlaskApp {% endblock %}--> +<div class="relative isolate overflow-hidden bg-gray-900"> + <svg class="absolute inset-0 -z-10 h-full w-full stroke-white/10 [mask-image:radial-gradient(100%_100%_at_top_right,white,transparent)]" aria-hidden="true"> + <defs> + <pattern id="983e3e4c-de6d-4c3f-8d64-b9761d1534cc" width="200" height="200" x="50%" y="-1" patternUnits="userSpaceOnUse"> + <path d="M.5 200V.5H200" fill="none" /> + </pattern> + </defs> + <svg x="50%" y="-1" class="overflow-visible fill-gray-800/20"> + <path d="M-200 0h201v201h-201Z M600 0h201v201h-201Z M-400 600h201v201h-201Z M200 800h201v201h-201Z" stroke-width="0" /> + </svg> + <rect width="100%" height="100%" stroke-width="0" fill="url(#983e3e4c-de6d-4c3f-8d64-b9761d1534cc)" /> + </svg> + <div class="absolute left-[calc(50%-4rem)] top-10 -z-10 transform-gpu blur-3xl sm:left-[calc(50%-18rem)] lg:left-48 lg:top-[calc(50%-30rem)] xl:left-[calc(50%-24rem)]" aria-hidden="true"> + <div class="aspect-[1108/632] w-[69.25rem] bg-gradient-to-r from-[#80caff] to-[#4f46e5] opacity-20" style="clip-path: polygon(73.6% 51.7%, 91.7% 11.8%, 100% 46.4%, 97.4% 82.2%, 92.5% 84.9%, 75.7% 64%, 55.3% 47.5%, 46.5% 49.4%, 45% 62.9%, 50.3% 87.2%, 21.3% 64.1%, 0.1% 100%, 5.4% 51.1%, 21.4% 63.9%, 58.9% 0.2%, 73.6% 51.7%)"></div> + </div> + <div class="mx-auto max-w-7xl px-6 pb-24 pt-10 sm:pb-32 lg:flex lg:px-8 lg:py-40"> + <div class="mx-auto max-w-2xl flex-shrink-0 lg:mx-0 lg:max-w-xl lg:pt-8"> + <img class="h-11" src="https://tailwindui.com/img/logos/mark.svg?color=indigo&shade=500" alt="Your Company"> + <div class="mt-24 sm:mt-32 lg:mt-16"> + <a href="#" class="inline-flex space-x-6"> + <span class="rounded-full bg-indigo-500/10 px-3 py-1 text-sm font-semibold leading-6 text-indigo-400 ring-1 ring-inset ring-indigo-500/20">What's new</span> + <span class="inline-flex items-center space-x-2 text-sm font-medium leading-6 text-gray-300"> + <span>Just shipped v1.0</span> + <svg class="h-5 w-5 text-gray-500" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"> + <path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /> + </svg> + </span> + </a> + </div> + <h1 class="mt-10 text-4xl font-bold tracking-tight text-white sm:text-6xl">RSS Anything</h1> + <p class="mt-6 text-lg leading-8 text-gray-300">Anim aute id magna aliqua ad ad non deserunt sunt. Qui irure qui lorem cupidatat commodo. Elit sunt amet fugiat veniam occaecat fugiat aliqua.</p> + <div class="mt-10 flex items-center gap-x-6"> + <a href="{{url_for('auth.signup')}}" class="rounded-md bg-indigo-500 px-3.5 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-indigo-400 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-400">Get started</a> + <a href="#" class="text-sm font-semibold leading-6 text-white">Learn more <span aria-hidden="true">→</span></a> + </div> + </div> + <div class="mx-auto mt-16 flex max-w-2xl sm:mt-24 lg:ml-10 lg:mr-0 lg:mt-0 lg:max-w-none lg:flex-none xl:ml-32"> + <div class="max-w-3xl flex-none sm:max-w-5xl lg:max-w-none"> + <img src="https://tailwindui.com/img/component-images/dark-project-app-screenshot.png" alt="App screenshot" width="2432" height="1442" class="w-[76rem] rounded-md bg-white/5 shadow-2xl ring-1 ring-white/10"> + </div> + </div> + </div> +</div> + + +{% endblock %} diff --git a/app/templates/main/dashboard.html b/app/templates/main/dashboard.html new file mode 100644 index 0000000..d2022c5 --- /dev/null +++ b/app/templates/main/dashboard.html @@ -0,0 +1,19 @@ +{% extends 'base.html' %} + +{% block content %} + <div class="min-h-full"> + <div class="py-10"> + <header> + <div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8"> + <h1 class="text-3xl font-bold leading-tight tracking-tight text-gray-900">{% block title %}Dashboard{% endblock %}</h1> + <h2>Hi {{name}} + </div> + </header> + <main> + <div class="mx-auto max-w-7xl sm:px-6 lg:px-8"> + <!-- Your content --> + </div> + </main> + </div> +</div> +{% endblock %} |