diff --git a/.gitignore b/.gitignore index d292513ab13..0696ba6acfe 100644 --- a/.gitignore +++ b/.gitignore @@ -28,4 +28,11 @@ credentials.dat .DS_store env/ .idea -.env* \ No newline at end of file +.env* +# customize +.fenv +initscript.sh +credentials.json +ConfigFirebase.js +robots.txt +tests \ No newline at end of file diff --git a/appengine/flexible/hello_world/app.yaml b/appengine/flexible/hello_world/app.yaml index 7aa7a47e159..84417195385 100644 --- a/appengine/flexible/hello_world/app.yaml +++ b/appengine/flexible/hello_world/app.yaml @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. + runtime: python env: flex entrypoint: gunicorn -b :$PORT main:app diff --git a/appengine/flexible/hello_world/app/__init__.py b/appengine/flexible/hello_world/app/__init__.py new file mode 100644 index 00000000000..7cc05946978 --- /dev/null +++ b/appengine/flexible/hello_world/app/__init__.py @@ -0,0 +1,34 @@ +from flask import Flask +from flask_bootstrap import Bootstrap +from flask_login import LoginManager + +from .config import Config +from .auth import auth +from .ingredients import ingredients +from .models import UserModel, RecipeModel +from .recipes import recipes +#from .services import services + +login__manager = LoginManager() +login__manager.login_view = 'auth.login' + +@login__manager.user_loader +def load_user(username): + return UserModel.query(username) + +def create_app(): + app = Flask(__name__) + bootstrap = Bootstrap(app) + + app.config.from_object(Config) + login__manager.init_app(app) + # login_manager.login_message = "You must be logged in to access this page." + # login_manager.login_view = "auth.login" + + #Es necesario registrar los blueprints + #app.register_blueprint(admin) + app.register_blueprint(auth) + app.register_blueprint(recipes) + app.register_blueprint(ingredients) + + return app \ No newline at end of file diff --git a/appengine/flexible/hello_world/app/admin/__init__.py b/appengine/flexible/hello_world/app/admin/__init__.py new file mode 100644 index 00000000000..10b16ca0878 --- /dev/null +++ b/appengine/flexible/hello_world/app/admin/__init__.py @@ -0,0 +1,5 @@ +from flask import Blueprint + +admin = Blueprint('admin', __name__, url_prefix='/admin') + +from . import views \ No newline at end of file diff --git a/appengine/flexible/hello_world/app/admin/views.py b/appengine/flexible/hello_world/app/admin/views.py new file mode 100644 index 00000000000..60cc41f9e2e --- /dev/null +++ b/appengine/flexible/hello_world/app/admin/views.py @@ -0,0 +1,41 @@ +"""app/admin/views.py""" + +import app +from flask import flash, redirect, render_template, url_for +from flask_login import current_user,login_required +from app.firestore_service import import__export_data,backend_only_create_tenant + +from . import admin + +@admin.route('/admin/import', methods=['GET']) +@login_required +def import_export_data(): + try: + # import__export_data() + + return {'message': 'Done'},200 + except Exception as inst: + print(type(inst)) # the exception instance + print(inst.args) # arguments stored in .args + print(inst) # __str__ allows args to be printed directly, + # but may be overridden in exception subclasses + return {'message': 'Error importing or exporting data'},400 + + +@admin.route('/admin/createTenant/', methods=['GET']) +@login_required +def createTenant(newTenant): + try: + backend_only_create_tenant(newTenant) + + return {'message': 'Done'},200 + except Exception as inst: + print(type(inst)) # the exception instance + print(inst.args) # arguments stored in .args + print(inst) # __str__ allows args to be printed directly, + # but may be overridden in exception subclasses + return {'message': 'Error creating tenant'},400 + + + + diff --git a/appengine/flexible/hello_world/app/auth/__init__.py b/appengine/flexible/hello_world/app/auth/__init__.py new file mode 100644 index 00000000000..be78aaa3d96 --- /dev/null +++ b/appengine/flexible/hello_world/app/auth/__init__.py @@ -0,0 +1,5 @@ +from flask import Blueprint + +auth = Blueprint('auth', __name__, url_prefix='/auth') + +from . import views \ No newline at end of file diff --git a/appengine/flexible/hello_world/app/auth/views.py b/appengine/flexible/hello_world/app/auth/views.py new file mode 100644 index 00000000000..25d1867bd7e --- /dev/null +++ b/appengine/flexible/hello_world/app/auth/views.py @@ -0,0 +1,243 @@ +from flask import render_template, session, redirect, flash, url_for, make_response, request +from flask_login import login_user, login_required, logout_user, current_user +from werkzeug.security import generate_password_hash, check_password_hash + + +from . import auth +from app.forms import LoginForm, GuestForm +from app.common_functions import generarQR, isLogin +from app.firestore_service import user_put, get_guest, guest_put, get_user_with_tenant, get_tenat_info, user_put_into_newsletter, create_demo_tenant_and_demo_user +from app.models import UserData, UserModel, GuestData, GuestModel +from datetime import timedelta + +import app + +@auth.route('signup', methods=['GET','POST']) +@login_required +def signup(): + ##TODO: verificar que al momento de generar la contraseña esté sumando un SAL, un código adicional al final para que no sea reversible y mas seguro + context={} + if session.get('admin'): + context = { + 'admin': session['admin'], + } + + if request.method == 'POST': + formData = request.form + username = formData.get('username').upper() + password = formData.get('password') + fullname = formData.get('fullname') + gender = formData.get('gender').upper() + + # print(formData) + context['form']=formData + + user__db = get_user_with_tenant(username,session['tenant']) + if user__db.to_dict() is None: + password__hash = generate_password_hash(password) + user__data = UserData(username=username, password=password__hash, admin=False, tenant=session['tenant'], fullname=fullname, gender=gender) + + user_put(user__data) + + flash('Usuario '+ username +' registrado', category='info') + return redirect(url_for('orders.list_orders')) + else: + flash('El usuario existe.') + + return render_template('signup.html', **context) + else: + flash('No tiene permisos de administrador', category='info') + return redirect(url_for('orders.list_orders')) + + +@auth.route('demo',methods=['GET']) +def demo(): + flash('Recuerda que podrás usar la plataforma, pero todos tus cambios se guardarán solo por 24 horas') + return render_template('demo_login.html') + + +@auth.route('signupGuest', methods=['GET','POST']) +def signup_guest(): + #TODO: add captcha + signup__guest__form = GuestForm() + + context = { + 'signup__guest__form' : signup__guest__form + } + + if signup__guest__form.validate_on_submit(): + name = signup__guest__form.name.data + email = signup__guest__form.email.data + phone = signup__guest__form.phone.data + + guest__doc = get_guest(email) + + + + if guest__doc.to_dict() is None: + guest__data = GuestData(name,email,phone) + + guest = GuestModel(guest__data) + guest_put(guest) + + flash('Gracias por usar Recetario, muy pronto será invitado') + #TODO:Luego de invitarlo aqui pudiera mostrar un manual de las bondades de RECETARIO, el "how it works" + + #next = request.args.get('next') + ##TODO: is_safe_url should check if the url is safe for redirects. + ## See http://flask.pocoo.org/snippets/62/ for an example. + #if not is_safe_url(next): + # return flask.abort(400) + + + return render_template('signupGuestDone.html', **context) + else: + flash('El invitado existe') + else: + flash('Por favor, sirvase de introducir sus datos para poder ser invitado por el admin ') + + return render_template('signupGuest.html', **context) + + +@auth.route('login', methods=['GET','POST']) +def login(): + """ + In order to authentificate a user + + type__of__tenant has two options: tenant or sandbox + """ + context = {} + + response = render_template('login.html', **context) + + if request.method == 'POST': + ##TODO: creear funncion que asegure el input del login + formData = request.form + + user__db = '' + tenant = '' + password = '' + temrs = '' + username = '' + + if formData.get('tenant'): + tenant = formData.get('tenant').upper() + if formData.get('tenant_store'): + tenant = formData.get('tenant_store').upper() + + if formData.get('password'): + password = formData.get('password') + if formData.get('password_store'): + password = formData.get('password_store') + + if formData.get('terms'): + terms = formData.get('terms').upper() + if formData.get('terms_store'): + terms = formData.get('terms_store').upper() + + if formData.get('username'): + username = formData.get('username').upper() + if formData.get('username_store'): + username = formData.get('username_store').upper() + + if tenant=='DEMO_VENDOR' and password=='contraseñanosegurademo': + session['type__of__tenant'] = 'sandbox' + session['tenant'] = username + tenant = username + + user__db = get_user_with_tenant(username,username).to_dict() + + if user__db is None: + password__hash = generate_password_hash(password) + create_demo_tenant_and_demo_user(username,password__hash,'VENDOR') + user_put_into_newsletter(username,username) + user__db = get_user_with_tenant(username,username).to_dict() + + elif tenant=='DEMO_STORE' and password=='contraseñanosegurademo': + session['type__of__tenant'] = 'sandbox' + session['tenant'] = username + tenant = username + user__db = get_user_with_tenant(username,username).to_dict() + + if user__db is None: + password__hash = generate_password_hash(password) + create_demo_tenant_and_demo_user(username,password__hash,'STORE') + user_put_into_newsletter(username,username) + user__db = get_user_with_tenant(username,username).to_dict() + + else: + session['type__of__tenant'] ='tenant' + session['tenant'] = tenant + context['form'] = formData + user__db = get_user_with_tenant(username,tenant).to_dict() + + + + if user__db is not None: + if check_password_hash(user__db['password'], password): + if tenant == user__db['tenant']: + user__data = UserData(username=username,password=password, admin=user__db['admin'], tenant=user__db['tenant'], fullname=user__db['fullname'], gender=user__db['gender'] ) + user = UserModel(user__data) + # search tenant info (like imageURL) + tenant = get_tenat_info(tenant) + + ##TODO: llamar a una funcion que agregue todos estos + ##TODO: crear una funcion que los quite y usar esa funcion en log_out + session['admin'] = user.admin + session['fullname'] = user.fullname + session['gender'] = user.gender + session['tenant'] = user.tenant + session['tenantImageURL'] = tenant.get('imageURL') + session['tenantName'] = tenant.get('name') + session['tenantType'] = tenant.get('type') + session['tenantPermits'] = tenant.get('permits') + session['tenantPayments'] = tenant.get('payments') + session['username'] = user.id + + login_user(user, remember=False, duration=None, force=False, fresh=True) + + if user.gender =='male': + flash(user.fullname +', Bienvenido', category='info') + elif user.gender =='female': + flash(user.fullname +', Bienvenida', category='info') + + response = make_response(redirect('/orders')) + else: + flash('La informacion no coincide', category='warning') + response = render_template('login.html', **context) + else: + flash('La informacion no coincide', category='warning') + response = render_template('login.html', **context) + else: + flash('La informacion no coincide', category='error') + ##TODO: customize los flash con colores + ##TODO: quizas un mensaje diferente flash('La informacion no coincide o el usuario no existe') + response = render_template('login.html', **context) + + + #si el usuario está logueado, redireccionar a recipes + if current_user.is_authenticated: + response = make_response(redirect('/orders')) + + return response + + +@auth.route('/logout', methods=['GET']) +@login_required +def logout(): + session.pop('admin') + session.pop('fullname') + session.pop('gender') + session.pop('tenant') + session.pop('tenantImageURL') + session.pop('tenantName') + session.pop('tenantType') + session.pop('tenantPermits') + session.pop('tenantPayments') + session.pop('username') + + ##TODO: saber si es necesario o no lo session.pop() + logout_user() + flash('Logout done') + #todo: agregar una prueba para asegurar que en la cookie no quedan datos del usuario + return redirect(url_for('auth.login')) diff --git a/appengine/flexible/hello_world/app/common_functions.py b/appengine/flexible/hello_world/app/common_functions.py new file mode 100644 index 00000000000..a10e2ade64a --- /dev/null +++ b/appengine/flexible/hello_world/app/common_functions.py @@ -0,0 +1,48 @@ +from flask import render_template, session, redirect, flash, url_for +from flask_login import current_user +## +## commons functions +## + + +def generarQR(url): + nombre_imagen = imgAddress + 'qrcode.png' + + # Link for website + input_data = url + + #Creating an instance of qrcode + qr = qrcode.QRCode( + version=1, + box_size=10, + border=5) + qr.add_data(input_data) + qr.make(fit=True) + img = qr.make_image(fill='black', back_color='white') + img.save('static/' + nombre_imagen) + + #print( '\nstatic/' + nombre_imagen + '\n') + + return nombre_imagen + +##function to detect if the user has been log-in +def isLogin(): + + + is__login = False + + if session.get('username'): + #and request.cookies.get('user_name') and request.cookies.get('user__id') + is__login = True + + return is__login + + +def check_admin(): + """ + Prevent non-admins from accessing the page + """ + if not current_user.admin: + return False + else: + return True \ No newline at end of file diff --git a/appengine/flexible/hello_world/app/config.py b/appengine/flexible/hello_world/app/config.py new file mode 100644 index 00000000000..44155527829 --- /dev/null +++ b/appengine/flexible/hello_world/app/config.py @@ -0,0 +1,2 @@ +class Config: + SECRET_KEY = '1LKm3fvz9lY8RuX3VmK1UXKx5dEQ5GYKL' \ No newline at end of file diff --git a/appengine/flexible/hello_world/app/firestore_service.py b/appengine/flexible/hello_world/app/firestore_service.py new file mode 100644 index 00000000000..0e74eb1a776 --- /dev/null +++ b/appengine/flexible/hello_world/app/firestore_service.py @@ -0,0 +1,716 @@ +import datetime +import firebase_admin +from flask import session +from firebase_admin import firestore + +app = firebase_admin.initialize_app() +db = firestore.client() + +## when you use document.get() return a list [] +## when you use collection.stream() return a generator + +# +# RESERVATIONS +# +def put_reservation(reservation): + # print( reservation['address']) + + reservation__ref = db.collection('reservations').document() + reservation__ref.set({ + 'address' : reservation['address'], + 'date' : reservation['date'], + 'hour' : reservation['hour'], + 'createDate' : datetime.datetime.now(), + 'customer' : "", + 'products' : reservation['group1'], + 'reference' : reservation['group2'], + 'state' : 'new', + }) + # print(reservation__ref.get().to_dict()) + return reservation__ref + + +def put_customer_into_reservation(reservation, formData): + print( reservation, formData ) + + reservation__ref = db.collection('reservations').document(reservation) + reservation__ref.set({ + 'customer' : formData, + }) + + return reservation__ref + + +def get_reservation(id): + return db.collection('reservations').document(id).get() + + +# +#USERS +# +def get_all_users(): + """ + Return a list with all users in your tenant + """ + # return db.collection(session['type__of__tenant']).document(session['tenant']).collection('users').where(, u'!=', u'ADMIN').stream() + return db.collection(session['type__of__tenant']).document(session['tenant']).collection('users').stream() + +def get_user(username): + return db.collection('users').document(username).get() + +def get_user_with_tenant(username,tenant): + """ + Return single user from DB using username as ID + """ + return db.collection(session['type__of__tenant']).document(tenant).collection('users').document(username).get() + +def user_put(user__data): + user_ref = db.collection(session['type__of__tenant']).document(session['tenant']).collection('users').document(user__data.username) + user_ref.set({'password': user__data.password,'admin': False, 'tenant':session['tenant'], 'gender':user__data.gender, 'fullname':user__data.fullname}) + +def user_put_into_newsletter(email,tenant): + """ + Add email to newsLetter list in BD + """ + user_ref = db.collection('newsletter').document(email) + user_ref.set({'tenant':tenant}) + + + +# +#RECIPES +# +def get_recipes(): + #return db.collection(u'collection').where(u'capital', u'==', True).stream() + return db.collection(session['type__of__tenant']).document(session['tenant']).collection('recipes').stream() + + +def get_recipe(recipe): + doc_ref = db.collection(session['type__of__tenant']).document(session['tenant']).collection(u'recipes').document(recipe) + try: + doc = doc_ref.get() + # a = doc.to_dict() + # for i,j in a.items(): + # print(i+str(j)) + # print(u'Document data: {}'.format(doc.to_dict())) + except google.cloud.exceptions.NotFound: + print('No such document!') + doc = None + + return doc + + +def get_recipe_ingredients(recipe): + return db.collection(session['type__of__tenant']).document(session['tenant']).collection(u'recipes').document(recipe).collection('ingredients').stream() + + +def recipe_put(recipe): + recipes_collection_ref = db.collection(session['type__of__tenant']).document(session['tenant']).collection('recipes').document(recipe.title) + recipes_collection_ref.set({ + 'description' : recipe.description, + 'instructions' : recipe.instructions, + 'servings' : recipe.servings, + 'imageURL' : recipe.imageURL, + 'product' : recipe.product, + }) + + if recipe.ingredients is not None: + recipes_ingredients_ref = db.collection(session['type__of__tenant']).document(session['tenant']).collection('recipes').document(recipe.title).collection('ingredients') + for k,v in recipe.ingredients.items(): + recipes_ingredients_ref.document(k).set(v) + + # recipes_ingredients_ref = db.collection('recipes').document(recipe.title).collection('ingredients') + # for k,v in recipe.ingredients.items(): + + # print ('k:' + str(k)) + # print ('v:' + str(v)) + # for key,values in v.items(): + # print ('key:' + str(key)) + # print ('values:' + str(values)) + # recipes_ingredients_ref.document(k).set({ + # key: str(values), + # }) + + # recipes_ingredients_ref.document('Galleta Maria').set({ + # 'daniel': 250, + # 'unit' : "gr", + # 'angy' : "ml", + # }, merge=True) + + # for k,v in recipe.ingredients.items(): + # for key,values in v.items(): + # recipes_ingredients_ref.document(k).set({ + # key: str(values), + # }) + + +def recipe_update(recipe, old_recipe=None): + if old_recipe is None: + # search for collection recipe reference + # set new content fields + # search for collection ingredients reference + + # for each document delete those + # set new ingredients subcollections reference + # set new content + + recipes_collection_ref = db.collection(session['type__of__tenant']).document(session['tenant']).collection('recipes').document(recipe.title) + recipes_collection_ref.set( + { + 'description' : recipe.description, + 'instructions' : recipe.instructions, + 'servings' : recipe.servings, + 'imageURL' : recipe.imageURL, + 'product' : recipe.product, + } + ) + + recipes_ingredients_ref = db.collection(session['type__of__tenant']).document(session['tenant']).collection('recipes').document(recipe.title).collection('ingredients') + delete_collection(recipes_ingredients_ref, 100, 0) + + if recipe.ingredients is not None: + for k,v in recipe.ingredients.items(): + recipes_ingredients_ref.document(k).set(v) + else: + ## TODO: delete old_recipe and call recipe_put(recipe): + pass + + + +def delete_collection(coll_ref, batch_size, counter): + batch = db.batch() + init_counter=counter + docs = coll_ref.limit(500).get() + deleted = 0 + + for doc in docs: + batch.delete(doc.reference) + deleted = deleted + 1 + + if deleted >= batch_size: + new_counter= init_counter + deleted + batch.commit() + print("potentially deleted: " + str(new_counter)) + return delete_collection(coll_ref, batch_size, new_counter) + batch.commit() + + + +# +#INGREDIENTS +# +def get_list_ingredients(): + return db.collection(session['type__of__tenant']).document(session['tenant']).collection('ingredients').stream() + + +def get_ingredient(ingredient): + doc_ref = db.collection(session['type__of__tenant']).document(session['tenant']).collection(u'ingredients').document(ingredient) + try: + doc = doc_ref.get() + # a = doc.to_dict() + # for i,j in a.items(): + # print(i+str(j)) + # print(u'Document data: {}'.format(doc.to_dict())) + except google.cloud.exceptions.NotFound: + print('No such document!') + doc = None + + return doc + + +def put_ingredient(ingredient): + ingredient_collection_ref = db.collection(session['type__of__tenant']).document(session['tenant']).collection('ingredients').document(ingredient.title) + ingredient_collection_ref.set({'price': ingredient.price, 'quantity': ingredient.quantity, 'unit': ingredient.unit, 'is_gluten_free': ingredient.is_gluten_free}) + + +def update_ingredient(ingredient, old_ingredient=None): + if old_ingredient is None: + ingredient_collection_ref = db.collection(session['type__of__tenant']).document(session['tenant']).collection('ingredients').document(ingredient.title) + ingredient_collection_ref.set({'price': ingredient.price, 'quantity': ingredient.quantity, 'unit': ingredient.unit, 'is_gluten_free': ingredient.is_gluten_free}) + else: + ## TODO: delete old_ingredient and call put_ingredient(ingredient): + pass + + +# +#GUESTS +# +def get_guest(email): + return db.collection('guest').document(email).get() + + +def guest_put(guest): + recipes_collection_ref = db.collection('guest').document(guest.email) + recipes_collection_ref.set({'email': guest.email, 'name': guest.name, 'phone': guest.phone}) + + +# +# ORDERS +# +def get_list_orders(): + return db.collection(session['type__of__tenant']).document(session['tenant']).collection('orders').stream() + + +def get_order(id): + doc_ref = db.collection(session['type__of__tenant']).document(session['tenant']).collection(u'orders').document(id) + try: + doc = doc_ref.get() + except google.cloud.exceptions.NotFound: + print('No such document!') + doc = None + return doc + + +def get_order_products(orderID): + return db.collection(session['type__of__tenant']).document(session['tenant']).collection(u'orders').document(orderID).collection('products').stream() + + +def put_order(order): + order_ref = db.collection(session['type__of__tenant']).document(session['tenant']).collection('orders').document() + order_ref.set({ + 'store' : order.store, + 'createdDate' : datetime.datetime.now(), + 'deliveryDate' : order.deliveryDate, + }) + + if order.products is not None: + products_ref = db.collection(session['type__of__tenant']).document(session['tenant']).collection('orders').document(order_ref.id).collection('products') + for k,v in order.products.items(): + products_ref.document(k).set(v) + + return order_ref.id + + +# +# STORES +# +def get_list_stores(): + return db.collection(session['type__of__tenant']).document(session['tenant']).collection('stores').stream() + + +def get_store(id): + doc_ref = db.collection(session['type__of__tenant']).document(session['tenant']).collection(u'stores').document(id) + try: + doc = doc_ref.get() + except google.cloud.exceptions.NotFound: + print('No such document!') + doc = None + return doc + + +def put_store(store): + store_collection_ref = db.collection(session['type__of__tenant']).document(session['tenant']).collection('stores') + store_collection_ref.add({ + 'name' : store.name, + 'address' : store.address, + 'contactNumber' : store.contactNumber, + 'email' : store.email, + 'telegram' : store.telegram, + 'instagram' : store.instagram, + }) + + +def update_store(store, old_store=None): + if old_store is None: + store_collection_ref = db.collection(session['type__of__tenant']).document(session['tenant']).collection('stores').document(store.storeID) + store_collection_ref.set({ + 'name' : store.name, + 'address' : store.address, + 'contactNumber' : store.contactNumber, + 'email' : store.email, + 'telegram' : store.telegram, + 'instagram' : store.instagram, + }) + else: + ## TODO: delete old_store and call put_store(store): + pass + + +# +# VENDORS +# +def get_list_vendors(): + return db.collection(session['type__of__tenant']).document(session['tenant']).collection('vendors').stream() + + +def get_vendor(id): + doc_ref = db.collection(session['type__of__tenant']).document(session['tenant']).collection(u'vendors').document(id) + try: + doc = doc_ref.get() + except google.cloud.exceptions.NotFound: + print('No such document!') + doc = None + return doc + + +def put_vendor(vendor): + vendor__collection__ref = db.collection(session['type__of__tenant']).document(session['tenant']).collection('vendors') + vendor__collection__ref.add({ + 'name' : vendor.name, + 'address' : vendor.address, + 'contactNumber' : vendor.contactNumber, + 'email' : vendor.email, + 'telegram' : vendor.telegram, + 'instagram' : vendor.instagram, + }) + + +def update_vendor(vendor, old_vendor=None): + if old_vendor is None: + vendor_collection_ref = db.collection(session['type__of__tenant']).document(session['tenant']).collection('vendors').document(vendor.vendorID) + vendor_collection_ref.set({ + 'name' : vendor.name, + 'address' : vendor.address, + 'contactNumber' : vendor.contactNumber, + 'email' : vendor.email, + 'telegram' : vendor.telegram, + 'instagram' : vendor.instagram, + }) + else: + ## TODO: delete old_vendor and call put_store(vendor): + pass + + + + +# +#INVENTORY +# +def get_inventory_products(): + return db.collection(session['type__of__tenant']).document(session['tenant']).collection('inventory').where(u'type', u'==', u'product').stream() + + +def get_inventory_ingredients(): + return db.collection(session['type__of__tenant']).document(session['tenant']).collection('inventory').where(u'type', u'==', u'ingredient').stream() + + +def get_inventory_product_info(productID): + """ + Return info of product + """ + return db.collection(session['type__of__tenant']).document(session['tenant']).collection('inventory').document(productID).get() + + +def add_inventory(inventory): + """ + Procedure to add a product to the inventory + """ + ref = db.collection(session['type__of__tenant']).document(session['tenant']).collection('inventory').document(inventory.id) + ref.set({ + 'name' : inventory.name, + 'quantity' : inventory.quantity, + 'type' : inventory.typeof, + }) + +def delete_inventory(product): + try: + doc_ref = db.collection(session['type__of__tenant']).document(session['tenant']).collection(u'inventory').document(product) + doc_ref.delete() + # batch = db.batch() + # batch.delete(doc_ref) + # batch.commit() + except google.cloud.exceptions.NotFound: + print('No such document!') + doc = None + + +# +#TENANT +# +def create_demo_tenant_and_demo_user(email,password,typeTenant='VENDOR'): + """ + Create new demo tenant in sandbox collection + """ + ref = db.collection('createTenant').document(typeTenant).get() + admin__ref = db.collection('createTenant').document(typeTenant).collection('users').document('ADMIN').get() + + dicc = ref.to_dict() + dicc__user = admin__ref.to_dict() + + dicc['name'] = email + dicc__user['fullname'] = email + dicc__user['tenant'] = email + dicc__user['password'] = password + + to__ref = db.collection('sandbox').document(email).set(dicc) + to__ref_user= db.collection('sandbox').document(email).collection('users').document(email).set(dicc__user) + + +def get_tenat_info(tenant): + return db.collection(session['type__of__tenant']).document(tenant).get() + + + + +# +# CAUTION: just for admin and sandbox trigger +# +def import__export_data(): + pass + # from_ref= db.collection('ADMIN').stream() + # to_ref = db.collection('tenant').document('ARIANI') + + # for doc in from_ref: + # to_ref.document(doc.id).set(doc.to_dict()) + + +def backend_only_create_tenant(newTenant,typeTenant='VENDOR'): + """ + Create new Tenant + """ + #comprobar que NO existe tenant (NO QUEREMOS SOBREESCRIBIR Todo UN CLIENTE POR FAVOR) + #obtener referencia a VENDOR o STORE con typeTenant + #obtener referencia a subcolección users para guardar usuario ADMIN + ref = db.collection('createTenant').document(typeTenant).get() + admin__ref = db.collection('createTenant').document(typeTenant).collection('users').document('ADMIN').get() + + dicc = ref.to_dict() + dicc__user = admin__ref.to_dict() + + dicc['name']=newTenant + dicc__user['tenant']=newTenant + + to__ref = db.collection('sandbox').document(newTenant).set(dicc) + to__ref_user= db.collection('sandbox').document(newTenant).collection('users').document('ADMIN').set(dicc__user) + + return True + + +def backend_only_sandbox_reset(): + """ + Restore sandbox state to NEW + Is time to reboot all this mess + Version 2 + """ + # obtener la lista de todos los tenants en la colleccion sandbox + # para cada tenant en la lista: + # buscar la referencia del tenant + # buscar sus subcolecciones + # para cada subcoleccione, borrar contenido usando delete_collection(recipes_ingredients_ref, 100, 0) + # borrar sandbox + # DONE + + try: + tenants = db.collection('sandbox').stream() + for tenant in tenants: + print(tenant.id) + ref = db.collection('sandbox').document(tenant.id) + ref_obj = ref.get() + dicc = ref_obj.to_dict() + + if ref.collection('recipes'): + ref__recipes = ref.collection('recipes') + delete_collection(ref__recipes, 100, 0) + if ref.collection('ingredients'): + ref__ingredients= ref.collection('ingredients') + delete_collection(ref__ingredients, 100, 0) + if ref.collection('orders'): + ref__orders = ref.collection('orders') + delete_collection(ref__orders, 100, 0) + if ref.collection('users'): + ref__users = ref.collection('users') + delete_collection(ref__users, 100, 0) + if ref.collection('inventory'): + ref__inventory = ref.collection('inventory') + delete_collection(ref__inventory, 100, 0) + if ref.collection('stores'): + ref__stores = ref.collection('stores') + delete_collection(ref.collection('stores'), 100, 0) + + + delete_collection(db.collection('sandbox'), 100, 0) + except Exception as inst: + print(type(inst)) # the exception instance + print(inst.args) # arguments stored in .args + print(inst) # __str__ allows args to be printed directly, + # but may be overridden in exception subclasses + + + + + return True + + +def backend_only_sandbox_reset_v1(): + """ + Restore sandbox state to NEW + Is time to reboot all this mess + """ + # buscar la referencia a sandbox + # obtener la lista de todos los tenants + # buscar la referencia del tenant + # buscar sus subcolecciones + # para cada subcoleccione, borrar contenido usando delete_collection(recipes_ingredients_ref, 100, 0) + # buscar su tipo de tenant + # buscar refencia al tenant original + # reemplazar tenant por la referencia original + # DONE + + try: + tenants = db.collection('sandbox').stream() + for tenant in tenants: + print(tenant.id) + ref = db.collection('sandbox').document(tenant.id) + ref_obj = ref.get() + dicc = ref_obj.to_dict() + typeTenant = dicc['type'] + + if ref.collection('recipes'): + ref__recipes = ref.collection('recipes') + delete_collection(ref__recipes, 100, 0) + if ref.collection('ingredients'): + ref__ingredients= ref.collection('ingredients') + delete_collection(ref__ingredients, 100, 0) + if ref.collection('orders'): + ref__orders = ref.collection('orders') + delete_collection(ref__orders, 100, 0) + if ref.collection('users'): + ref__users = ref.collection('users') + delete_collection(ref__users, 100, 0) + if ref.collection('inventory'): + ref__inventory = ref.collection('inventory') + delete_collection(ref__inventory, 100, 0) + if ref.collection('stores'): + ref__stores = ref.collection('stores') + delete_collection(ref__stores, 100, 0) + + original_ref = db.collection('createTenant').document(typeTenant).get() + admin__user__original_ref = db.collection('createTenant').document(typeTenant).collection('users').document('ADMIN').get() + + dicc = original_ref.to_dict() + dicc__user = admin__user__original_ref.to_dict() + + dicc['name']=tenant.id + dicc['type']=tenant.id + dicc__user['tenant']=tenant.id + + db.collection('sandbox').document(tenant.id).set(dicc) + db.collection('sandbox').document(tenant.id).collection('users').document('ADMIN').set(dicc__user) + except Exception as inst: + print(type(inst)) # the exception instance + print(inst.args) # arguments stored in .args + print(inst) # __str__ allows args to be printed directly, + # but may be overridden in exception subclasses + + + + + return True + + + +# +#PRODUCTS +# +def get_list_products(): + return db.collection(session['type__of__tenant']).document(session['tenant']).collection('products').stream() + + +def get_product(product): + doc_ref = db.collection(session['type__of__tenant']).document(session['tenant']).collection(u'products').document(product) + try: + doc = doc_ref.get() + except google.cloud.exceptions.NotFound: + print('No such document!') + doc = None + + return doc + + +def put_product(product): + product_collection_ref = db.collection(session['type__of__tenant']).document(session['tenant']).collection('products').document() + product_collection_ref.set({ + 'name' : product.name, + 'imageURL' : product.imageURL, + 'description' : product.description, + 'cost' : product.cost, + 'vendor' : product.vendor, + 'price' : product.price, + }) + + +def update_product(product, old_product=None): + if old_product is None: + product_collection_ref = db.collection(session['type__of__tenant']).document(session['tenant']).collection('products').document(product.id) + product_collection_ref.set({ + 'imageURL' : product.imageURL, + 'cost' : product.cost, + 'price' : product.price, + 'name' : product.name, + 'description' : product.description, + 'vendor' : product.vendor + }) + else: + ## TODO: delete old_product and call put_product(product): + pass + + +def check_products_if_exists(products): + """ + return None if one of products ID is not Found + """ + result = {} + + for p in products: + i = db.collection(session['type__of__tenant']).document(session['tenant']).collection('products').document(p).get() + + if i.to_dict() is None: + return None + else: + result[i.id] = i + # dicc = result[i.id]].to_dict() + # name = dicc.get('name') + # print(name, i.id, dicc, ite) + + return result + + + + +# +#TRANSACTIONS +# +def get_daily_list_transactions(): + dtoday = datetime.date.today() + lista_t = [] + + transactions = db.collection(session['type__of__tenant']).document(session['tenant']).collection('transactions').stream() + for t in transactions: + # print( t.id, str( dtoday) ) + if str(t.id) > str(dtoday): + lista_t.append(t) + + + return lista_t + # return db.collection(session['type__of__tenant']).document(session['tenant']).collection('transactions').stream() + + + +def get_transaction(transaction): + doc_ref = db.collection(session['type__of__tenant']).document(session['tenant']).collection('transactions').document(transaction) + try: + doc = doc_ref.get() + except google.cloud.exceptions.NotFound: + print('No such document!') + doc = None + return doc + + + + + +# def get_todos(user_id): +# return db.collection('users')\ +# .document(user_id)\ +# .collection('todos').get() + + +# def put_todo(user_id, description): +# todos_collection_ref = db.collection('users').document(user_id).collection('todos') +# todos_collection_ref.add({'description': description, 'done': False}) + + +# def delete_todo(user_id, todo_id): +# todo_ref = db.document('users/{}/todos/{}'.format(user_id, todo_id)) +# todo_ref.delete() + diff --git a/appengine/flexible/hello_world/app/forms.py b/appengine/flexible/hello_world/app/forms.py new file mode 100644 index 00000000000..882ae28c64d --- /dev/null +++ b/appengine/flexible/hello_world/app/forms.py @@ -0,0 +1,53 @@ +from flask_wtf import FlaskForm +from wtforms.fields import HiddenField, StringField, IntegerField, PasswordField, SubmitField, TextAreaField, SelectField, FormField, FieldList +from wtforms.validators import DataRequired + + +# +#LOGIN/SIGNUP +# +class LoginForm(FlaskForm): + username = StringField('Nombre de usuario', validators=[DataRequired()]) + password = PasswordField('Password', validators=[DataRequired()]) + submit = SubmitField('Enviar') + + +# +#INGREDIENTS (NOT USE) +# +class RecipeIngredientsForm(FlaskForm): + name = StringField('Nombre del ingrediente:') + quantity = IntegerField('Cantidad:') + unit = StringField('Unidad de medida:') + +# +#RECIPES (NOT USE BUT STILL UPDATED) +# +class RecipesForm(FlaskForm): + title = StringField('Titulo de receta:', validators=[DataRequired()]) + description = StringField('Descripcion de receta:', validators=[DataRequired()]) + instructions = TextAreaField('Instrucciones de la receta:', validators=[DataRequired()]) + servings = IntegerField('Cantidad de porciones:', validators=[DataRequired()]) + #ingredients = FormField(IngredientsForm) + submit = SubmitField('Enviar') + + +# +#INGREDIENT +# +class IngredientForm(FlaskForm): + is_gluten_free = StringField('Libre de Gluten') + quantity = IntegerField('Cantidad:') + unit = StringField('Unidad de medida:') + price = IntegerField('Costo') + + + +# +#GUESTS +# +class GuestForm(FlaskForm): + name = StringField('Nombre:', validators=[DataRequired()]) + email = StringField('Correo electrónico:',validators=[DataRequired()]) + phone = StringField('Telefono:', validators=[DataRequired()]) + submit = SubmitField('Enviar') diff --git a/appengine/flexible/hello_world/app/ingredients/__init__.py b/appengine/flexible/hello_world/app/ingredients/__init__.py new file mode 100644 index 00000000000..d7f5306bef8 --- /dev/null +++ b/appengine/flexible/hello_world/app/ingredients/__init__.py @@ -0,0 +1,5 @@ +from flask import Blueprint + +ingredients = Blueprint('ingredients', __name__, url_prefix='/ingredients') + +from . import views \ No newline at end of file diff --git a/appengine/flexible/hello_world/app/ingredients/views.py b/appengine/flexible/hello_world/app/ingredients/views.py new file mode 100644 index 00000000000..bbaa87f0e1a --- /dev/null +++ b/appengine/flexible/hello_world/app/ingredients/views.py @@ -0,0 +1,188 @@ +from . import ingredients +from flask import render_template, flash, redirect, url_for, session, jsonify, request +from flask_login import login_required, current_user + + +import app +from app.firestore_service import get_list_ingredients, get_ingredient, put_ingredient, update_ingredient +from app.models import IngredientData +from app.common_functions import check_admin + + +@ingredients.route('/', methods=['GET'] ) +@login_required +def index(): + return redirect(url_for('ingredients.list_ingredients')) + + +@ingredients.route('create', methods=['GET','POST']) +@login_required +def create(): + + title = 'Nuevo Ingrediente' + context = { + 'title' : title, + 'admin' : session['admin'], + 'navbar': 'ingredients', + 'units' : ['gr', 'ml', 'unidad'], + } + + if request.method== 'POST': + ingredients = {} + formData = request.form + + title = formData.get('title').upper() + price = float (formData.get('price')) + quantity = float (formData.get('quantity')) + unit = formData.get('unit') + + if formData.get('is_gluten_free'): + is_gluten_free = True + else: + is_gluten_free = False + ##TODO: cuando falla el post no mantiene el valor de checkbox + + context['form'] = formData + + ingredient__data= IngredientData(title, price, quantity, unit, is_gluten_free) + #print(ingredient__data.is_gluten_free) + + ingredient__db = get_ingredient(title) + if ingredient__db.to_dict() is None: + put_ingredient(ingredient__data) + flash('Ingrediente creado') + + return redirect(url_for('ingredients.list_ingredients')) + else: + flash('Ya existe Ingrediente') + + + return render_template('ingredient_create.html', **context) + + return render_template('ingredient_create.html', **context) + + +@ingredients.route('all', methods=['GET']) +@login_required +def list_ingredients(): + + if current_user.is_authenticated: + username = current_user.id + + context = { + 'ingredients' : get_list_ingredients(), + 'admin' : session['admin'], + 'navbar' : 'ingredients', + } + + return render_template('ingredients_list.html', **context) + else: + #no autenticado + return make_response(redirect('/auth/login')) + + +@ingredients.route('select/', methods=['GET']) +@login_required +def select(ingredient): + + ##TODO: saber como hacer un buen try catch + ##TODO: con el decorador @login_required ya no es necesario preguntar si el current_user está autenticado + if current_user.is_authenticated: + username = current_user.id + + ingredient_db = get_ingredient(ingredient).to_dict() + + ## verificar que si existe esta receta + if ingredient_db is not None: + if ingredient_db.get('is_gluten_free'): + ingredient_db['is_gluten_free'] = True + else: + ingredient_db['is_gluten_free'] = False + + context = { + 'title' : ingredient, + 'form' : ingredient_db, + 'admin' : session['admin'], + 'navbar' : 'ingredients', + } + print(ingredient_db['is_gluten_free']) + + return render_template('ingredient_update.html', **context) + + else: + return redirect(url_for('ingredients.list_ingredients')) + + else: + # usuario no autenticado + # return make_response(redirect('/auth/login')) + return redirect(url_for('auth.login')) + + +@ingredients.route('update/', methods=['POST']) +@login_required +def update(ingredient): + ##TODO: saber como hacer un buen try catch + ##TODO: detectar cambios en el formulario para mostrar el boton de guardar + context = { + 'title' : ingredient, + 'admin' : session['admin'], + 'navbar' : 'ingredient', + } + + if request.method== 'POST': + formData = request.form + + if 'go_back_btn' in formData: + return redirect(url_for('ingredient.list_ingredients')) + else: + print( formData.to_dict() ) + + ##TODO: verificar el nombre receta, si cambia se debe hacer un procedimiento distinto + title = ingredient + price = float (formData.get('price') ) + quantity = float (formData.get('quantity') ) + unit = formData.get('unit') + + if formData.get('is_gluten_free'): + is_gluten_free = True + else: + is_gluten_free = False + + ingredient__data = IngredientData(title, price, quantity, unit, is_gluten_free) + ingredient_db = get_ingredient(ingredient__data.title) + # print(ingredient_db.to_dict()) + + if ingredient_db.to_dict() is not None: + update_ingredient(ingredient__data) + flash('Ingrediente actualizado') + return redirect(url_for('ingredients.select', ingredient=ingredient__data.title)) + else: + flash('No existe ingrediente para actualizar') + return redirect(url_for('ingredients.list_ingredients')) + + # return redirect(url_for('ingredients.select', ingredient=ingredient__data.title)) + + +@ingredients.route('/dropdownHTML', methods=['GET'] ) +@login_required +def ajaxHTML(): + ingredients = get_list_ingredients() + + context = { + 'ingredients__list' : ingredients, + } + + return render_template('dropdown.html', **context) + + +@ingredients.route('/dropdown', methods=['GET'] ) +@login_required +def ajax(): + ingredients = get_list_ingredients() + r='' + i=0 + for j in ingredients: + r += "" + + return r + #return jsonify(r) \ No newline at end of file diff --git a/appengine/flexible/hello_world/app/models.py b/appengine/flexible/hello_world/app/models.py new file mode 100644 index 00000000000..7a5f5942028 --- /dev/null +++ b/appengine/flexible/hello_world/app/models.py @@ -0,0 +1,400 @@ +from flask_login import UserMixin +from flask import session +from .firestore_service import get_user_with_tenant, get_recipe, get_guest, get_ingredient, get_product + +# +#USERS Collection(users) +# +class UserData: + def __init__(self, username, password, admin=False, tenant='', fullname='', gender=''): + self.username = username + self.password = password + self.admin = admin + self.tenant = tenant + self.fullname = fullname + self.gender = gender + + + +class UserModel(UserMixin): + def __init__(self, user__data): + """ + :param user_data: UserData + """ + self.id = user__data.username + self.password = user__data.password + self.admin = user__data.admin + self.tenant = user__data.tenant + self.fullname = user__data.fullname + self.gender = user__data.gender + + @staticmethod + def query(user_id): + ##TODO: asegurarme que este query solo se ejecute cuando session esté seteada, de lo contrario dará error y no lo estoy manejando con un try + ##TODO: manejar con un try + user_doc = get_user_with_tenant(user_id,session['tenant']) + user_data = UserData( + username=user_doc.id, + password=user_doc.to_dict()['password'], + admin =user_doc.to_dict()['admin'], + tenant =user_doc.to_dict()['tenant'], + fullname=user_doc.to_dict()['fullname'], + gender =user_doc.to_dict()['gender'], + ) + + return UserModel(user_data) + + +# +#RECIPES Collection(recipes) +# +class RecipeData: + def __init__(self, title, description, instructions, servings, imageURL, ingredients, product): + self.title = title + self.description = description + self.instructions = instructions + self.ingredients = ingredients + self.servings = servings + self.product = product + self.imageURL = imageURL + + +class RecipeModel(): + def __init__(self, recipe): + """ + :param recipe: recipeData + """ + self.id = recipe.title + self.description = recipe.description + self.instructions = recipe.instructions + self.servings = recipe.servings + self.imageURL = recipe.imageURL + self.product = recipe.product + self.ingredients = recipe.ingredients + + + + @staticmethod + def query(recipe): + recipe__bd = get_recipe(recipe) + recipe__data= RecipeData( + title = recipe__bd.id, + description = recipe__bd.to_dict()['description'], + instructions= recipe__bd.to_dict()['instructions'], + servings = recipe__bd.to_dict()['servings'], + imageURL = recipe__bd.to_dict()['imageURL'], + product = recipe__bd.to_dict()['product'], + ingredients = recipe__bd.to_dict()['ingredients'], + ) + + return RecipeModel(recipe__data) + + + +# +#GUESTS Collection(guest) +# +class GuestData: + def __init__(self, name, email, phone): + self.email = email + self.name = name + self.phone = phone + + +class GuestModel(): + def __init__(self, guest): + """ + :param guest_data: GuestData + """ + self.email = guest.email + self.name = guest.name + self.phone = guest.phone + + @staticmethod + def query(email): + guest__bd = get_guest(email) + guest_data = GuestData( + email = guest__bd.id, + name = guest__bd.to_dict()['name'], + phone = guest__bd.to_dict()['phone'], + ) + + return GuestModel(guest_data) + + + +# +#INGREDIENTS Collection(ingredients) +# +class IngredientData: + def __init__(self, title, price=0, quantity=0, unit='gr', is_gluten_free=False): + self.title = title + self.price = price + self.quantity = quantity + self.unit = unit + self.is_gluten_free = is_gluten_free + +class IngredientsModel(): + def __init__(self, ingredient): + """ + :param ingredient: IngredientData + """ + self.id = ingredient.title + self.price = ingredient.price + self.quantity = ingredient.quantity + self.unit = ingredient.unit + self.is_gluten_free = ingredient.is_gluten_free + + @staticmethod + def query(ingredient): + ingredient__bd = get_ingredient(email) + ingredient__data = IngredientData( + title = ingredient__bd.id, + price = ingredient__bd.price, + quantity = ingredient__bd.quantity, + unit = ingredient__bd.unit, + is_gluten_free = ingredient__bd.is_gluten_free, + ) + + + + + +# +#STORES Collection(stores) +# +class StoreData: + def __init__(self, storeID, name, address, contactNumber, email, telegram, instagram ): + self.storeID = storeID + self.name = name + self.address = address + self.contactNumber = contactNumber + self.email = email + self.telegram = telegram + self.instagram = instagram + + +class StoreModel(): + def __init__(self, storeData): + """ + :param store: storeData + """ + self.id = storeData.storeID + self.name = storeData.name + self.address = storeData.address + self.contactNumber = storeData.contactNumber + self.email = storeData.email + self.telegram = storeData.telegram + self.instagram = storeData.instagram + + + @staticmethod + def query(storeID): + store__bd = get_store(storeID) + store__data= StoreData( + id = store__bd.storeID, + name = store__bd.to_dict()['name'], + address = store__bd.to_dict()['address'], + contactNumber = store__bd.to_dict()['contactNumber'], + email = store__bd.to_dict()['email'], + telegram = store__bd.to_dict()['telegram'], + instagram = store__bd.to_dict()['instagram'], + ) + + return StoreModel(store__data) + + + +# +#VENDORS Collection(vendors) +# +class VendorData: + def __init__(self, vendorID, name, address, contactNumber, email, telegram, instagram ): + self.id = id + self.name = name + self.address = address + self.contactNumber = contactNumber + self.email = email + self.telegram = telegram + self.instagram = instagram + + +class VendorModel(): + def __init__(self, vendorData): + """ + :param vendor: vendorData + """ + self.id = vendorData.id + self.name = vendorData.name + self.address = vendorData.address + self.contactNumber = vendorData.contactNumber + self.email = vendorData.email + self.telegram = vendorData.telegram + self.instagram = vendorData.instagram + + + @staticmethod + def query(vendorID): + vendor__bd = get_vendor(vendorID) + vendor__data= VendorData( + id = vendor__bd.id, + name = vendor__bd.to_dict()['name'], + address = vendor__bd.to_dict()['address'], + contactNumber = vendor__bd.to_dict()['contactNumber'], + email = vendor__bd.to_dict()['email'], + telegram = vendor__bd.to_dict()['telegram'], + instagram = vendor__bd.to_dict()['instagram'], + ) + + return VendorModel(vendor__data) + + + +# +#ORDERS Collection(orders) +# +class OrderData: + def __init__(self, store, deliveryDate, products): + self.store = store + self.deliveryDate = deliveryDate + self.products = products + + +class OrderModel(): + def __init__(self, orderData): + """ + :param order: orderData + """ + self.id = orderData.id + self.store = orderData.store + self.deliveryDate = orderData.deliveryDate + self.products = orderData.products + + + @staticmethod + def query(order): + order__bd = get_order(order) + order__data= OrderData( + id = order__bd.id, + store = order__bd.to_dict()['store'], + deliveryDate= order__bd.to_dict()['deliveryDate'], + products = order__bd.to_dict()['products'], + ) + + return OrderModel(order__data) + + + + + +# +#INVENTORY Collection(inventory) +# +class InventoryData: + def __init__(self, id, name, quantity, typeof): + self.id = id + self.name = name + self.quantity= quantity + self.typeof = typeof + +class InventoryModel(): + def __init__(self, inventory): + """ + :param inventory: InventoryData + """ + self.id = inventory.id + self.name = inventory.name + self.quantity = inventory.quantity + self.typeof = inventory.typeof + + + +# +#PRODUCTS Collection(products) +# +class ProductData: + def __init__(self, id, name, description, cost, price, vendor='NOVENDOR', imageURL='https://danielpedroza.pythonanywhere.com/static/assets/img/platzi.png'): + self.id = id + self.name = name + self.description= description + self.price = price + self.vendor = vendor + self.cost = cost + self.imageURL = imageURL + +class ProductModel(): + def __init__(self, product): + """ + :param product: ProductData + """ + self.id = product.id + self.name = product.name + self.description= product.description + self.price = product.price + self.vendor = product.vendor + self.cost = product.cost + self.imageURL = imageURL + + @staticmethod + def query(product): + product__bd = get_product(product) + product__data = ProductData( + id = product__bd.id, + price = product__bd.price, + name = product__bd.name, + description = product__bd.description, + vendor = product__bd.vendor, + cost = product__bd.cost, + imageURL = product__bd.imageURL, + ) + + +# +#TRANSACTION Collection(transactions) +# +class TransactionData: + def __init__(self, customer, paymentMethod, totalPrice, totalCost, products, state, typeof, subtypeof, reference): + self.customer = customer + self.paymentMethod = paymentMethod + self.totalPrice = totalPrice + self.totalCost = totalCost + self.products = products + self.state = state + self.typeof = typeof + self.subtypeof = subtypeof + self.reference = reference + +class TransactionModel(): + def __init__(self, transactionData): + """ + :param transaction: transactionData + """ + self.id = transactionData.id + self.customer = transactionData.customer + self.paymentMethod = transactionData.paymentMethod + self.totalPrice = transactionData.totalPrice + self.totalCost = transactionData.totalCost + self.products = transactionData.products + self.state = transactionData.state + self.typeof = transactionData.typeof + self.subtypeof = transactionData.subtypeof + self.reference = transactionData.reference + + + @staticmethod + def query(transaction): + transaction__db = get_transaction(transaction) + transaction__data= TransactionData( + id = transaction__db.id, + customer = transaction__db.to_dict()['customer'], + paymentMethod= transaction__db.to_dict()['paymentMethod'], + totalPrice = transaction__db.to_dict()['totalPrice'], + totalCost = transaction__db.to_dict()['totalCost'], + products = transaction__db.to_dict()['products'], + state = transaction__db.to_dict()['state'], + typeof = transaction__db.to_dict()['typeof'], + subtypeof = transaction__db.to_dict()['subtypeof'], + reference = transaction__db.to_dict()['reference'], + ) + + return TransactionModel(transaction__data) diff --git a/appengine/flexible/hello_world/app/recipes/__init__.py b/appengine/flexible/hello_world/app/recipes/__init__.py new file mode 100644 index 00000000000..ce17a62b92d --- /dev/null +++ b/appengine/flexible/hello_world/app/recipes/__init__.py @@ -0,0 +1,5 @@ +from flask import Blueprint + +recipes = Blueprint('recipes', __name__, url_prefix='/recipes') + +from . import views \ No newline at end of file diff --git a/appengine/flexible/hello_world/app/recipes/views.py b/appengine/flexible/hello_world/app/recipes/views.py new file mode 100644 index 00000000000..43a18b1ab38 --- /dev/null +++ b/appengine/flexible/hello_world/app/recipes/views.py @@ -0,0 +1,332 @@ +from . import recipes +from flask import render_template, flash, redirect, url_for, session, jsonify, request +from flask_login import login_required, current_user + + +import app +from app.forms import RecipesForm +from app.firestore_service import get_recipes, get_recipe, get_recipe_ingredients, recipe_put, recipe_update, get_list_ingredients, get_ingredient +from app.models import RecipeData, RecipeModel +from app.common_functions import check_admin +#import os + +@recipes.route('/', methods=['GET'] ) +@login_required +def recipes_index(): + return redirect(url_for('recipes.list_recipes')) + + +@recipes.route('/ajax', methods=['GET'] ) +def ajax(): + + check_admin() + + recipe__form = RecipesForm() + response = recipe__form.ingredients + + return jsonify(response) + + +@recipes.route('create', methods=['GET','POST']) +@login_required +def create(): + ##TODO: cuando la receta viene sin nombre de receta lanza un error de google, debo capturarlo con un try + title = 'Nueva receta' + context = { + 'navbar' : 'recipes', + 'title' : title, + 'admin' : session['admin'], + 'ingredients__list' : get_list_ingredients(), + } + + if request.method== 'POST': + ingredients = {} + formData = request.form + + title = formData.get('title').upper() + description = formData.get('description') + instructions= formData.get('instructions') + servings = int (formData.get('servings')) + imageURL = formData.get('imageURL') + if formData.get('product'): + product = True + else: + product = False + context['form'] = formData + #context["IMAGE_UPLOADS"] = 'C://Users/DPedroza/Documents/Daniel Pedroza/workspace/2020/recetario/imagesToUpload' + + if request.files: + pass + #image = request.files['image'] + #print(image) + #print( request.files.get('image') ) + + ##TODO:primero aprendamos a guardarlo en el server(la laptop, localhost) + #image.save(os.path.join(context["IMAGE_UPLOADS"], image.filename)) + #print("image saved") + + ##TODO:ahora aprendamos a usar firebase server + # upload_blob( + # bucket_name='blogeekplatzi-2e74f.appspot.com', + # source_file_name='C:/Users/DPedroza/Documents/Daniel Pedroza/workspace/2020/recetario/imagesToUpload/2.jpg', + # destination_blob_name='recipes/ingredient-2.jpg', + # ) + #print("image saved") + + #ahora aprendamos a usar Firebase web (javascript) + + + + # print("*********************************************") + # print("image saved") + #bucket_name='recetario-blogeekplatzi-2e74f', + #gs://blogeekplatzi-2e74f.appspot.com + #source_file_name='C:/Users/DPedroza/Documents/Daniel Pedroza/workspace/2020/recetario/imagesToUpload/1.jpg', + #source_file_name='C:/Users/DPedroza/Documents/Daniel Pedroza/flask.jpg', + #destination_blob_name='recipes/ingredient-'+title, + # responseURL = upload_blob( + # bucket_name='blogeekplatzi-2e74f.appspot.com', + # source_file_name='C:/Users/DPedroza/Documents/Daniel Pedroza/workspace/2020/recetario/imagesToUpload/2.jpg', + # destination_blob_name='recipes/ingredient-2.jpg', + # ) + # print(responseURL) + + # except google.api_core.exceptions.NotFound as error: + # print("Arrojó una excepcion") + # except google.api_core.exceptions.NameError as error: + # print("Arrojó una excepcion") + + context['zipped'] = zip( formData.getlist('ingredients-name'),formData.getlist('ingredients-quantity'),formData.getlist('ingredients-unit')) + + for k in context['zipped']: + ingredients[ k[0] ] = { 'quantity':k[1], 'unit': k[2]} + # #print(ingredients) + + recipe__data= RecipeData(title, description, instructions, servings, imageURL, ingredients, product) + # print(recipe__data) + + recipe_db = get_recipe(recipe__data.title) + if recipe_db.to_dict() is None: + recipe_put(recipe__data) + flash('Receta creada') + + return redirect(url_for('recipes.list_recipes')) + else: + flash('Ya existe Receta') + ##TODO:revisar template porque está fallando los ingredientes y están fallando el javascript + + return render_template('recipe_create.html', **context) + + return render_template('recipe_create.html', **context) + + +@recipes.route('all', methods=['GET']) +@login_required +def list_recipes(): + + if current_user.is_authenticated: + username = current_user.id + + context = { + ##TODO: colocar un try catch + 'navbar' : 'recipes', + 'cil' : 'Ver recetas', + 'recipes' : get_recipes(), + 'admin' : session['admin'], + } + + return render_template('recipes_list.html', **context) + else: + #no autenticado + return make_response(redirect('/auth/login')) + + +@recipes.route('select/', methods=['GET']) +@login_required +def select(recipe): + + ##TODO: saber como hacer un buen try catch + try: + pass + except expression as identifier: + recipe_db = None + ingredients__db = None + else: + pass + finally: + pass + + if current_user.is_authenticated: + username = current_user.id + + recipe_db = get_recipe(recipe).to_dict() + + ## verificar que si existe esta receta + if recipe_db is not None: + ingredients__db = get_recipe_ingredients(recipe) + + # a = recipe_db + # for i,j in a.items(): + # print(i+str(j)) + # print(u'Document data: {}'.format(recipe_db)) + # print( recipe_db.get('description')) + # print( recipe_db.get('instructions')) + # print( recipe_db.get('servings')) + + # mostrar = ingredients__db + # for r in mostrar: + # print( r.id ) + # print( r.get('quantity')) + # print( r.get('unit')) + + context = { + 'navbar' : 'recipes', + 'title' : recipe, + 'form' : recipe_db, + 'ingredients' : ingredients__db, + 'admin' : session['admin'], + 'ingredients__list' : get_list_ingredients(), + } + return render_template('recipe_update.html', **context) + + else: + return redirect(url_for('recipes.list_recipes')) + + else: + # usuario no autenticado + # return make_response(redirect('/auth/login')) + return redirect(url_for('auth.login')) + + +@recipes.route('update/', methods=['POST']) +@login_required +def update(recipe): + ##TODO: saber como hacer un buen try catch + ##TODO: detectar cambios en el formulario para mostrar el boton de guardar + ##TODO: es importante advertir que la regla de firebase storage está pública y no debería ser así + ##TODO: es importante advertir que mientras no se cargue totalmente la foto en storage y retorne la URL no deberia permitirse subir + + check_admin() + + context = { + 'navbar' : 'recipes', + 'title' : recipe, + 'admin' : session['admin'], + } + + if request.method== 'POST': + formData = request.form + + if 'go_back_btn' in formData: + return redirect(url_for('recipes.list_recipes')) + elif 'go_estimate_btn' in formData: + return redirect(url_for('recipes.estimate', recipe=recipe)) + else: + ingredients = {} + #print( formData.to_dict() ) + ##TODO: verificar el nombre receta, si cambia se debe hacer un procedimiento distinto + title = recipe + description = formData.get('description') + instructions= formData.get('instructions') + servings = int(formData.get('servings')) + imageURL = formData.get('imageURL') + + if formData.get('product'): + product = True + else: + product = False + + context['form'] = formData + context['zipped'] = zip( formData.getlist('ingredients-name'),formData.getlist('ingredients-quantity'),formData.getlist('ingredients-unit')) + + for k in context['zipped']: + ingredients[ k[0] ] = { 'quantity':k[1], 'unit': k[2]} + #print(ingredients) + + recipe__data = RecipeData(title=title, description=description, instructions=instructions, servings=servings, imageURL=imageURL, ingredients=ingredients, product=product) + recipe_db = get_recipe(recipe__data.title) + print(recipe__data) + + if recipe_db.to_dict() is not None: + recipe_update(recipe__data) + flash('Receta actualizada') + return redirect(url_for('recipes.select', recipe=recipe__data.title)) + else: + flash('No existe receta para actualizar') + return render_template('recipe_update.html', **context) + + +@recipes.route('estimate', methods=['POST']) +@login_required +def estimate(): + ##TODO: saber como hacer un buen try catch + ##TODO: amount debe ser siempre mayor a cero y no puede ser float, chequearlo + check_admin() + + formData = request.form + recipe = formData.get('recipe') + amount = formData.get('amount') + + # flash('Calculando Costos') + context = { + 'navbar' : 'recipes', + 'title' : recipe, + 'admin' : session['admin'], + 'info' : {}, + } + + + if request.method== 'POST': + #conocer el valor de cada ingrediente + #dividir cantidad del ingrediente receta entre cantidad presentacion ingrediente + recipe__ingredients__db2 = get_recipe_ingredients(recipe) + recipe__ingredients__db = get_recipe_ingredients(recipe) + recipe__db = get_recipe(recipe).to_dict() + total = 0 + + for ri in recipe__ingredients__db2: + i = get_ingredient(ri.id) + + ##asignacion de cada ingrediente + context['info'][ri.id] = i.to_dict() + + #calculo de cantidad de porciones + context['info'][ri.id]['newQuantity'] = int(ri.get('quantity')) * int(amount) + + dividendo = int (context['info'][ri.id]['newQuantity']) + divisor = int (i.get('quantity')) + result_float= dividendo / divisor + cociente = dividendo // divisor + resto = dividendo % divisor + + if ( result_float > 1 and resto > 0 ): + #print('result_float > 1 and resto > 0 ' + str(result_float) + ' - ' + str(cociente) + ' - ' + str(resto) ) + context['info'][ri.id]['newPrice'] = int( i.get('price')) * int(cociente+1) + context['info'][ri.id]['newNeed'] = cociente+1 + elif ( result_float > 1 and resto == 0 ): + #print('result_float > 1 and resto = 0 ' + str(result_float) + ' - ' + str(cociente) + ' - ' + str(resto) ) + context['info'][ri.id]['newPrice'] = int( i.get('price')) * int(cociente) + context['info'][ri.id]['newNeed'] = cociente + else: + #print('else, es menor a 1, no hay cambios ' + str(result_float) + ' - ' + str(cociente) + ' - ' + str(resto) ) + context['info'][ri.id]['newPrice'] = int( i.get('price')) + context['info'][ri.id]['newNeed'] = 1 + + #sumatoria + total += context['info'][ri.id].get('newPrice') + + # print (ri.id) + # print (ri.get('quantity')) + # print ( context['info'][ri.id]['newQuantity'] ) + # print(total) + + + context['ingredients'] = recipe__ingredients__db + context['recipe__db'] = recipe__db + context['servings'] = int(recipe__db.get('servings')) * int(amount) + context['amount'] = amount + context['total'] = total + ##TODO: cuando se calcula las porciones no debe dar un numero float, hay que chequear eso + #print(context) + + return render_template( 'recipe_estimate.html', **context ) diff --git a/appengine/flexible/hello_world/app/sandbox_reset.py b/appengine/flexible/hello_world/app/sandbox_reset.py new file mode 100644 index 00000000000..2e57ff753ed --- /dev/null +++ b/appengine/flexible/hello_world/app/sandbox_reset.py @@ -0,0 +1,11 @@ +""" +Python daily task to delete sandbox +""" +import requests +try: + requests.get('https://danielpedroza.pythonanywhere.com/api/sandboxReset',data=None) + #return {'message': 'Done'},200 +except expression as identifier: + pass + #return {'message': 'Error: '+ expression},500 + diff --git a/appengine/flexible/hello_world/app/static/assets/css/booking.css b/appengine/flexible/hello_world/app/static/assets/css/booking.css new file mode 100644 index 00000000000..b01c5829e61 --- /dev/null +++ b/appengine/flexible/hello_world/app/static/assets/css/booking.css @@ -0,0 +1 @@ +::-webkit-scrollbar{display:none}*{margin:0;padding:0;text-decoration:none;-webkit-user-select:none;user-select:none}body{color:#333;font-family:"Roboto",sans-serif;text-align:center}h1{font-weight:700;font-size:28px;font-size:1.75rem;line-height:2.1rem}@media only screen and (min-width:768px){h1{font-size:40px;font-size:2.5rem;line-height:3rem}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){h1{font-size:60px;font-size:3.75rem;line-height:4.5rem;margin-bottom:50px}}h2{font-weight:500;font-size:18px;font-size:1.125rem;line-height:1.35rem}@media only screen and (min-width:768px){h2{font-size:24px;font-size:1.5rem;line-height:1.8rem}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){h2{font-size:30px;font-size:1.875rem;line-height:2.25rem}}h3{font-weight:500;font-size:16px;font-size:1rem;line-height:1.2rem}@media only screen and (min-width:768px){h3{font-size:22px;font-size:1.375rem;line-height:1.65rem}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){h3{font-size:26px;font-size:1.625rem;line-height:1.95rem}}p{font-weight:500;font-size:14px;font-size:0.875rem;line-height:1.05rem}@media only screen and (min-width:768px){p{font-size:16px;font-size:1rem;line-height:1.2rem}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){p{font-size:18px;font-size:1.125rem;line-height:1.35rem}}header{width:100vw;height:70px;background-color:#fff;position:fixed;top:0;left:0;z-index:1;opacity:0.9;display:flex;justify-content:center;align-items:center}header img{width:120px;opacity:1}@media only screen and (min-width:768px){header img{width:150px}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){header img{width:180px}}@media only screen and (min-width:768px){header{height:100px}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){header{height:120px}}.bar{position:fixed;left:0;top:70px;width:100vw;height:10px;background-color:#262626}.bar__progress{height:10px;background-color:#39B387}@media only screen and (min-width:768px){.bar{top:100px}}.bookingContainer{width:100%;height:auto;z-index:1;bottom:0;left:0;padding:20px 10vw;border-top:2px solid #5a5a5a5a;box-sizing:border-box;background-color:#1A1A1A;position:fixed;display:flex;justify-content:center;align-items:center;justify-content:space-between;color:#fff}@media only screen and (min-width:992px){.bookingContainer{position:relative;top:30px;width:auto;padding:20px 0;background-color:transparent}}.bookingContainer__price p{display:block;color:#808080;font-size:12px;font-size:0.75rem;line-height:0.9rem}.bookingContainer__price #price{color:#fff}.bookingContainer__button{color:#fff;outline:none;border:none;font-weight:600;border-radius:10px;width:40vw;height:40px;transition:300ms;background-color:#39B387;cursor:pointer;display:flex;justify-content:center;align-items:center}@media only screen and (min-width:992px){.bookingContainer__button{width:20vw}}.bookingContainer__button:hover{background-color:#47daa4}.bookingContainer__button h2{font-size:15px;font-size:0.9375rem;line-height:1.125rem;margin:0;font-weight:600}.bookingContainer__button img{width:15px;margin-left:10px}.inputs{box-sizing:border-box!important;height:50px!important;color:#fff!important;font-weight:600!important;border:2px solid #474747!important;border-radius:30px!important;background-color:#262626!important;padding:0 20px!important;margin-bottom:20px!important;outline:#1F7757!important}.inputs::placeholder{color:#CCC}.inputs:focus{border:2px solid #fff!important}.inputs:valid{border:2px solid #fff!important;background-image:url("/assets/icons/check.png")!important;background-size:20px!important;background-position:95% center!important;background-repeat:no-repeat!important}.modal{background-color:#262626!important;color:#808080!important}.datepicker-table td.is-today{color:#fff!important;font-weight:bold!important}.datepicker-done{color:#fff!important;font-weight:bold!important}.datepicker-cancel{color:#808080!important}.select-wrapper input.select-dropdown{color:#fff!important}.datepicker-date-display,.timepicker-digital-display{background-color:#474747!important}.timepicker-plate{background-color:#474747!important}.timepicker-tick{color:#808080!important}.timepicker-canvas line{stroke:#fff!important}.timepicker-canvas-bg{fill:#fff!important}.timepicker-canvas-bearing{fill:#fff!important}.timepicker-close{color:#fff!important}[type=radio]:checked+span:after{background-color:#fff!important}[type=radio]:checked+span:after{border:2px solid #fff!important}[type=radio]:not(:checked)+span:before{border:2px solid #474747!important;background-color:#262626!important}[type=radio]+span,[type=radio]:checked+span{padding:0 30px!important}body{background-color:#1A1A1A;text-align:left;color:white}@media only screen and (min-width:768px){body{margin-top:100px}}@media only screen and (min-width:992px){body{margin:60px 0 0}}header{background-color:#1A1A1A}.booking{margin:0 10vw;padding:70px 0 100px}@media only screen and (min-width:992px){.booking{margin:0 15vw}}@media only screen and (min-width:992px){.booking{margin:0 30vw}}h1{margin-top:50px;color:#fff}form{margin:50px 0;-webkit-user-select:initial;user-select:initial}@media only screen and (min-width:992px){form{margin:30px 0 0 0;padding:40px 20px;border:5px solid #5a5a5a5a;border-radius:20px;box-sizing:border-box}}form div .bookingExtra__duration{margin-top:50px}.bar__progress{width:31%}.bookingContainer__price p{font-size:12px!important;font-size:12px;font-size:0.75rem;line-height:0.9rem}.bookingContainer__button h2{font-size:15px!important;font-size:15px;font-size:0.9375rem;line-height:1.125rem} \ No newline at end of file diff --git a/appengine/flexible/hello_world/app/static/assets/css/checkout.css b/appengine/flexible/hello_world/app/static/assets/css/checkout.css new file mode 100644 index 00000000000..09cddce453e --- /dev/null +++ b/appengine/flexible/hello_world/app/static/assets/css/checkout.css @@ -0,0 +1,172 @@ +/* Variables */ +* { + box-sizing: border-box; +} + +body { + font-family: -apple-system, BlinkMacSystemFont, sans-serif; + font-size: 16px; + -webkit-font-smoothing: antialiased; + display: flex; + justify-content: center; + align-content: center; + height: 100vh; + width: 100vw; +} + +form { + width: 30vw; + min-width: 500px; + align-self: center; + box-shadow: 0px 0px 0px 0.5px rgba(50, 50, 93, 0.1), + 0px 2px 5px 0px rgba(50, 50, 93, 0.1), 0px 1px 1.5px 0px rgba(0, 0, 0, 0.07); + border-radius: 7px; + padding: 40px; +} + +input { + border-radius: 6px; + margin-bottom: 6px; + padding: 12px; + border: 1px solid rgba(50, 50, 93, 0.1); + height: 44px; + font-size: 16px; + width: 100%; + background: white; +} + +.result-message { + line-height: 22px; + font-size: 16px; +} + +.result-message a { + color: rgb(89, 111, 214); + font-weight: 600; + text-decoration: none; +} + +.hidden { + display: none; +} + +#card-error { + color: rgb(105, 115, 134); + text-align: left; + font-size: 13px; + line-height: 17px; + margin-top: 12px; +} + +#card-element { + border-radius: 4px 4px 0 0 ; + padding: 12px; + border: 1px solid rgba(50, 50, 93, 0.1); + height: 44px; + width: 100%; + background: white; +} + +#payment-request-button { + margin-bottom: 32px; +} + +/* Buttons and links */ +button { + background: #5469d4; + color: #ffffff; + font-family: Arial, sans-serif; + border-radius: 0 0 4px 4px; + border: 0; + padding: 12px 16px; + font-size: 16px; + font-weight: 600; + cursor: pointer; + display: block; + transition: all 0.2s ease; + box-shadow: 0px 4px 5.5px 0px rgba(0, 0, 0, 0.07); + width: 100%; +} +button:hover { + filter: contrast(115%); +} +button:disabled { + opacity: 0.5; + cursor: default; +} + +/* spinner/processing state, errors */ +.spinner, +.spinner:before, +.spinner:after { + border-radius: 50%; +} +.spinner { + color: #ffffff; + font-size: 22px; + text-indent: -99999px; + margin: 0px auto; + position: relative; + width: 20px; + height: 20px; + box-shadow: inset 0 0 0 2px; + -webkit-transform: translateZ(0); + -ms-transform: translateZ(0); + transform: translateZ(0); +} +.spinner:before, +.spinner:after { + position: absolute; + content: ""; +} +.spinner:before { + width: 10.4px; + height: 20.4px; + background: #5469d4; + border-radius: 20.4px 0 0 20.4px; + top: -0.2px; + left: -0.2px; + -webkit-transform-origin: 10.4px 10.2px; + transform-origin: 10.4px 10.2px; + -webkit-animation: loading 2s infinite ease 1.5s; + animation: loading 2s infinite ease 1.5s; +} +.spinner:after { + width: 10.4px; + height: 10.2px; + background: #5469d4; + border-radius: 0 10.2px 10.2px 0; + top: -0.1px; + left: 10.2px; + -webkit-transform-origin: 0px 10.2px; + transform-origin: 0px 10.2px; + -webkit-animation: loading 2s infinite ease; + animation: loading 2s infinite ease; +} + +@-webkit-keyframes loading { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } +} +@keyframes loading { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } +} + +@media only screen and (max-width: 600px) { + form { + width: 80vw; + } +} diff --git a/appengine/flexible/hello_world/app/static/assets/css/confirmation.css b/appengine/flexible/hello_world/app/static/assets/css/confirmation.css new file mode 100644 index 00000000000..b1d17673fc5 --- /dev/null +++ b/appengine/flexible/hello_world/app/static/assets/css/confirmation.css @@ -0,0 +1 @@ +::-webkit-scrollbar{display:none}*{margin:0;padding:0;text-decoration:none;-webkit-user-select:none;user-select:none}body{color:#333;font-family:"Roboto",sans-serif;text-align:center}h1{font-weight:700;font-size:28px;font-size:1.75rem;line-height:2.1rem}@media only screen and (min-width:768px){h1{font-size:40px;font-size:2.5rem;line-height:3rem}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){h1{font-size:60px;font-size:3.75rem;line-height:4.5rem;margin-bottom:50px}}h2{font-weight:500;font-size:18px;font-size:1.125rem;line-height:1.35rem}@media only screen and (min-width:768px){h2{font-size:24px;font-size:1.5rem;line-height:1.8rem}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){h2{font-size:30px;font-size:1.875rem;line-height:2.25rem}}h3{font-weight:500;font-size:16px;font-size:1rem;line-height:1.2rem}@media only screen and (min-width:768px){h3{font-size:22px;font-size:1.375rem;line-height:1.65rem}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){h3{font-size:26px;font-size:1.625rem;line-height:1.95rem}}p{font-weight:500;font-size:14px;font-size:0.875rem;line-height:1.05rem}@media only screen and (min-width:768px){p{font-size:16px;font-size:1rem;line-height:1.2rem}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){p{font-size:18px;font-size:1.125rem;line-height:1.35rem}}header{width:100vw;height:70px;background-color:#fff;position:fixed;top:0;left:0;z-index:1;opacity:0.9;display:flex;justify-content:center;align-items:center}header img{width:120px;opacity:1}@media only screen and (min-width:768px){header img{width:150px}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){header img{width:180px}}@media only screen and (min-width:768px){header{height:100px}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){header{height:120px}}.services{margin-top:15vh;text-align:center}@media only screen and (min-width:992px){.services{margin-top:200px}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){.services{margin-top:15vh}}.services h1,.services p{margin:0 10vw}@media only screen and (min-width:992px){.services h1,.services p{margin:0 15vw}}.services p{margin:10px 0 50px}.services__scroll{overflow:scroll}.services__container{padding:30px 0;display:inline-flex;margin:0 10vw;margin-bottom:50px}@media only screen and (min-width:992px){.services__container{margin:0 15vw}}@media only screen and (min-width:992px){.services__container{padding:100px 0}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){.services__container{padding:30px 0}}.services__container--item{width:250px;height:350px;margin:0 10px;padding:20px;box-sizing:border-box;color:#fff;text-align:left;display:flex;justify-content:center;align-items:center;flex-direction:column;justify-content:flex-end}@media only screen and (min-width:768px){.services__container--item{width:321px;height:450px;margin:0 20px;padding:30px}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){.services__container--item{width:350px;height:490px}}.services__container--item p{margin:10px 0 15px}.services__container--item .button{top:0;width:150px;height:40px;display:flex;justify-content:center;align-items:center;margin:20px 0 0}@media only screen and (min-width:768px){.services__container--item .button{width:200px;height:50px}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){.services__container--item .button{width:230px}}.services__container #services__container--item1{box-shadow:0px 5px 10px 5px rgba(0,0,0,0.3);background-image:linear-gradient(rgba(31,119,87,0),#103c2c),url("https://images.unsplash.com/photo-1542848284-8afa78a08ccb?ixid=MXwxMjA3fDB8MHxzZWFyY2h8MTJ8fG1hc3NhZ2V8ZW58MHx8MHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60");background-position:center;background-size:cover}.services__container #services__container--item2{background-image:linear-gradient(rgba(26,26,26,0.6),rgba(26,26,26,0.6)),url("https://images.unsplash.com/photo-1567169556820-a566b9f53776?ixid=MXwxMjA3fDB8MHxzZWFyY2h8NXx8bmFpbHN8ZW58MHx8MHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60");background-position:center;background-size:cover}.services__container #services__container--item3{background-image:linear-gradient(rgba(26,26,26,0.6),rgba(26,26,26,0.6)),url("https://images.unsplash.com/photo-1584467735815-f778f274e296?ixid=MXwxMjA3fDB8MHxzZWFyY2h8MTJ8fGRvY3RvcnxlbnwwfDF8MHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60");background-position:center;background-size:cover}.services__container #services__container--item4{background-image:linear-gradient(rgba(26,26,26,0.6),rgba(26,26,26,0.6)),url("https://images.unsplash.com/photo-1592947945242-69312358628b?ixid=MXwxMjA3fDB8MHxzZWFyY2h8MXx8cHN5Y2hvbG9naXN0fGVufDB8MXwwfA%3D%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60");background-position:center;background-size:cover}.footer{margin-top:15vh;padding-top:15vh;text-align:center;background-color:#1A1A1A;color:#fff}@media only screen and (min-width:992px){.footer{padding:15vh 15vh;display:flex;align-items:flex-start;justify-content:space-between}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){.footer{display:block;margin-top:15vh;padding:15vh 0 0}}.footer__logo{border-bottom:1px solid #585858;padding-bottom:100px}@media only screen and (min-width:992px){.footer__logo{border:initial;padding:initial}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){.footer__logo{border-bottom:1px solid #585858;padding-bottom:100px}}.footer__logo img{width:200px;margin-bottom:10px}.footer__links{padding:40px 0;margin:0 10vw;font-size:14px;font-size:0.875rem;line-height:1.05rem}@media only screen and (min-width:992px){.footer__links{margin:0 15vw}}@media only screen and (min-width:992px){.footer__links{padding:initial;margin:0}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){.footer__links{padding:40px 0;margin:0 10vw}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px) and (min-width:992px){.footer__links{margin:0 15vw}}.footer__links--items{margin-top:20px}.footer__links--items a{display:block;color:#fff;margin-bottom:10px;transition:300ms}.footer__links--items a:hover{color:#39B387}.footer__contact{padding:40px 0}@media only screen and (min-width:992px){.footer__contact{padding:initial}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){.footer__contact{padding:40px 0}}.footer__contact--items{margin-top:20px}.footer__contact--items img{margin:0 10px;height:30px;transition:300ms}@media only screen and (min-width:992px){.footer__contact--items img:hover{height:40px}}.footer__social{padding:40px 0}@media only screen and (min-width:992px){.footer__social{padding:initial}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){.footer__social{padding:40px 0}}.footer__social--items{margin-top:20px}.footer__social--items img{margin:0 10px;height:30px;transition:300ms}@media only screen and (min-width:992px){.footer__social--items img:hover{height:40px}}.inputs{width:100%;height:50px;box-sizing:border-box;color:#fff;font-weight:600;border:2px solid #474747;border-radius:30px;background-color:#262626;padding:0 20px;margin-bottom:20px;outline:none;font-size:15px;font-size:0.9375rem;line-height:1.125rem}.inputs::placeholder{color:#CCC}.inputs:focus{border:2px solid #fff}.inputs:valid{border:2px solid #fff;background-image:url("/assets/icons/check.png");background-size:20px;background-position:95% center;background-repeat:no-repeat}.button__fullWidth,.button__mini{width:80vw;height:50px;background-color:#39B387;border-radius:10px;color:#fff;margin:0 auto;font-weight:700;transition:300ms;font-size:20px;font-size:1.25rem;line-height:1.5rem;display:flex;justify-content:center;align-items:center}.button__fullWidth:hover,.button__mini:hover{background-color:#47daa4}@media only screen and (min-width:768px){.button__fullWidth,.button__mini{width:30vw}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){.button__fullWidth,.button__mini{margin-top:30px}}.button__mini{font-weight:600;font-size:16px;font-size:1rem;line-height:1.2rem;width:90%;height:40px;background-color:rgba(57,179,135,0.5);border:2px solid #fff}body{padding-top:200px;text-align:left}@media only screen and (min-width:992px){h1,h2,h3,h4,h5,p{max-width:40vw}}main{margin-bottom:100px;margin:0 10vw}@media only screen and (min-width:992px){main{margin:0 15vw}}main h1{color:#39B387;margin:20px 0}main img{width:80px}.services h1{color:#333;margin-bottom:30px}@media only screen and (min-width:992px){.services h1{margin-bottom:0}}.reservation{margin:0 10vw;margin-top:15vh}@media only screen and (min-width:992px){.reservation{margin:0 15vw}}.reservation h1{color:#333}@media only screen and (min-width:992px){.reservation h1{margin-top:70px}}.reservation__containerDate{background-color:#1A1A1A;color:#fff;width:100%;margin-top:30px;padding:20px;box-sizing:border-box;height:200px}@media only screen and (min-width:992px){.reservation__containerDate{max-width:40vw}}.reservation__containerDate--info{padding:20px 0;height:70%;width:70%;color:#CCC;display:flex;flex-direction:column;justify-content:space-between}.reservation__containerDate--info img{width:20px;margin-right:10px}.reservation__containerDate--info__item{display:flex;justify-content:center;align-items:center;justify-content:flex-start}.reservation__containerUser{background-color:#fff;color:#333;box-shadow:0px 3px 6px #00000029;width:100%;margin-top:30px;padding:20px;box-sizing:border-box;height:200px}@media only screen and (min-width:992px){.reservation__containerUser{max-width:40vw}}.reservation__containerUser--info{padding:20px 0;height:70%;width:70%;color:#CCC;display:flex;flex-direction:column;justify-content:space-between}.reservation__containerUser--info img{width:20px;margin-right:10px}.reservation__containerUser--info__item{display:flex;justify-content:center;align-items:center;justify-content:flex-start}.actions{margin:0 10vw;margin-top:15vh;display:flex;justify-content:center;align-items:center;flex-direction:column}@media only screen and (min-width:992px){.actions{margin:0 15vw}}@media only screen and (min-width:992px){.actions{margin-top:100px;justify-content:left}}.actions a{margin:20px 0 10px}.actions a img{width:20px;margin-right:10px}.actions #actions__button2{margin:0;background-color:#fff;border:3px solid #39B387;color:#39B387} \ No newline at end of file diff --git a/appengine/flexible/hello_world/app/static/assets/css/email.css b/appengine/flexible/hello_world/app/static/assets/css/email.css new file mode 100644 index 00000000000..310b2d8b6f0 --- /dev/null +++ b/appengine/flexible/hello_world/app/static/assets/css/email.css @@ -0,0 +1 @@ +::-webkit-scrollbar{display:none}*{margin:0;padding:0;text-decoration:none;-webkit-user-select:none;user-select:none}body{color:#333;font-family:"Roboto",sans-serif;text-align:center}h1{font-weight:700;font-size:28px;font-size:1.75rem;line-height:2.1rem}@media only screen and (min-width:768px){h1{font-size:40px;font-size:2.5rem;line-height:3rem}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){h1{font-size:60px;font-size:3.75rem;line-height:4.5rem;margin-bottom:50px}}h2{font-weight:500;font-size:18px;font-size:1.125rem;line-height:1.35rem}@media only screen and (min-width:768px){h2{font-size:24px;font-size:1.5rem;line-height:1.8rem}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){h2{font-size:30px;font-size:1.875rem;line-height:2.25rem}}h3{font-weight:500;font-size:16px;font-size:1rem;line-height:1.2rem}@media only screen and (min-width:768px){h3{font-size:22px;font-size:1.375rem;line-height:1.65rem}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){h3{font-size:26px;font-size:1.625rem;line-height:1.95rem}}p{font-weight:500;font-size:14px;font-size:0.875rem;line-height:1.05rem}@media only screen and (min-width:768px){p{font-size:16px;font-size:1rem;line-height:1.2rem}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){p{font-size:18px;font-size:1.125rem;line-height:1.35rem}}header{width:100vw;height:70px;background-color:#fff;position:fixed;top:0;left:0;z-index:1;opacity:0.9;display:flex;justify-content:center;align-items:center}header img{width:120px;opacity:1}@media only screen and (min-width:768px){header img{width:150px}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){header img{width:180px}}@media only screen and (min-width:768px){header{height:100px}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){header{height:120px}}.inputs{width:100%;height:50px;box-sizing:border-box;color:#fff;font-weight:600;border:2px solid #474747;border-radius:30px;background-color:#262626;padding:0 20px;margin-bottom:20px;outline:none;font-size:15px;font-size:0.9375rem;line-height:1.125rem}.inputs::placeholder{color:#CCC}.inputs:focus{border:2px solid #fff}.inputs:valid{border:2px solid #fff;background-image:url("/assets/icons/check.png");background-size:20px;background-position:95% center;background-repeat:no-repeat}.services{margin-top:15vh;text-align:center}@media only screen and (min-width:992px){.services{margin-top:200px}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){.services{margin-top:15vh}}.services h1,.services p{margin:0 10vw}@media only screen and (min-width:992px){.services h1,.services p{margin:0 15vw}}.services p{margin:10px 0 50px}.services__scroll{overflow:scroll}.services__container{padding:30px 0;display:inline-flex;margin:0 10vw;margin-bottom:50px}@media only screen and (min-width:992px){.services__container{margin:0 15vw}}@media only screen and (min-width:992px){.services__container{padding:100px 0}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){.services__container{padding:30px 0}}.services__container--item{width:250px;height:350px;margin:0 10px;padding:20px;box-sizing:border-box;color:#fff;text-align:left;display:flex;justify-content:center;align-items:center;flex-direction:column;justify-content:flex-end}@media only screen and (min-width:768px){.services__container--item{width:321px;height:450px;margin:0 20px;padding:30px}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){.services__container--item{width:350px;height:490px}}.services__container--item p{margin:10px 0 15px}.services__container--item .button{top:0;width:150px;height:40px;display:flex;justify-content:center;align-items:center;margin:20px 0 0}@media only screen and (min-width:768px){.services__container--item .button{width:200px;height:50px}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){.services__container--item .button{width:230px}}.services__container #services__container--item1{box-shadow:0px 5px 10px 5px rgba(0,0,0,0.3);background-image:linear-gradient(rgba(31,119,87,0),#103c2c),url("https://images.unsplash.com/photo-1542848284-8afa78a08ccb?ixid=MXwxMjA3fDB8MHxzZWFyY2h8MTJ8fG1hc3NhZ2V8ZW58MHx8MHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60");background-position:center;background-size:cover}.services__container #services__container--item2{background-image:linear-gradient(rgba(26,26,26,0.6),rgba(26,26,26,0.6)),url("https://images.unsplash.com/photo-1567169556820-a566b9f53776?ixid=MXwxMjA3fDB8MHxzZWFyY2h8NXx8bmFpbHN8ZW58MHx8MHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60");background-position:center;background-size:cover}.services__container #services__container--item3{background-image:linear-gradient(rgba(26,26,26,0.6),rgba(26,26,26,0.6)),url("https://images.unsplash.com/photo-1584467735815-f778f274e296?ixid=MXwxMjA3fDB8MHxzZWFyY2h8MTJ8fGRvY3RvcnxlbnwwfDF8MHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60");background-position:center;background-size:cover}.services__container #services__container--item4{background-image:linear-gradient(rgba(26,26,26,0.6),rgba(26,26,26,0.6)),url("https://images.unsplash.com/photo-1592947945242-69312358628b?ixid=MXwxMjA3fDB8MHxzZWFyY2h8MXx8cHN5Y2hvbG9naXN0fGVufDB8MXwwfA%3D%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60");background-position:center;background-size:cover}.footer{margin-top:15vh;padding-top:15vh;text-align:center;background-color:#1A1A1A;color:#fff}@media only screen and (min-width:992px){.footer{padding:15vh 15vh;display:flex;align-items:flex-start;justify-content:space-between}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){.footer{display:block;margin-top:15vh;padding:15vh 0 0}}.footer__logo{border-bottom:1px solid #585858;padding-bottom:100px}@media only screen and (min-width:992px){.footer__logo{border:initial;padding:initial}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){.footer__logo{border-bottom:1px solid #585858;padding-bottom:100px}}.footer__logo img{width:200px;margin-bottom:10px}.footer__links{padding:40px 0;margin:0 10vw;font-size:14px;font-size:0.875rem;line-height:1.05rem}@media only screen and (min-width:992px){.footer__links{margin:0 15vw}}@media only screen and (min-width:992px){.footer__links{padding:initial;margin:0}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){.footer__links{padding:40px 0;margin:0 10vw}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px) and (min-width:992px){.footer__links{margin:0 15vw}}.footer__links--items{margin-top:20px}.footer__links--items a{display:block;color:#fff;margin-bottom:10px;transition:300ms}.footer__links--items a:hover{color:#39B387}.footer__contact{padding:40px 0}@media only screen and (min-width:992px){.footer__contact{padding:initial}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){.footer__contact{padding:40px 0}}.footer__contact--items{margin-top:20px}.footer__contact--items img{margin:0 10px;height:30px;transition:300ms}@media only screen and (min-width:992px){.footer__contact--items img:hover{height:40px}}.footer__social{padding:40px 0}@media only screen and (min-width:992px){.footer__social{padding:initial}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){.footer__social{padding:40px 0}}.footer__social--items{margin-top:20px}.footer__social--items img{margin:0 10px;height:30px;transition:300ms}@media only screen and (min-width:992px){.footer__social--items img:hover{height:40px}}body{padding:200px 0 0;text-align:left}main{margin:0 10vw;margin-bottom:100px}@media only screen and (min-width:992px){main{margin:0 15vw}}main h1{color:#39B387;margin:20px 0}main img{width:80px}.services h1{color:#333;margin-bottom:30px} \ No newline at end of file diff --git a/appengine/flexible/hello_world/app/static/assets/css/googleMap.css b/appengine/flexible/hello_world/app/static/assets/css/googleMap.css new file mode 100644 index 00000000000..ed9cfef05f1 --- /dev/null +++ b/appengine/flexible/hello_world/app/static/assets/css/googleMap.css @@ -0,0 +1,15 @@ +#map { + height: 100%; + width: 100%; +} +#googleMap__modal { + margin: 0 auto; + width: 90% !important ; + height: 100% !important ; +} +.modal-header a { + background-color: #39B387; + color: white; + width: 100%; + text-align: right; +} diff --git a/appengine/flexible/hello_world/app/static/assets/css/logIn.css b/appengine/flexible/hello_world/app/static/assets/css/logIn.css new file mode 100644 index 00000000000..677cf25984d --- /dev/null +++ b/appengine/flexible/hello_world/app/static/assets/css/logIn.css @@ -0,0 +1 @@ +::-webkit-scrollbar{display:none}*{margin:0;padding:0;text-decoration:none;-webkit-user-select:none;user-select:none}body{color:#333;font-family:"Roboto",sans-serif;text-align:center}h1{font-weight:700;font-size:28px;font-size:1.75rem;line-height:2.1rem}@media only screen and (min-width:768px){h1{font-size:40px;font-size:2.5rem;line-height:3rem}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){h1{font-size:60px;font-size:3.75rem;line-height:4.5rem;margin-bottom:50px}}h2{font-weight:500;font-size:18px;font-size:1.125rem;line-height:1.35rem}@media only screen and (min-width:768px){h2{font-size:24px;font-size:1.5rem;line-height:1.8rem}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){h2{font-size:30px;font-size:1.875rem;line-height:2.25rem}}h3{font-weight:500;font-size:16px;font-size:1rem;line-height:1.2rem}@media only screen and (min-width:768px){h3{font-size:22px;font-size:1.375rem;line-height:1.65rem}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){h3{font-size:26px;font-size:1.625rem;line-height:1.95rem}}p{font-weight:500;font-size:14px;font-size:0.875rem;line-height:1.05rem}@media only screen and (min-width:768px){p{font-size:16px;font-size:1rem;line-height:1.2rem}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){p{font-size:18px;font-size:1.125rem;line-height:1.35rem}}header{width:100vw;height:70px;background-color:#fff;position:fixed;top:0;left:0;z-index:1;opacity:0.9;display:flex;justify-content:center;align-items:center}header img{width:120px;opacity:1}@media only screen and (min-width:768px){header img{width:150px}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){header img{width:180px}}@media only screen and (min-width:768px){header{height:100px}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){header{height:120px}}.bar{position:fixed;left:0;top:70px;width:100vw;height:10px;background-color:#262626}.bar__progress{height:10px;background-color:#39B387}@media only screen and (min-width:768px){.bar{top:100px}}.bookingContainer{width:100%;height:auto;z-index:1;bottom:0;left:0;padding:20px 10vw;border-top:2px solid #5a5a5a5a;box-sizing:border-box;background-color:#1A1A1A;position:fixed;display:flex;justify-content:center;align-items:center;justify-content:space-between;color:#fff}@media only screen and (min-width:992px){.bookingContainer{position:relative;top:30px;width:auto;padding:20px 0;background-color:transparent}}.bookingContainer__price p{display:block;color:#808080;font-size:12px;font-size:0.75rem;line-height:0.9rem}.bookingContainer__price #price{color:#fff}.bookingContainer__button{color:#fff;outline:none;border:none;font-weight:600;border-radius:10px;width:40vw;height:40px;transition:300ms;background-color:#39B387;cursor:pointer;display:flex;justify-content:center;align-items:center}@media only screen and (min-width:992px){.bookingContainer__button{width:20vw}}.bookingContainer__button:hover{background-color:#47daa4}.bookingContainer__button h2{font-size:15px;font-size:0.9375rem;line-height:1.125rem;margin:0;font-weight:600}.bookingContainer__button img{width:15px;margin-left:10px}.inputs{width:100%;height:50px;box-sizing:border-box;color:#fff;font-weight:600;border:2px solid #474747;border-radius:30px;background-color:#262626;padding:0 20px;margin-bottom:20px;outline:none;font-size:15px;font-size:0.9375rem;line-height:1.125rem}.inputs::placeholder{color:#CCC}.inputs:focus{border:2px solid #fff}.inputs:valid{border:2px solid #fff;background-image:url("/assets/icons/check.png");background-size:20px;background-position:95% center;background-repeat:no-repeat}.socialMediaAccount{text-align:center}@media only screen and (min-width:992px){.socialMediaAccount{margin-top:20px}}.socialMediaAccount p{color:#CCC}.socialMediaAccount__items{display:flex;justify-content:center;align-items:center}.socialMediaAccount__items a{width:40px;height:40px;border-radius:50px;margin:10px 10px;box-sizing:border-box;border:1px solid #fff;display:flex;justify-content:center;align-items:center}.socialMediaAccount__items a img{width:20px}.socialMediaAccount__items a #socialMediaAccount__items--fb{height:20px;width:auto}body{background-color:#1A1A1A;padding:70px 0 100px;text-align:left}@media only screen and (min-width:768px){body{margin-top:100px}}@media only screen and (min-width:992px){body{margin:150px 0 0;padding:0}}header{background-color:#1A1A1A}.booking{margin:0 10vw}@media only screen and (min-width:992px){.booking{margin:0 15vw}}@media only screen and (min-width:992px){.booking{margin:0 30vw}}h1{margin-top:50px;color:#fff}form{margin:50px 0 20px}@media only screen and (min-width:992px){form{margin:30px 0 0 0;padding:40px 20px;border:5px solid #5a5a5a5a;border-radius:20px;box-sizing:border-box}}.bar__progress{width:66%}.recoverPassword{color:#808080;margin:0 10vw;margin-bottom:40px}@media only screen and (min-width:992px){.recoverPassword{margin:0 15vw}}@media only screen and (min-width:992px){.recoverPassword{margin:20px 30vw}}.recoverPassword a{color:#fff} \ No newline at end of file diff --git a/appengine/flexible/hello_world/app/static/assets/css/main.css b/appengine/flexible/hello_world/app/static/assets/css/main.css new file mode 100644 index 00000000000..20858d89b75 --- /dev/null +++ b/appengine/flexible/hello_world/app/static/assets/css/main.css @@ -0,0 +1 @@ +::-webkit-scrollbar{display:none}*{margin:0;padding:0;text-decoration:none;-webkit-user-select:none;user-select:none}body{color:#333;font-family:"Roboto",sans-serif;text-align:center}h1{font-weight:700;font-size:28px;font-size:1.75rem;line-height:2.1rem}@media only screen and (min-width:768px){h1{font-size:40px;font-size:2.5rem;line-height:3rem}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){h1{font-size:60px;font-size:3.75rem;line-height:4.5rem;margin-bottom:50px}}h2{font-weight:500;font-size:18px;font-size:1.125rem;line-height:1.35rem}@media only screen and (min-width:768px){h2{font-size:24px;font-size:1.5rem;line-height:1.8rem}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){h2{font-size:30px;font-size:1.875rem;line-height:2.25rem}}h3{font-weight:500;font-size:16px;font-size:1rem;line-height:1.2rem}@media only screen and (min-width:768px){h3{font-size:22px;font-size:1.375rem;line-height:1.65rem}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){h3{font-size:26px;font-size:1.625rem;line-height:1.95rem}}p{font-weight:500;font-size:14px;font-size:0.875rem;line-height:1.05rem}@media only screen and (min-width:768px){p{font-size:16px;font-size:1rem;line-height:1.2rem}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){p{font-size:18px;font-size:1.125rem;line-height:1.35rem}}header{width:100vw;height:70px;background-color:#fff;position:fixed;top:0;left:0;z-index:1;opacity:0.9;display:flex;justify-content:center;align-items:center}header img{width:120px;opacity:1}@media only screen and (min-width:768px){header img{width:150px}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){header img{width:180px}}@media only screen and (min-width:768px){header{height:100px}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){header{height:120px}}main{margin-top:70px}main .main__image{background-size:cover;background-repeat:no-repeat;background-position:center center;width:100%;height:45vh;object-fit:cover}main .main__text{margin:0 10vw;margin-top:20px;margin-bottom:40px}@media only screen and (min-width:992px){main .main__text{margin:0 15vw}}main .main__text h1{margin-bottom:20px}main .main__text p{color:#CCC}.button__fullWidth,.button__mini{width:80vw;height:50px;background-color:#39B387;border-radius:10px;color:#fff;margin:0 auto;font-weight:700;transition:300ms;font-size:20px;font-size:1.25rem;line-height:1.5rem;display:flex;justify-content:center;align-items:center}.button__fullWidth:hover,.button__mini:hover{background-color:#47daa4}@media only screen and (min-width:768px){.button__fullWidth,.button__mini{width:30vw}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){.button__fullWidth,.button__mini{margin-top:30px}}.button__mini{font-weight:600;font-size:16px;font-size:1rem;line-height:1.2rem;width:90%;height:40px;background-color:rgba(57,179,135,0.5);border:2px solid #fff}.inputs{width:100%;height:50px;box-sizing:border-box;color:#fff;font-weight:600;border:2px solid #474747;border-radius:30px;background-color:#262626;padding:0 20px;margin-bottom:20px;outline:none;font-size:15px;font-size:0.9375rem;line-height:1.125rem}.inputs::placeholder{color:#CCC}.inputs:focus{border:2px solid #fff}.inputs:valid{border:2px solid #fff;background-image:url("/assets/icons/check.png");background-size:20px;background-position:95% center;background-repeat:no-repeat}.benefits{margin-top:15vh}.benefits h1{margin:0 10vw}@media only screen and (min-width:992px){.benefits h1{margin:0 15vw}}.benefits__scroll{overflow:scroll}.benefits__cardsContainer{padding:50px 0;display:inline-flex;margin:0 10vw}@media only screen and (min-width:992px){.benefits__cardsContainer{margin:0 15vw}}.benefits__cardsContainer--item{width:150px;height:200px;padding:20px;margin:0 10px;border-radius:10px;box-sizing:border-box;text-align:left;transition:300ms;display:flex;justify-content:center;align-items:center;align-items:flex-end}@media only screen and (min-width:768px){.benefits__cardsContainer--item{margin:0 20px}}.benefits__cardsContainer #benefits__cardsContainer--item1{background-image:linear-gradient(rgba(26,26,26,0.4),rgba(26,26,26,0.4)),url("https://images.unsplash.com/photo-1470010762743-1fa2363f65ca?ixid=MXwxMjA3fDB8MHxzZWFyY2h8Mzd8fHJlbGF4fGVufDB8fDB8&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60");background-position:center;background-size:cover}.benefits__cardsContainer #benefits__cardsContainer--item2{background-image:linear-gradient(rgba(26,26,26,0.4),rgba(26,26,26,0.4)),url("https://images.unsplash.com/photo-1591343395082-e120087004b4?ixid=MXwxMjA3fDB8MHxzZWFyY2h8NjR8fHJlbGF4fGVufDB8fDB8&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60");background-position:center;background-size:cover}.benefits__cardsContainer #benefits__cardsContainer--item3{background-image:linear-gradient(rgba(26,26,26,0.4),rgba(26,26,26,0.4)),url("https://images.unsplash.com/photo-1558554142-0b016c857381?ixid=MXwxMjA3fDB8MHxzZWFyY2h8MTF8fGhlYWx0aHxlbnwwfHwwfA%3D%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60");background-position:center;background-size:cover}.benefits__cardsContainer #benefits__cardsContainer--item4{background-image:linear-gradient(rgba(26,26,26,0.4),rgba(26,26,26,0.4)),url("https://images.unsplash.com/photo-1582944682702-8d357814f820?ixid=MXwxMjA3fDB8MHxzZWFyY2h8M3x8c2V4fGVufDB8fDB8&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60");background-position:center;background-size:cover}.benefits__cardsContainer #benefits__cardsContainer--item5{background-image:linear-gradient(rgba(26,26,26,0.4),rgba(26,26,26,0.4)),url("https://images.unsplash.com/photo-1466193341027-56e68017ee2d?ixid=MXwxMjA3fDB8MHxzZWFyY2h8N3x8aGFwcHl8ZW58MHx8MHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60");background-position:center;background-size:cover}.how{margin-top:15vh}.how__scroll{overflow:scroll}.how__container{display:inline-flex;padding:50px 0;margin:0 10vw}@media only screen and (min-width:992px){.how__container{margin:0 15vw}}.how__container--item{width:160px;height:auto;border-radius:10px;margin:0 10px;padding:20px;box-sizing:border-box;background-color:#262626;text-align:left}.how__container--item__step{color:#474747;font-weight:bolder;font-size:60px;font-size:3.75rem;line-height:4.5rem}.what{margin:0 10vw;margin-top:15vh}@media only screen and (min-width:992px){.what{margin:0 15vw}}@media only screen and (min-width:992px){.what{margin-top:15vh}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){.what{margin-top:15vh}}.what h3{color:#fff;margin-bottom:10px}.what__cardContainer{padding:30px 0;display:flex;justify-content:center;align-items:center;flex-direction:column}.what__cardContainer--item{width:100%;height:auto;border-radius:10px;margin-bottom:10px;padding:20px;box-sizing:border-box;color:#CCC;background-color:#262626;display:flex;justify-content:center;align-items:center;flex-direction:column;align-items:flex-start;text-align:left}.what__cardContainer--item img{width:40px;margin-bottom:20px}@media only screen and (min-width:768px){.what__cardContainer--item{width:70%;margin-bottom:30px}}@media only screen and (min-width:992px){.what__cardContainer--item{width:55%}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){.what__cardContainer--item{width:70%}}.bulletPoints{text-align:center;margin:0 10vw;margin-top:15vh;margin-bottom:15vh}@media only screen and (min-width:992px){.bulletPoints{margin:0 15vw}}@media only screen and (min-width:992px){.bulletPoints{display:flex;padding:30vh 0;margin:0 15vw;display:flex;justify-content:center;align-items:center;justify-content:space-between}}.bulletPoints__container{margin-bottom:80px;display:flex;justify-content:center;align-items:center;flex-direction:column}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){.bulletPoints__container{margin-bottom:100px}}@media only screen and (min-width:992px){.bulletPoints__container{margin:0}}.bulletPoints__container--iconContainer{width:70px;height:70px;border-radius:100%;background-color:#39B387;display:flex;justify-content:center;align-items:center;margin:0 auto}.bulletPoints__container--iconContainer img{height:40px}@media only screen and (min-width:768px){.bulletPoints__container--iconContainer img{height:50px}}@media only screen and (min-width:768px){.bulletPoints__container--iconContainer{width:100px;height:100px}}.bulletPoints__container--iconContainer #bulletPoints__container--iconContainer__partners{height:30px;padding-left:5px}.bulletPoints__container h1{margin:10px 0}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){.bulletPoints__container h1{margin:30px 0}}@media only screen and (min-width:768px){.bulletPoints__container p{width:40vh}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){.bulletPoints__container p{width:40vh}}.testimonials{margin-top:15vh}.testimonials h1{text-align:center;margin:0 10vw}@media only screen and (min-width:992px){.testimonials h1{margin:0 15vw}}.testimonials__scroll{overflow:scroll}.testimonials__container{display:inline-flex;margin:0 10vw;padding:50px 0 30px}@media only screen and (min-width:992px){.testimonials__container{margin:0 15vw}}@media only screen and (min-width:992px){.testimonials__container{display:flex;justify-content:center;align-items:center;justify-content:space-between;margin:0 15vw}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){.testimonials__container{display:inline-flex}}.testimonials__container--item{width:250px;height:200px;margin-right:30px;background-color:#FAFAFA;box-sizing:border-box;display:flex;justify-content:center;align-items:center;flex-direction:column;justify-content:space-between}@media only screen and (min-width:768px){.testimonials__container--item{width:300px;height:240px}}@media only screen and (min-width:992px){.testimonials__container--item{margin:70px 0 0}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){.testimonials__container--item{width:350px;height:250px;margin-right:30px}}.testimonials__container--item--iconQuote{width:50px;position:relative;top:-15px;right:80px}@media only screen and (min-width:768px){.testimonials__container--item--iconQuote{width:60px;top:-20px;right:105px}}@media only screen and (min-width:992px){.testimonials__container--item--iconQuote{width:70px;top:-25px;right:85px}}.testimonials__container--item--calification{padding:0 20px 20px;margin-top:10px;text-align:left}@media only screen and (min-width:768px){.testimonials__container--item--calification{padding:0 30px}}.testimonials__container--item--calification--stars{width:100px;opacity:0.5;margin-bottom:10px}@media only screen and (min-width:768px){.testimonials__container--item--calification--stars{width:120px}}.testimonials__container--item--calification p{font-style:italic}.testimonials__container--item--person{width:100%;height:50px;background-color:#39B387;display:flex;justify-content:center;align-items:center;justify-content:flex-start}@media only screen and (min-width:768px){.testimonials__container--item--person{height:60px;margin-top:10px}}.testimonials__container--item--person img{width:30px;height:30px;border-radius:100%;margin:0 10px 0 20px;object-fit:cover}@media only screen and (min-width:768px){.testimonials__container--item--person img{width:40px;height:40px}}@media only screen and (min-width:992px){.testimonials__container--item--person img{margin:0 20px 0 30px}}.testimonials__container--item--person h3{color:#fff}.services{margin-top:15vh;text-align:center}@media only screen and (min-width:992px){.services{margin-top:200px}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){.services{margin-top:15vh}}.services h1,.services p{margin:0 10vw}@media only screen and (min-width:992px){.services h1,.services p{margin:0 15vw}}.services p{margin:10px 0 50px}.services__scroll{overflow:scroll}.services__container{padding:30px 0;display:inline-flex;margin:0 10vw;margin-bottom:50px}@media only screen and (min-width:992px){.services__container{margin:0 15vw}}@media only screen and (min-width:992px){.services__container{padding:100px 0}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){.services__container{padding:30px 0}}.services__container--item{width:250px;height:350px;margin:0 10px;padding:20px;box-sizing:border-box;color:#fff;text-align:left;display:flex;justify-content:center;align-items:center;flex-direction:column;justify-content:flex-end}@media only screen and (min-width:768px){.services__container--item{width:321px;height:450px;margin:0 20px;padding:30px}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){.services__container--item{width:350px;height:490px}}.services__container--item p{margin:10px 0 15px}.services__container--item .button{top:0;width:150px;height:40px;display:flex;justify-content:center;align-items:center;margin:20px 0 0}@media only screen and (min-width:768px){.services__container--item .button{width:200px;height:50px}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){.services__container--item .button{width:230px}}.services__container #services__container--item1{box-shadow:0px 5px 10px 5px rgba(0,0,0,0.3);background-image:linear-gradient(rgba(31,119,87,0),#103c2c),url("https://images.unsplash.com/photo-1542848284-8afa78a08ccb?ixid=MXwxMjA3fDB8MHxzZWFyY2h8MTJ8fG1hc3NhZ2V8ZW58MHx8MHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60");background-position:center;background-size:cover}.services__container #services__container--item2{background-image:linear-gradient(rgba(26,26,26,0.6),rgba(26,26,26,0.6)),url("https://images.unsplash.com/photo-1567169556820-a566b9f53776?ixid=MXwxMjA3fDB8MHxzZWFyY2h8NXx8bmFpbHN8ZW58MHx8MHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60");background-position:center;background-size:cover}.services__container #services__container--item3{background-image:linear-gradient(rgba(26,26,26,0.6),rgba(26,26,26,0.6)),url("https://images.unsplash.com/photo-1584467735815-f778f274e296?ixid=MXwxMjA3fDB8MHxzZWFyY2h8MTJ8fGRvY3RvcnxlbnwwfDF8MHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60");background-position:center;background-size:cover}.services__container #services__container--item4{background-image:linear-gradient(rgba(26,26,26,0.6),rgba(26,26,26,0.6)),url("https://images.unsplash.com/photo-1592947945242-69312358628b?ixid=MXwxMjA3fDB8MHxzZWFyY2h8MXx8cHN5Y2hvbG9naXN0fGVufDB8MXwwfA%3D%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60");background-position:center;background-size:cover}.footer{margin-top:15vh;padding-top:15vh;text-align:center;background-color:#1A1A1A;color:#fff}@media only screen and (min-width:992px){.footer{padding:15vh 15vh;display:flex;align-items:flex-start;justify-content:space-between}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){.footer{display:block;margin-top:15vh;padding:15vh 0 0}}.footer__logo{border-bottom:1px solid #585858;padding-bottom:100px}@media only screen and (min-width:992px){.footer__logo{border:initial;padding:initial}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){.footer__logo{border-bottom:1px solid #585858;padding-bottom:100px}}.footer__logo img{width:200px;margin-bottom:10px}.footer__links{padding:40px 0;margin:0 10vw;font-size:14px;font-size:0.875rem;line-height:1.05rem}@media only screen and (min-width:992px){.footer__links{margin:0 15vw}}@media only screen and (min-width:992px){.footer__links{padding:initial;margin:0}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){.footer__links{padding:40px 0;margin:0 10vw}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px) and (min-width:992px){.footer__links{margin:0 15vw}}.footer__links--items{margin-top:20px}.footer__links--items a{display:block;color:#fff;margin-bottom:10px;transition:300ms}.footer__links--items a:hover{color:#39B387}.footer__contact{padding:40px 0}@media only screen and (min-width:992px){.footer__contact{padding:initial}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){.footer__contact{padding:40px 0}}.footer__contact--items{margin-top:20px}.footer__contact--items img{margin:0 10px;height:30px;transition:300ms}@media only screen and (min-width:992px){.footer__contact--items img:hover{height:40px}}.footer__social{padding:40px 0}@media only screen and (min-width:992px){.footer__social{padding:initial}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){.footer__social{padding:40px 0}}.footer__social--items{margin-top:20px}.footer__social--items img{margin:0 10px;height:30px;transition:300ms}@media only screen and (min-width:992px){.footer__social--items img:hover{height:40px}}@media only screen and (min-width:992px){main{display:inline-flex;align-items:center;justify-content:space-between}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){main{display:initial}}main .main__image{background-image:linear-gradient(180deg,white 0%,rgba(255,255,255,0) 40%,rgba(255,255,255,0) 60%,white 100%),url("https://images.unsplash.com/photo-1599486970440-beacf3b2135e?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=634&q=80");height:60vh;background-position:center 60%}@media only screen and (min-width:768px){main .main__image{background-position:center center}}@media only screen and (min-width:992px){main .main__image{height:calc(100vh - 70px);width:65vw;position:relative;left:40%}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){main .main__image{height:60vh;width:initial;background-position:center 40%;position:initial}}@media only screen and (min-width:992px){main .main__text{position:relative;right:35%;bottom:50px;text-align:left}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){main .main__text{position:initial;text-align:center}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){main .main__text h1{margin-top:50px}}main .main__text h3{color:#CCC}@media only screen and (min-width:992px){main .button__fullWidth,main .button__mini{position:relative;right:calc(75% + 2px);top:90px}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){main .button__fullWidth,main .button__mini{position:initial}}.how{padding:15vh 0;background-color:#FAFAFA}.how__container--item{background-color:#fff}.how__container--item__step{color:#F2F2F2}.benefits__cardsContainer{color:#fff}.benefits #benefits__cardsContainer--item1{background-image:linear-gradient(rgba(26,26,26,0.4),rgba(26,26,26,0.4)),url("https://images.unsplash.com/photo-1549341171-7ea821073142?ixid=MXwxMjA3fDB8MHxzZWFyY2h8MTR8fGluZmx1ZW5jZXxlbnwwfHwwfA%3D%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=80");background-position:center;background-size:cover}.benefits #benefits__cardsContainer--item2{background-image:linear-gradient(rgba(26,26,26,0.4),rgba(26,26,26,0.4)),url("https://images.unsplash.com/photo-1606103920295-9a091573f160?ixid=MXwxMjA3fDB8MHxzZWFyY2h8Mnx8Ym9yZWR8ZW58MHx8MHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=80");background-position:center;background-size:cover}.benefits #benefits__cardsContainer--item3{background-image:linear-gradient(rgba(26,26,26,0.4),rgba(26,26,26,0.4)),url("https://images.unsplash.com/photo-1434494878577-86c23bcb06b9?ixid=MXwxMjA3fDB8MHxzZWFyY2h8MTF8fHRpbWV8ZW58MHx8MHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=80");background-position:center;background-size:cover}.benefits #benefits__cardsContainer--item4{background-image:linear-gradient(rgba(26,26,26,0.4),rgba(26,26,26,0.4)),url("https://images.unsplash.com/photo-1598601193393-e750daeefbca?ixid=MXwxMjA3fDB8MHxzZWFyY2h8MjZ8fGhhcGluZXNzfGVufDB8fDB8&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=80");background-position:center;background-size:cover}.what{background-color:#FAFAFA;margin:15vh 0 0;padding:15vh 0}.what h3{color:#333}.what__cardContainer{margin:0 10vw}@media only screen and (min-width:992px){.what__cardContainer{margin:0 15vw}}.what__cardContainer--item{background-color:#fff}.what #special__icon{width:30px} \ No newline at end of file diff --git a/appengine/flexible/hello_world/app/static/assets/css/massage.css b/appengine/flexible/hello_world/app/static/assets/css/massage.css new file mode 100644 index 00000000000..6db39422657 --- /dev/null +++ b/appengine/flexible/hello_world/app/static/assets/css/massage.css @@ -0,0 +1 @@ +::-webkit-scrollbar{display:none}*{margin:0;padding:0;text-decoration:none;-webkit-user-select:none;user-select:none}body{color:#333;font-family:"Roboto",sans-serif;text-align:center}h1{font-weight:700;font-size:28px;font-size:1.75rem;line-height:2.1rem}@media only screen and (min-width:768px){h1{font-size:40px;font-size:2.5rem;line-height:3rem}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){h1{font-size:60px;font-size:3.75rem;line-height:4.5rem;margin-bottom:50px}}h2{font-weight:500;font-size:18px;font-size:1.125rem;line-height:1.35rem}@media only screen and (min-width:768px){h2{font-size:24px;font-size:1.5rem;line-height:1.8rem}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){h2{font-size:30px;font-size:1.875rem;line-height:2.25rem}}h3{font-weight:500;font-size:16px;font-size:1rem;line-height:1.2rem}@media only screen and (min-width:768px){h3{font-size:22px;font-size:1.375rem;line-height:1.65rem}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){h3{font-size:26px;font-size:1.625rem;line-height:1.95rem}}p{font-weight:500;font-size:14px;font-size:0.875rem;line-height:1.05rem}@media only screen and (min-width:768px){p{font-size:16px;font-size:1rem;line-height:1.2rem}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){p{font-size:18px;font-size:1.125rem;line-height:1.35rem}}header{width:100vw;height:70px;background-color:#fff;position:fixed;top:0;left:0;z-index:1;opacity:0.9;display:flex;justify-content:center;align-items:center}header img{width:120px;opacity:1}@media only screen and (min-width:768px){header img{width:150px}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){header img{width:180px}}@media only screen and (min-width:768px){header{height:100px}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){header{height:120px}}.button__fullWidth,.button__mini{width:80vw;height:50px;background-color:#39B387;border-radius:10px;color:#fff;margin:0 auto;font-weight:700;transition:300ms;font-size:20px;font-size:1.25rem;line-height:1.5rem;display:flex;justify-content:center;align-items:center}.button__fullWidth:hover,.button__mini:hover{background-color:#47daa4}@media only screen and (min-width:768px){.button__fullWidth,.button__mini{width:30vw}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){.button__fullWidth,.button__mini{margin-top:30px}}.button__mini{font-weight:600;font-size:16px;font-size:1rem;line-height:1.2rem;width:90%;height:40px;background-color:rgba(57,179,135,0.5);border:2px solid #fff}main{margin-top:70px}main .main__image{background-size:cover;background-repeat:no-repeat;background-position:center center;width:100%;height:45vh;object-fit:cover}main .main__text{margin:0 10vw;margin-top:20px;margin-bottom:40px}@media only screen and (min-width:992px){main .main__text{margin:0 15vw}}main .main__text h1{margin-bottom:20px}main .main__text p{color:#CCC}.how{margin-top:15vh}.how__scroll{overflow:scroll}.how__container{display:inline-flex;padding:50px 0;margin:0 10vw}@media only screen and (min-width:992px){.how__container{margin:0 15vw}}.how__container--item{width:160px;height:auto;border-radius:10px;margin:0 10px;padding:20px;box-sizing:border-box;background-color:#262626;text-align:left}.how__container--item__step{color:#474747;font-weight:bolder;font-size:60px;font-size:3.75rem;line-height:4.5rem}.what{margin:0 10vw;margin-top:15vh}@media only screen and (min-width:992px){.what{margin:0 15vw}}@media only screen and (min-width:992px){.what{margin-top:15vh}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){.what{margin-top:15vh}}.what h3{color:#fff;margin-bottom:10px}.what__cardContainer{padding:30px 0;display:flex;justify-content:center;align-items:center;flex-direction:column}.what__cardContainer--item{width:100%;height:auto;border-radius:10px;margin-bottom:10px;padding:20px;box-sizing:border-box;color:#CCC;background-color:#262626;display:flex;justify-content:center;align-items:center;flex-direction:column;align-items:flex-start;text-align:left}.what__cardContainer--item img{width:40px;margin-bottom:20px}@media only screen and (min-width:768px){.what__cardContainer--item{width:70%;margin-bottom:30px}}@media only screen and (min-width:992px){.what__cardContainer--item{width:55%}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){.what__cardContainer--item{width:70%}}.testimonials{margin-top:15vh}.testimonials h1{text-align:center;margin:0 10vw}@media only screen and (min-width:992px){.testimonials h1{margin:0 15vw}}.testimonials__scroll{overflow:scroll}.testimonials__container{display:inline-flex;margin:0 10vw;padding:50px 0 30px}@media only screen and (min-width:992px){.testimonials__container{margin:0 15vw}}@media only screen and (min-width:992px){.testimonials__container{display:flex;justify-content:center;align-items:center;justify-content:space-between;margin:0 15vw}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){.testimonials__container{display:inline-flex}}.testimonials__container--item{width:250px;height:200px;margin-right:30px;background-color:#FAFAFA;box-sizing:border-box;display:flex;justify-content:center;align-items:center;flex-direction:column;justify-content:space-between}@media only screen and (min-width:768px){.testimonials__container--item{width:300px;height:240px}}@media only screen and (min-width:992px){.testimonials__container--item{margin:70px 0 0}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){.testimonials__container--item{width:350px;height:250px;margin-right:30px}}.testimonials__container--item--iconQuote{width:50px;position:relative;top:-15px;right:80px}@media only screen and (min-width:768px){.testimonials__container--item--iconQuote{width:60px;top:-20px;right:105px}}@media only screen and (min-width:992px){.testimonials__container--item--iconQuote{width:70px;top:-25px;right:85px}}.testimonials__container--item--calification{padding:0 20px 20px;margin-top:10px;text-align:left}@media only screen and (min-width:768px){.testimonials__container--item--calification{padding:0 30px}}.testimonials__container--item--calification--stars{width:100px;opacity:0.5;margin-bottom:10px}@media only screen and (min-width:768px){.testimonials__container--item--calification--stars{width:120px}}.testimonials__container--item--calification p{font-style:italic}.testimonials__container--item--person{width:100%;height:50px;background-color:#39B387;display:flex;justify-content:center;align-items:center;justify-content:flex-start}@media only screen and (min-width:768px){.testimonials__container--item--person{height:60px;margin-top:10px}}.testimonials__container--item--person img{width:30px;height:30px;border-radius:100%;margin:0 10px 0 20px;object-fit:cover}@media only screen and (min-width:768px){.testimonials__container--item--person img{width:40px;height:40px}}@media only screen and (min-width:992px){.testimonials__container--item--person img{margin:0 20px 0 30px}}.testimonials__container--item--person h3{color:#fff}.footer{margin-top:15vh;padding-top:15vh;text-align:center;background-color:#1A1A1A;color:#fff}@media only screen and (min-width:992px){.footer{padding:15vh 15vh;display:flex;align-items:flex-start;justify-content:space-between}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){.footer{display:block;margin-top:15vh;padding:15vh 0 0}}.footer__logo{border-bottom:1px solid #585858;padding-bottom:100px}@media only screen and (min-width:992px){.footer__logo{border:initial;padding:initial}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){.footer__logo{border-bottom:1px solid #585858;padding-bottom:100px}}.footer__logo img{width:200px;margin-bottom:10px}.footer__links{padding:40px 0;margin:0 10vw;font-size:14px;font-size:0.875rem;line-height:1.05rem}@media only screen and (min-width:992px){.footer__links{margin:0 15vw}}@media only screen and (min-width:992px){.footer__links{padding:initial;margin:0}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){.footer__links{padding:40px 0;margin:0 10vw}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px) and (min-width:992px){.footer__links{margin:0 15vw}}.footer__links--items{margin-top:20px}.footer__links--items a{display:block;color:#fff;margin-bottom:10px;transition:300ms}.footer__links--items a:hover{color:#39B387}.footer__contact{padding:40px 0}@media only screen and (min-width:992px){.footer__contact{padding:initial}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){.footer__contact{padding:40px 0}}.footer__contact--items{margin-top:20px}.footer__contact--items img{margin:0 10px;height:30px;transition:300ms}@media only screen and (min-width:992px){.footer__contact--items img:hover{height:40px}}.footer__social{padding:40px 0}@media only screen and (min-width:992px){.footer__social{padding:initial}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){.footer__social{padding:40px 0}}.footer__social--items{margin-top:20px}.footer__social--items img{margin:0 10px;height:30px;transition:300ms}@media only screen and (min-width:992px){.footer__social--items img:hover{height:40px}}.benefits{margin-top:15vh}.benefits h1{margin:0 10vw}@media only screen and (min-width:992px){.benefits h1{margin:0 15vw}}.benefits__scroll{overflow:scroll}.benefits__cardsContainer{padding:50px 0;display:inline-flex;margin:0 10vw}@media only screen and (min-width:992px){.benefits__cardsContainer{margin:0 15vw}}.benefits__cardsContainer--item{width:150px;height:200px;padding:20px;margin:0 10px;border-radius:10px;box-sizing:border-box;text-align:left;transition:300ms;display:flex;justify-content:center;align-items:center;align-items:flex-end}@media only screen and (min-width:768px){.benefits__cardsContainer--item{margin:0 20px}}.benefits__cardsContainer #benefits__cardsContainer--item1{background-image:linear-gradient(rgba(26,26,26,0.4),rgba(26,26,26,0.4)),url("https://images.unsplash.com/photo-1470010762743-1fa2363f65ca?ixid=MXwxMjA3fDB8MHxzZWFyY2h8Mzd8fHJlbGF4fGVufDB8fDB8&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60");background-position:center;background-size:cover}.benefits__cardsContainer #benefits__cardsContainer--item2{background-image:linear-gradient(rgba(26,26,26,0.4),rgba(26,26,26,0.4)),url("https://images.unsplash.com/photo-1591343395082-e120087004b4?ixid=MXwxMjA3fDB8MHxzZWFyY2h8NjR8fHJlbGF4fGVufDB8fDB8&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60");background-position:center;background-size:cover}.benefits__cardsContainer #benefits__cardsContainer--item3{background-image:linear-gradient(rgba(26,26,26,0.4),rgba(26,26,26,0.4)),url("https://images.unsplash.com/photo-1558554142-0b016c857381?ixid=MXwxMjA3fDB8MHxzZWFyY2h8MTF8fGhlYWx0aHxlbnwwfHwwfA%3D%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60");background-position:center;background-size:cover}.benefits__cardsContainer #benefits__cardsContainer--item4{background-image:linear-gradient(rgba(26,26,26,0.4),rgba(26,26,26,0.4)),url("https://images.unsplash.com/photo-1582944682702-8d357814f820?ixid=MXwxMjA3fDB8MHxzZWFyY2h8M3x8c2V4fGVufDB8fDB8&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60");background-position:center;background-size:cover}.benefits__cardsContainer #benefits__cardsContainer--item5{background-image:linear-gradient(rgba(26,26,26,0.4),rgba(26,26,26,0.4)),url("https://images.unsplash.com/photo-1466193341027-56e68017ee2d?ixid=MXwxMjA3fDB8MHxzZWFyY2h8N3x8aGFwcHl8ZW58MHx8MHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60");background-position:center;background-size:cover}body{background-color:#1A1A1A;color:#fff;text-align:center}h1,h2{color:#fff}header{background-color:#1A1A1A}@media only screen and (min-width:992px){main{display:inline-flex;align-items:center;justify-content:space-between}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){main{display:initial}}main .main__image{background-image:linear-gradient(180deg,#1a1a1a 0%,rgba(26,26,26,0) 40%,rgba(26,26,26,0) 60%,#1a1a1a 100%),url("https://images.unsplash.com/photo-1611072172521-ecfb9bfd8af0?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=1052&q=80")}@media only screen and (min-width:768px){main .main__image{background-position:center center}}@media only screen and (min-width:992px){main .main__image{height:calc(100vh - 70px);width:65vw;position:relative;left:42%}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){main .main__image{height:60vh;width:initial;background-position:center 40%;position:initial}}@media only screen and (min-width:992px){main .main__text{position:relative;right:35%;bottom:50px;text-align:left}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){main .main__text{position:initial;text-align:center}}@media only screen and (min-width:992px){main .button__fullWidth,main .button__mini{position:relative;right:calc(80% + 2px);top:90px}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){main .button__fullWidth,main .button__mini{position:initial}}#special__icon{width:30px}.testimonials{padding:0;background-color:initial;margin-top:15vh;text-align:initial}.testimonials__container--item{background-color:#262626}.testimonials__container--item--iconQuote{opacity:0.6}.testimonials__container--item--calification--stars{opacity:initial}.testimonials__container--item--calification p{color:#CCC}.testimonials__container--item--person{background-color:#333}.division{margin-top:15vh;width:100%;height:20px;background-color:#262626}.footer{margin-top:0} \ No newline at end of file diff --git a/appengine/flexible/hello_world/app/static/assets/css/newPassword.css b/appengine/flexible/hello_world/app/static/assets/css/newPassword.css new file mode 100644 index 00000000000..a39838d93cd --- /dev/null +++ b/appengine/flexible/hello_world/app/static/assets/css/newPassword.css @@ -0,0 +1 @@ +::-webkit-scrollbar{display:none}*{margin:0;padding:0;text-decoration:none;-webkit-user-select:none;user-select:none}body{color:#333;font-family:"Roboto",sans-serif;text-align:center}h1{font-weight:700;font-size:28px;font-size:1.75rem;line-height:2.1rem}@media only screen and (min-width:768px){h1{font-size:40px;font-size:2.5rem;line-height:3rem}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){h1{font-size:60px;font-size:3.75rem;line-height:4.5rem;margin-bottom:50px}}h2{font-weight:500;font-size:18px;font-size:1.125rem;line-height:1.35rem}@media only screen and (min-width:768px){h2{font-size:24px;font-size:1.5rem;line-height:1.8rem}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){h2{font-size:30px;font-size:1.875rem;line-height:2.25rem}}h3{font-weight:500;font-size:16px;font-size:1rem;line-height:1.2rem}@media only screen and (min-width:768px){h3{font-size:22px;font-size:1.375rem;line-height:1.65rem}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){h3{font-size:26px;font-size:1.625rem;line-height:1.95rem}}p{font-weight:500;font-size:14px;font-size:0.875rem;line-height:1.05rem}@media only screen and (min-width:768px){p{font-size:16px;font-size:1rem;line-height:1.2rem}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){p{font-size:18px;font-size:1.125rem;line-height:1.35rem}}header{width:100vw;height:70px;background-color:#fff;position:fixed;top:0;left:0;z-index:1;opacity:0.9;display:flex;justify-content:center;align-items:center}header img{width:120px;opacity:1}@media only screen and (min-width:768px){header img{width:150px}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){header img{width:180px}}@media only screen and (min-width:768px){header{height:100px}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){header{height:120px}}.bookingContainer{width:100%;height:auto;z-index:1;bottom:0;left:0;padding:20px 10vw;border-top:2px solid #5a5a5a5a;box-sizing:border-box;background-color:#1A1A1A;position:fixed;display:flex;justify-content:center;align-items:center;justify-content:space-between;color:#fff}@media only screen and (min-width:992px){.bookingContainer{position:relative;top:30px;width:auto;padding:20px 0;background-color:transparent}}.bookingContainer__price p{display:block;color:#808080;font-size:12px;font-size:0.75rem;line-height:0.9rem}.bookingContainer__price #price{color:#fff}.bookingContainer__button{color:#fff;outline:none;border:none;font-weight:600;border-radius:10px;width:40vw;height:40px;transition:300ms;background-color:#39B387;cursor:pointer;display:flex;justify-content:center;align-items:center}@media only screen and (min-width:992px){.bookingContainer__button{width:20vw}}.bookingContainer__button:hover{background-color:#47daa4}.bookingContainer__button h2{font-size:15px;font-size:0.9375rem;line-height:1.125rem;margin:0;font-weight:600}.bookingContainer__button img{width:15px;margin-left:10px}.inputs{width:100%;height:50px;box-sizing:border-box;color:#fff;font-weight:600;border:2px solid #474747;border-radius:30px;background-color:#262626;padding:0 20px;margin-bottom:20px;outline:none;font-size:15px;font-size:0.9375rem;line-height:1.125rem}.inputs::placeholder{color:#CCC}.inputs:focus{border:2px solid #fff}.inputs:valid{border:2px solid #fff;background-image:url("/assets/icons/check.png");background-size:20px;background-position:95% center;background-repeat:no-repeat}body{background-color:#1A1A1A;margin-top:120px}.booking{margin:0 10vw}@media only screen and (min-width:992px){.booking{margin:0 15vw}}h1{margin-top:40px;color:#fff}form{margin:40px 0} \ No newline at end of file diff --git a/appengine/flexible/hello_world/app/static/assets/css/number.css b/appengine/flexible/hello_world/app/static/assets/css/number.css new file mode 100644 index 00000000000..f3b88727a35 --- /dev/null +++ b/appengine/flexible/hello_world/app/static/assets/css/number.css @@ -0,0 +1 @@ +::-webkit-scrollbar{display:none}*{margin:0;padding:0;text-decoration:none;-webkit-user-select:none;user-select:none}body{color:#333;font-family:"Roboto",sans-serif;text-align:center}h1{font-weight:700;font-size:28px;font-size:1.75rem;line-height:2.1rem}@media only screen and (min-width:768px){h1{font-size:40px;font-size:2.5rem;line-height:3rem}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){h1{font-size:60px;font-size:3.75rem;line-height:4.5rem;margin-bottom:50px}}h2{font-weight:500;font-size:18px;font-size:1.125rem;line-height:1.35rem}@media only screen and (min-width:768px){h2{font-size:24px;font-size:1.5rem;line-height:1.8rem}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){h2{font-size:30px;font-size:1.875rem;line-height:2.25rem}}h3{font-weight:500;font-size:16px;font-size:1rem;line-height:1.2rem}@media only screen and (min-width:768px){h3{font-size:22px;font-size:1.375rem;line-height:1.65rem}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){h3{font-size:26px;font-size:1.625rem;line-height:1.95rem}}p{font-weight:500;font-size:14px;font-size:0.875rem;line-height:1.05rem}@media only screen and (min-width:768px){p{font-size:16px;font-size:1rem;line-height:1.2rem}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){p{font-size:18px;font-size:1.125rem;line-height:1.35rem}}header{width:100vw;height:70px;background-color:#fff;position:fixed;top:0;left:0;z-index:1;opacity:0.9;display:flex;justify-content:center;align-items:center}header img{width:120px;opacity:1}@media only screen and (min-width:768px){header img{width:150px}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){header img{width:180px}}@media only screen and (min-width:768px){header{height:100px}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){header{height:120px}}.bar{position:fixed;left:0;top:70px;width:100vw;height:10px;background-color:#262626}.bar__progress{height:10px;background-color:#39B387}@media only screen and (min-width:768px){.bar{top:100px}}.bookingContainer{width:100%;height:auto;z-index:1;bottom:0;left:0;padding:20px 10vw;border-top:2px solid #5a5a5a5a;box-sizing:border-box;background-color:#1A1A1A;position:fixed;display:flex;justify-content:center;align-items:center;justify-content:space-between;color:#fff}@media only screen and (min-width:992px){.bookingContainer{position:relative;top:30px;width:auto;padding:20px 0;background-color:transparent}}.bookingContainer__price p{display:block;color:#808080;font-size:12px;font-size:0.75rem;line-height:0.9rem}.bookingContainer__price #price{color:#fff}.bookingContainer__button{color:#fff;outline:none;border:none;font-weight:600;border-radius:10px;width:40vw;height:40px;transition:300ms;background-color:#39B387;cursor:pointer;display:flex;justify-content:center;align-items:center}@media only screen and (min-width:992px){.bookingContainer__button{width:20vw}}.bookingContainer__button:hover{background-color:#47daa4}.bookingContainer__button h2{font-size:15px;font-size:0.9375rem;line-height:1.125rem;margin:0;font-weight:600}.bookingContainer__button img{width:15px;margin-left:10px}.inputs{width:100%;height:50px;box-sizing:border-box;color:#fff;font-weight:600;border:2px solid #474747;border-radius:30px;background-color:#262626;padding:0 20px;margin-bottom:20px;outline:none;font-size:15px;font-size:0.9375rem;line-height:1.125rem}.inputs::placeholder{color:#CCC}.inputs:focus{border:2px solid #fff}.inputs:valid{border:2px solid #fff;background-image:url("/assets/icons/check.png");background-size:20px;background-position:95% center;background-repeat:no-repeat}body{background-color:#1A1A1A;margin-top:70px}.booking{margin:0 10vw}@media only screen and (min-width:992px){.booking{margin:0 15vw}}h1{margin-top:40px;color:#fff}p{color:#CCC}form{margin:40px 0}.bar__progress{width:66%} \ No newline at end of file diff --git a/appengine/flexible/hello_world/app/static/assets/css/pay.css b/appengine/flexible/hello_world/app/static/assets/css/pay.css new file mode 100644 index 00000000000..7b73066cd35 --- /dev/null +++ b/appengine/flexible/hello_world/app/static/assets/css/pay.css @@ -0,0 +1 @@ +::-webkit-scrollbar{display:none}*{margin:0;padding:0;text-decoration:none;-webkit-user-select:none;user-select:none}body{color:#333;font-family:"Roboto",sans-serif;text-align:center}h1{font-weight:700;font-size:28px;font-size:1.75rem;line-height:2.1rem}@media only screen and (min-width:768px){h1{font-size:40px;font-size:2.5rem;line-height:3rem}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){h1{font-size:60px;font-size:3.75rem;line-height:4.5rem;margin-bottom:50px}}h2{font-weight:500;font-size:18px;font-size:1.125rem;line-height:1.35rem}@media only screen and (min-width:768px){h2{font-size:24px;font-size:1.5rem;line-height:1.8rem}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){h2{font-size:30px;font-size:1.875rem;line-height:2.25rem}}h3{font-weight:500;font-size:16px;font-size:1rem;line-height:1.2rem}@media only screen and (min-width:768px){h3{font-size:22px;font-size:1.375rem;line-height:1.65rem}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){h3{font-size:26px;font-size:1.625rem;line-height:1.95rem}}p{font-weight:500;font-size:14px;font-size:0.875rem;line-height:1.05rem}@media only screen and (min-width:768px){p{font-size:16px;font-size:1rem;line-height:1.2rem}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){p{font-size:18px;font-size:1.125rem;line-height:1.35rem}}header{width:100vw;height:70px;background-color:#fff;position:fixed;top:0;left:0;z-index:1;opacity:0.9;display:flex;justify-content:center;align-items:center}header img{width:120px;opacity:1}@media only screen and (min-width:768px){header img{width:150px}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){header img{width:180px}}@media only screen and (min-width:768px){header{height:100px}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){header{height:120px}}.bar{position:fixed;left:0;top:70px;width:100vw;height:10px;background-color:#262626}.bar__progress{height:10px;background-color:#39B387}@media only screen and (min-width:768px){.bar{top:100px}}.bookingContainer{width:100%;height:auto;z-index:1;bottom:0;left:0;padding:20px 10vw;border-top:2px solid #5a5a5a5a;box-sizing:border-box;background-color:#1A1A1A;position:fixed;display:flex;justify-content:center;align-items:center;justify-content:space-between;color:#fff}@media only screen and (min-width:992px){.bookingContainer{position:relative;top:30px;width:auto;padding:20px 0;background-color:transparent}}.bookingContainer__price p{display:block;color:#808080;font-size:12px;font-size:0.75rem;line-height:0.9rem}.bookingContainer__price #price{color:#fff}.bookingContainer__button{color:#fff;outline:none;border:none;font-weight:600;border-radius:10px;width:40vw;height:40px;transition:300ms;background-color:#39B387;cursor:pointer;display:flex;justify-content:center;align-items:center}@media only screen and (min-width:992px){.bookingContainer__button{width:20vw}}.bookingContainer__button:hover{background-color:#47daa4}.bookingContainer__button h2{font-size:15px;font-size:0.9375rem;line-height:1.125rem;margin:0;font-weight:600}.bookingContainer__button img{width:15px;margin-left:10px}.cupon__container--item__input,.form__sectionContainer--expired,.inputs{width:100%;height:50px;box-sizing:border-box;color:#fff;font-weight:600;border:2px solid #474747;border-radius:30px;background-color:#262626;padding:0 20px;margin-bottom:20px;outline:none;font-size:15px;font-size:0.9375rem;line-height:1.125rem}.cupon__container--item__input::placeholder,.form__sectionContainer--expired::placeholder,.inputs::placeholder{color:#CCC}.cupon__container--item__input:focus,.form__sectionContainer--expired:focus,.inputs:focus{border:2px solid #fff}.cupon__container--item__input:valid,.form__sectionContainer--expired:valid,.inputs:valid{border:2px solid #fff;background-image:url("/assets/icons/check.png");background-size:20px;background-position:95% center;background-repeat:no-repeat}body{background-color:#1A1A1A;padding:70px 0 100px;text-align:left}@media only screen and (min-width:768px){body{margin-top:100px;padding:0}}@media only screen and (min-width:992px){body{margin:150px 0 0}}header{background-color:#1A1A1A}.booking{margin:0 10vw}@media only screen and (min-width:992px){.booking{margin:0 15vw}}@media only screen and (min-width:992px){.booking{margin:0 30vw}}h1{margin-top:50px;color:#fff;margin-bottom:10px}@media only screen and (min-width:992px){h1{margin:0 0 20px}}p{color:#CCC;font-weight:500}form{margin:50px 0;-webkit-user-select:initial;user-select:initial}@media only screen and (min-width:992px){form{margin:30px 0 0 0;padding:40px 20px;border:5px solid #5a5a5a5a;border-radius:20px;box-sizing:border-box}}label{color:#CCC;font-weight:500;position:relative;left:20px;bottom:10px;font-size:12px;font-size:0.75rem;line-height:0.9rem}.form__sectionContainer{margin-top:20px;display:flex;justify-content:center;align-items:center;justify-content:space-between}.form__sectionContainer--expired{display:flex;justify-content:center;align-items:center;width:40vw}@media only screen and (min-width:992px){.form__sectionContainer--expired{width:15vw}}.form__sectionContainer--expired__container{display:flex;justify-content:center;align-items:center;position:relative;right:32px}.form__sectionContainer--expired input{background-color:transparent;border:none;outline:none;font-weight:600;color:white;font-size:15px;font-size:0.9375rem;line-height:1.125rem}.form__sectionContainer--expired input::placeholder{color:#CCC}.form__sectionContainer--expired--month,.form__sectionContainer--expired--year{width:40px;padding-left:20px}.form__sectionContainer--expired label{bottom:40px;left:40px}@media only screen and (min-width:992px){.form__sectionContainer--expired label{left:10px}}.form__sectionContainer--cvv label{bottom:40px;left:48px}.form__sectionContainer--cvv .cupon__container--item__input,.form__sectionContainer--cvv .form__sectionContainer--expired,.form__sectionContainer--cvv .inputs{width:30vw}@media only screen and (min-width:992px){.form__sectionContainer--cvv .cupon__container--item__input,.form__sectionContainer--cvv .form__sectionContainer--expired,.form__sectionContainer--cvv .inputs{width:8vw}}.bar__progress{width:95%}.cupon{margin-top:50px}.cupon__container{display:flex;justify-content:center;align-items:center;justify-content:space-between}.cupon__container--item__input{width:45vw}@media only screen and (min-width:992px){.cupon__container--item__input{width:20vw}}.cupon__container--item__input:valid{background-image:none}.cupon__container--item a{font-weight:700;color:#39B387;text-transform:uppercase;margin-left:26px;transition:300ms;font-size:20px;font-size:1.25rem;line-height:1.5rem}.cupon__container--item a:hover{color:gold}.cupon__container--item a:focus{color:#808080} \ No newline at end of file diff --git a/appengine/flexible/hello_world/app/static/assets/css/recoverPassword.css b/appengine/flexible/hello_world/app/static/assets/css/recoverPassword.css new file mode 100644 index 00000000000..ae2a0b0e85c --- /dev/null +++ b/appengine/flexible/hello_world/app/static/assets/css/recoverPassword.css @@ -0,0 +1 @@ +::-webkit-scrollbar{display:none}*{margin:0;padding:0;text-decoration:none;-webkit-user-select:none;user-select:none}body{color:#333;font-family:"Roboto",sans-serif;text-align:center}h1{font-weight:700;font-size:28px;font-size:1.75rem;line-height:2.1rem}@media only screen and (min-width:768px){h1{font-size:40px;font-size:2.5rem;line-height:3rem}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){h1{font-size:60px;font-size:3.75rem;line-height:4.5rem;margin-bottom:50px}}h2{font-weight:500;font-size:18px;font-size:1.125rem;line-height:1.35rem}@media only screen and (min-width:768px){h2{font-size:24px;font-size:1.5rem;line-height:1.8rem}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){h2{font-size:30px;font-size:1.875rem;line-height:2.25rem}}h3{font-weight:500;font-size:16px;font-size:1rem;line-height:1.2rem}@media only screen and (min-width:768px){h3{font-size:22px;font-size:1.375rem;line-height:1.65rem}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){h3{font-size:26px;font-size:1.625rem;line-height:1.95rem}}p{font-weight:500;font-size:14px;font-size:0.875rem;line-height:1.05rem}@media only screen and (min-width:768px){p{font-size:16px;font-size:1rem;line-height:1.2rem}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){p{font-size:18px;font-size:1.125rem;line-height:1.35rem}}header{width:100vw;height:70px;background-color:#fff;position:fixed;top:0;left:0;z-index:1;opacity:0.9;display:flex;justify-content:center;align-items:center}header img{width:120px;opacity:1}@media only screen and (min-width:768px){header img{width:150px}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){header img{width:180px}}@media only screen and (min-width:768px){header{height:100px}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){header{height:120px}}.bar{position:fixed;left:0;top:70px;width:100vw;height:10px;background-color:#262626}.bar__progress{height:10px;background-color:#39B387}@media only screen and (min-width:768px){.bar{top:100px}}.bookingContainer{width:100%;height:auto;z-index:1;bottom:0;left:0;padding:20px 10vw;border-top:2px solid #5a5a5a5a;box-sizing:border-box;background-color:#1A1A1A;position:fixed;display:flex;justify-content:center;align-items:center;justify-content:space-between;color:#fff}@media only screen and (min-width:992px){.bookingContainer{position:relative;top:30px;width:auto;padding:20px 0;background-color:transparent}}.bookingContainer__price p{display:block;color:#808080;font-size:12px;font-size:0.75rem;line-height:0.9rem}.bookingContainer__price #price{color:#fff}.bookingContainer__button{color:#fff;outline:none;border:none;font-weight:600;border-radius:10px;width:40vw;height:40px;transition:300ms;background-color:#39B387;cursor:pointer;display:flex;justify-content:center;align-items:center}@media only screen and (min-width:992px){.bookingContainer__button{width:20vw}}.bookingContainer__button:hover{background-color:#47daa4}.bookingContainer__button h2{font-size:15px;font-size:0.9375rem;line-height:1.125rem;margin:0;font-weight:600}.bookingContainer__button img{width:15px;margin-left:10px}.inputs{width:100%;height:50px;box-sizing:border-box;color:#fff;font-weight:600;border:2px solid #474747;border-radius:30px;background-color:#262626;padding:0 20px;margin-bottom:20px;outline:none;font-size:15px;font-size:0.9375rem;line-height:1.125rem}.inputs::placeholder{color:#CCC}.inputs:focus{border:2px solid #fff}.inputs:valid{border:2px solid #fff;background-image:url("/assets/icons/check.png");background-size:20px;background-position:95% center;background-repeat:no-repeat}body{background-color:#1A1A1A;padding:100px 0;text-align:left}header{background-color:#1A1A1A}.booking{margin:0 10vw}@media only screen and (min-width:992px){.booking{margin:0 15vw}}h1{margin-top:40px;color:#fff}p{color:#CCC}form{margin:40px 0}.bar__progress{width:66%}.bookingContainer{justify-content:flex-end} \ No newline at end of file diff --git a/appengine/flexible/hello_world/app/static/assets/css/register.css b/appengine/flexible/hello_world/app/static/assets/css/register.css new file mode 100644 index 00000000000..3c710c15e49 --- /dev/null +++ b/appengine/flexible/hello_world/app/static/assets/css/register.css @@ -0,0 +1 @@ +::-webkit-scrollbar{display:none}*{margin:0;padding:0;text-decoration:none;-webkit-user-select:none;user-select:none}body{color:#333;font-family:"Roboto",sans-serif;text-align:center}h1{font-weight:700;font-size:28px;font-size:1.75rem;line-height:2.1rem}@media only screen and (min-width:768px){h1{font-size:40px;font-size:2.5rem;line-height:3rem}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){h1{font-size:60px;font-size:3.75rem;line-height:4.5rem;margin-bottom:50px}}h2{font-weight:500;font-size:18px;font-size:1.125rem;line-height:1.35rem}@media only screen and (min-width:768px){h2{font-size:24px;font-size:1.5rem;line-height:1.8rem}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){h2{font-size:30px;font-size:1.875rem;line-height:2.25rem}}h3{font-weight:500;font-size:16px;font-size:1rem;line-height:1.2rem}@media only screen and (min-width:768px){h3{font-size:22px;font-size:1.375rem;line-height:1.65rem}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){h3{font-size:26px;font-size:1.625rem;line-height:1.95rem}}p{font-weight:500;font-size:14px;font-size:0.875rem;line-height:1.05rem}@media only screen and (min-width:768px){p{font-size:16px;font-size:1rem;line-height:1.2rem}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){p{font-size:18px;font-size:1.125rem;line-height:1.35rem}}header{width:100vw;height:70px;background-color:#fff;position:fixed;top:0;left:0;z-index:1;opacity:0.9;display:flex;justify-content:center;align-items:center}header img{width:120px;opacity:1}@media only screen and (min-width:768px){header img{width:150px}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){header img{width:180px}}@media only screen and (min-width:768px){header{height:100px}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){header{height:120px}}.bar{position:fixed;left:0;top:70px;width:100vw;height:10px;background-color:#262626}.bar__progress{height:10px;background-color:#39B387}@media only screen and (min-width:768px){.bar{top:100px}}.bookingContainer{width:100%;height:auto;z-index:1;bottom:0;left:0;padding:20px 10vw;border-top:2px solid #5a5a5a5a;box-sizing:border-box;background-color:#1A1A1A;position:fixed;display:flex;justify-content:center;align-items:center;justify-content:space-between;color:#fff}@media only screen and (min-width:992px){.bookingContainer{position:relative;top:30px;width:auto;padding:20px 0;background-color:transparent}}.bookingContainer__price p{display:block;color:#808080;font-size:12px;font-size:0.75rem;line-height:0.9rem}.bookingContainer__price #price{color:#fff}.bookingContainer__button{color:#fff;outline:none;border:none;font-weight:600;border-radius:10px;width:40vw;height:40px;transition:300ms;background-color:#39B387;cursor:pointer;display:flex;justify-content:center;align-items:center}@media only screen and (min-width:992px){.bookingContainer__button{width:20vw}}.bookingContainer__button:hover{background-color:#47daa4}.bookingContainer__button h2{font-size:15px;font-size:0.9375rem;line-height:1.125rem;margin:0;font-weight:600}.bookingContainer__button img{width:15px;margin-left:10px}.inputs{width:100%;height:50px;box-sizing:border-box;color:#fff;font-weight:600;border:2px solid #474747;border-radius:30px;background-color:#262626;padding:0 20px;margin-bottom:20px;outline:none;font-size:15px;font-size:0.9375rem;line-height:1.125rem}.inputs::placeholder{color:#CCC}.inputs:focus{border:2px solid #fff}.inputs:valid{border:2px solid #fff;background-image:url("/assets/icons/check.png");background-size:20px;background-position:95% center;background-repeat:no-repeat}.socialMediaAccount{text-align:center}@media only screen and (min-width:992px){.socialMediaAccount{margin-top:20px}}.socialMediaAccount p{color:#CCC}.socialMediaAccount__items{display:flex;justify-content:center;align-items:center}.socialMediaAccount__items a{width:40px;height:40px;border-radius:50px;margin:10px 10px;box-sizing:border-box;border:1px solid #fff;display:flex;justify-content:center;align-items:center}.socialMediaAccount__items a img{width:20px}.socialMediaAccount__items a #socialMediaAccount__items--fb{height:20px;width:auto}body{background-color:#1A1A1A;padding:70px 0 100px;text-align:left}@media only screen and (min-width:768px){body{margin-top:100px}}@media only screen and (min-width:992px){body{margin:60px 0 0}}header{background-color:#1A1A1A}.booking{margin:0 10vw}@media only screen and (min-width:992px){.booking{margin:0 15vw}}@media only screen and (min-width:992px){.booking{margin:0 30vw}}h1{margin-top:30px;color:#fff}form{margin:30px 0 10px}@media only screen and (min-width:992px){form{margin:30px 0 0 0;padding:40px 20px;border:5px solid #5a5a5a5a;border-radius:20px;box-sizing:border-box}}.bar__progress{width:66%}.bookingContainer__button{width:100%} \ No newline at end of file diff --git a/appengine/flexible/hello_world/app/static/assets/css/selection.css b/appengine/flexible/hello_world/app/static/assets/css/selection.css new file mode 100644 index 00000000000..119498207a3 --- /dev/null +++ b/appengine/flexible/hello_world/app/static/assets/css/selection.css @@ -0,0 +1 @@ +::-webkit-scrollbar{display:none}*{margin:0;padding:0;text-decoration:none;-webkit-user-select:none;user-select:none}body{color:#333;font-family:"Roboto",sans-serif;text-align:center}h1{font-weight:700;font-size:28px;font-size:1.75rem;line-height:2.1rem}@media only screen and (min-width:768px){h1{font-size:40px;font-size:2.5rem;line-height:3rem}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){h1{font-size:60px;font-size:3.75rem;line-height:4.5rem;margin-bottom:50px}}h2{font-weight:500;font-size:18px;font-size:1.125rem;line-height:1.35rem}@media only screen and (min-width:768px){h2{font-size:24px;font-size:1.5rem;line-height:1.8rem}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){h2{font-size:30px;font-size:1.875rem;line-height:2.25rem}}h3{font-weight:500;font-size:16px;font-size:1rem;line-height:1.2rem}@media only screen and (min-width:768px){h3{font-size:22px;font-size:1.375rem;line-height:1.65rem}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){h3{font-size:26px;font-size:1.625rem;line-height:1.95rem}}p{font-weight:500;font-size:14px;font-size:0.875rem;line-height:1.05rem}@media only screen and (min-width:768px){p{font-size:16px;font-size:1rem;line-height:1.2rem}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){p{font-size:18px;font-size:1.125rem;line-height:1.35rem}}header{width:100vw;height:70px;background-color:#fff;position:fixed;top:0;left:0;z-index:1;opacity:0.9;display:flex;justify-content:center;align-items:center}header img{width:120px;opacity:1}@media only screen and (min-width:768px){header img{width:150px}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){header img{width:180px}}@media only screen and (min-width:768px){header{height:100px}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){header{height:120px}}.footer{margin-top:15vh;padding-top:15vh;text-align:center;background-color:#1A1A1A;color:#fff}@media only screen and (min-width:992px){.footer{padding:15vh 15vh;display:flex;align-items:flex-start;justify-content:space-between}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){.footer{display:block;margin-top:15vh;padding:15vh 0 0}}.footer__logo{border-bottom:1px solid #585858;padding-bottom:100px}@media only screen and (min-width:992px){.footer__logo{border:initial;padding:initial}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){.footer__logo{border-bottom:1px solid #585858;padding-bottom:100px}}.footer__logo img{width:200px;margin-bottom:10px}.footer__links{padding:40px 0;margin:0 10vw;font-size:14px;font-size:0.875rem;line-height:1.05rem}@media only screen and (min-width:992px){.footer__links{margin:0 15vw}}@media only screen and (min-width:992px){.footer__links{padding:initial;margin:0}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){.footer__links{padding:40px 0;margin:0 10vw}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px) and (min-width:992px){.footer__links{margin:0 15vw}}.footer__links--items{margin-top:20px}.footer__links--items a{display:block;color:#fff;margin-bottom:10px;transition:300ms}.footer__links--items a:hover{color:#39B387}.footer__contact{padding:40px 0}@media only screen and (min-width:992px){.footer__contact{padding:initial}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){.footer__contact{padding:40px 0}}.footer__contact--items{margin-top:20px}.footer__contact--items img{margin:0 10px;height:30px;transition:300ms}@media only screen and (min-width:992px){.footer__contact--items img:hover{height:40px}}.footer__social{padding:40px 0}@media only screen and (min-width:992px){.footer__social{padding:initial}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){.footer__social{padding:40px 0}}.footer__social--items{margin-top:20px}.footer__social--items img{margin:0 10px;height:30px;transition:300ms}@media only screen and (min-width:992px){.footer__social--items img:hover{height:40px}}.carousel{color:#fff;height:calc(100vh - 70px);margin-top:70px}.carousel .carousel-item h1,.carousel .carousel-item p{margin:0 10vw}@media only screen and (min-width:992px){.carousel .carousel-item h1,.carousel .carousel-item p{margin:0 15vw}}.carousel .carousel-item p{font-size:15px;font-size:0.9375rem;line-height:1.125rem}.carousel #item1{background-image:linear-gradient(rgba(26,26,26,0.5),rgba(26,26,26,0.5)),url("https://images.unsplash.com/photo-1587613864411-969e5288c708?ixid=MXwxMjA3fDB8MHxzZWFyY2h8M3x8cmVzZWFyY2h8ZW58MHwxfDB8&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60");background-position:center;background-size:cover}.carousel #item2{background-image:linear-gradient(rgba(26,26,26,0.5),rgba(26,26,26,0.5)),url("https://images.unsplash.com/photo-1552664688-cf412ec27db2?ixid=MXwxMjA3fDB8MHxzZWFyY2h8MXx8cmVzZWFyY2h8ZW58MHwxfDB8&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60");background-position:center;background-size:cover}.carousel #item3{background-image:linear-gradient(rgba(26,26,26,0.5),rgba(26,26,26,0.5)),url("https://images.unsplash.com/photo-1524758870432-af57e54afa26?ixlib=rb-1.2.1&ixid=MXwxMjA3fDB8MHxzZWFyY2h8NHx8bWVldGluZ3xlbnwwfDF8MHw%3D&auto=format&fit=crop&w=500&q=60");background-position:center;background-size:cover}.carousel #item4{background-image:linear-gradient(rgba(26,26,26,0.5),rgba(26,26,26,0.5)),url("https://images.unsplash.com/photo-1545315003-c5ad6226c272?ixid=MXwxMjA3fDB8MHxzZWFyY2h8MXx8Y29uZ3JhdHVsYXRpb25zfGVufDB8MXwwfA%3D%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60");background-position:center;background-size:cover} \ No newline at end of file diff --git a/appengine/flexible/hello_world/app/static/assets/css/userData.css b/appengine/flexible/hello_world/app/static/assets/css/userData.css new file mode 100644 index 00000000000..e59a931e1c0 --- /dev/null +++ b/appengine/flexible/hello_world/app/static/assets/css/userData.css @@ -0,0 +1 @@ +::-webkit-scrollbar{display:none}*{margin:0;padding:0;text-decoration:none;-webkit-user-select:none;user-select:none}body{color:#333;font-family:"Roboto",sans-serif;text-align:center}h1{font-weight:700;font-size:28px;font-size:1.75rem;line-height:2.1rem}@media only screen and (min-width:768px){h1{font-size:40px;font-size:2.5rem;line-height:3rem}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){h1{font-size:60px;font-size:3.75rem;line-height:4.5rem;margin-bottom:50px}}h2{font-weight:500;font-size:18px;font-size:1.125rem;line-height:1.35rem}@media only screen and (min-width:768px){h2{font-size:24px;font-size:1.5rem;line-height:1.8rem}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){h2{font-size:30px;font-size:1.875rem;line-height:2.25rem}}h3{font-weight:500;font-size:16px;font-size:1rem;line-height:1.2rem}@media only screen and (min-width:768px){h3{font-size:22px;font-size:1.375rem;line-height:1.65rem}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){h3{font-size:26px;font-size:1.625rem;line-height:1.95rem}}p{font-weight:500;font-size:14px;font-size:0.875rem;line-height:1.05rem}@media only screen and (min-width:768px){p{font-size:16px;font-size:1rem;line-height:1.2rem}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){p{font-size:18px;font-size:1.125rem;line-height:1.35rem}}header{width:100vw;height:70px;background-color:#fff;position:fixed;top:0;left:0;z-index:1;opacity:0.9;display:flex;justify-content:center;align-items:center}header img{width:120px;opacity:1}@media only screen and (min-width:768px){header img{width:150px}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){header img{width:180px}}@media only screen and (min-width:768px){header{height:100px}}@media only screen and (min-device-width:1024px) and (max-device-width:1366px){header{height:120px}}.bar{position:fixed;left:0;top:70px;width:100vw;height:10px;background-color:#262626}.bar__progress{height:10px;background-color:#39B387}@media only screen and (min-width:768px){.bar{top:100px}}.bookingContainer{width:100%;height:auto;z-index:1;bottom:0;left:0;padding:20px 10vw;border-top:2px solid #5a5a5a5a;box-sizing:border-box;background-color:#1A1A1A;position:fixed;display:flex;justify-content:center;align-items:center;justify-content:space-between;color:#fff}@media only screen and (min-width:992px){.bookingContainer{position:relative;top:30px;width:auto;padding:20px 0;background-color:transparent}}.bookingContainer__price p{display:block;color:#808080;font-size:12px;font-size:0.75rem;line-height:0.9rem}.bookingContainer__price #price{color:#fff}.bookingContainer__button{color:#fff;outline:none;border:none;font-weight:600;border-radius:10px;width:40vw;height:40px;transition:300ms;background-color:#39B387;cursor:pointer;display:flex;justify-content:center;align-items:center}@media only screen and (min-width:992px){.bookingContainer__button{width:20vw}}.bookingContainer__button:hover{background-color:#47daa4}.bookingContainer__button h2{font-size:15px;font-size:0.9375rem;line-height:1.125rem;margin:0;font-weight:600}.bookingContainer__button img{width:15px;margin-left:10px}.inputs{width:100%;height:50px;box-sizing:border-box;color:#fff;font-weight:600;border:2px solid #474747;border-radius:30px;background-color:#262626;padding:0 20px;margin-bottom:20px;outline:none;font-size:15px;font-size:0.9375rem;line-height:1.125rem}.inputs::placeholder{color:#CCC}.inputs:focus{border:2px solid #fff}.inputs:valid{border:2px solid #fff;background-image:url("/assets/icons/check.png");background-size:20px;background-position:95% center;background-repeat:no-repeat}body{background-color:#1A1A1A;padding:70px 0 100px;text-align:left}@media only screen and (min-width:768px){body{margin-top:100px}}@media only screen and (min-width:992px){body{margin:140px 0 0;padding:0}}header{background-color:#1A1A1A}.booking{margin:0 10vw}@media only screen and (min-width:992px){.booking{margin:0 15vw}}@media only screen and (min-width:992px){.booking{margin:0 30vw}}main form{-webkit-user-select:initial;user-select:initial}@media only screen and (min-width:992px){main form{margin:30px 0 0 0;padding:40px 20px;border:5px solid #5a5a5a5a;border-radius:20px;box-sizing:border-box}}main form ul input:valid{border:2px solid #fff;background-image:url("/assets/icons/check.png");background-size:20px;background-position:95% center;background-repeat:no-repeat}h1{margin-top:40px;color:#fff}.account{margin:0 10vw}@media only screen and (min-width:992px){.account{margin:0 15vw}}@media only screen and (min-width:992px){.account{margin:50px 30vw}}.account p{color:#808080;font-weight:500;margin-bottom:10px;transition:300ms}.account p a{color:#fff}.account p a:hover{text-decoration:underline}form{margin:40px 0}.bar__progress{width:66%} \ No newline at end of file diff --git a/appengine/flexible/hello_world/app/static/assets/favicon/manifest.json b/appengine/flexible/hello_world/app/static/assets/favicon/manifest.json new file mode 100644 index 00000000000..013d4a6a533 --- /dev/null +++ b/appengine/flexible/hello_world/app/static/assets/favicon/manifest.json @@ -0,0 +1,41 @@ +{ + "name": "App", + "icons": [ + { + "src": "\/android-icon-36x36.png", + "sizes": "36x36", + "type": "image\/png", + "density": "0.75" + }, + { + "src": "\/android-icon-48x48.png", + "sizes": "48x48", + "type": "image\/png", + "density": "1.0" + }, + { + "src": "\/android-icon-72x72.png", + "sizes": "72x72", + "type": "image\/png", + "density": "1.5" + }, + { + "src": "\/android-icon-96x96.png", + "sizes": "96x96", + "type": "image\/png", + "density": "2.0" + }, + { + "src": "\/android-icon-144x144.png", + "sizes": "144x144", + "type": "image\/png", + "density": "3.0" + }, + { + "src": "\/android-icon-192x192.png", + "sizes": "192x192", + "type": "image\/png", + "density": "4.0" + } + ] +} \ No newline at end of file diff --git a/appengine/flexible/hello_world/app/static/assets/icons/Icon awesome-user-check.png b/appengine/flexible/hello_world/app/static/assets/icons/Icon awesome-user-check.png new file mode 100644 index 00000000000..55fef5eebf7 Binary files /dev/null and b/appengine/flexible/hello_world/app/static/assets/icons/Icon awesome-user-check.png differ diff --git a/appengine/flexible/hello_world/app/static/assets/icons/Icon material-security.png b/appengine/flexible/hello_world/app/static/assets/icons/Icon material-security.png new file mode 100644 index 00000000000..041634e57ad Binary files /dev/null and b/appengine/flexible/hello_world/app/static/assets/icons/Icon material-security.png differ diff --git a/appengine/flexible/hello_world/app/static/assets/icons/Icon simple-whatsapp.png b/appengine/flexible/hello_world/app/static/assets/icons/Icon simple-whatsapp.png new file mode 100644 index 00000000000..60102c17ed1 Binary files /dev/null and b/appengine/flexible/hello_world/app/static/assets/icons/Icon simple-whatsapp.png differ diff --git a/appengine/flexible/hello_world/app/static/assets/icons/arrow.png b/appengine/flexible/hello_world/app/static/assets/icons/arrow.png new file mode 100644 index 00000000000..7b85f34a733 Binary files /dev/null and b/appengine/flexible/hello_world/app/static/assets/icons/arrow.png differ diff --git a/appengine/flexible/hello_world/app/static/assets/icons/back.png b/appengine/flexible/hello_world/app/static/assets/icons/back.png new file mode 100644 index 00000000000..4e1a6c321aa Binary files /dev/null and b/appengine/flexible/hello_world/app/static/assets/icons/back.png differ diff --git a/appengine/flexible/hello_world/app/static/assets/icons/calendar.png b/appengine/flexible/hello_world/app/static/assets/icons/calendar.png new file mode 100644 index 00000000000..98be1bdff99 Binary files /dev/null and b/appengine/flexible/hello_world/app/static/assets/icons/calendar.png differ diff --git a/appengine/flexible/hello_world/app/static/assets/icons/check.png b/appengine/flexible/hello_world/app/static/assets/icons/check.png new file mode 100644 index 00000000000..6f63320f0b1 Binary files /dev/null and b/appengine/flexible/hello_world/app/static/assets/icons/check.png differ diff --git a/appengine/flexible/hello_world/app/static/assets/icons/check__green.png b/appengine/flexible/hello_world/app/static/assets/icons/check__green.png new file mode 100644 index 00000000000..08c1236522a Binary files /dev/null and b/appengine/flexible/hello_world/app/static/assets/icons/check__green.png differ diff --git a/appengine/flexible/hello_world/app/static/assets/icons/email.png b/appengine/flexible/hello_world/app/static/assets/icons/email.png new file mode 100644 index 00000000000..f1d52acacf6 Binary files /dev/null and b/appengine/flexible/hello_world/app/static/assets/icons/email.png differ diff --git a/appengine/flexible/hello_world/app/static/assets/icons/email__gray.png b/appengine/flexible/hello_world/app/static/assets/icons/email__gray.png new file mode 100644 index 00000000000..c14d75ef413 Binary files /dev/null and b/appengine/flexible/hello_world/app/static/assets/icons/email__gray.png differ diff --git a/appengine/flexible/hello_world/app/static/assets/icons/facebook.png b/appengine/flexible/hello_world/app/static/assets/icons/facebook.png new file mode 100644 index 00000000000..1a562cbb7c7 Binary files /dev/null and b/appengine/flexible/hello_world/app/static/assets/icons/facebook.png differ diff --git a/appengine/flexible/hello_world/app/static/assets/icons/google.png b/appengine/flexible/hello_world/app/static/assets/icons/google.png new file mode 100644 index 00000000000..e2d4c86cf6d Binary files /dev/null and b/appengine/flexible/hello_world/app/static/assets/icons/google.png differ diff --git a/appengine/flexible/hello_world/app/static/assets/icons/hour.png b/appengine/flexible/hello_world/app/static/assets/icons/hour.png new file mode 100644 index 00000000000..0ca72654f75 Binary files /dev/null and b/appengine/flexible/hello_world/app/static/assets/icons/hour.png differ diff --git a/appengine/flexible/hello_world/app/static/assets/icons/instagram.png b/appengine/flexible/hello_world/app/static/assets/icons/instagram.png new file mode 100644 index 00000000000..cd8f98b74f0 Binary files /dev/null and b/appengine/flexible/hello_world/app/static/assets/icons/instagram.png differ diff --git a/appengine/flexible/hello_world/app/static/assets/icons/location.png b/appengine/flexible/hello_world/app/static/assets/icons/location.png new file mode 100644 index 00000000000..e9ad51cd856 Binary files /dev/null and b/appengine/flexible/hello_world/app/static/assets/icons/location.png differ diff --git a/appengine/flexible/hello_world/app/static/assets/icons/phone.png b/appengine/flexible/hello_world/app/static/assets/icons/phone.png new file mode 100644 index 00000000000..15c6c539e51 Binary files /dev/null and b/appengine/flexible/hello_world/app/static/assets/icons/phone.png differ diff --git a/appengine/flexible/hello_world/app/static/assets/icons/quote.png b/appengine/flexible/hello_world/app/static/assets/icons/quote.png new file mode 100644 index 00000000000..b580372e95e Binary files /dev/null and b/appengine/flexible/hello_world/app/static/assets/icons/quote.png differ diff --git a/appengine/flexible/hello_world/app/static/assets/icons/security.png b/appengine/flexible/hello_world/app/static/assets/icons/security.png new file mode 100644 index 00000000000..041634e57ad Binary files /dev/null and b/appengine/flexible/hello_world/app/static/assets/icons/security.png differ diff --git a/appengine/flexible/hello_world/app/static/assets/icons/stars.png b/appengine/flexible/hello_world/app/static/assets/icons/stars.png new file mode 100644 index 00000000000..20680d8c665 Binary files /dev/null and b/appengine/flexible/hello_world/app/static/assets/icons/stars.png differ diff --git a/appengine/flexible/hello_world/app/static/assets/icons/twitter.png b/appengine/flexible/hello_world/app/static/assets/icons/twitter.png new file mode 100644 index 00000000000..0fbf3a82cfe Binary files /dev/null and b/appengine/flexible/hello_world/app/static/assets/icons/twitter.png differ diff --git a/appengine/flexible/hello_world/app/static/assets/icons/user.png b/appengine/flexible/hello_world/app/static/assets/icons/user.png new file mode 100644 index 00000000000..ecc28fda674 Binary files /dev/null and b/appengine/flexible/hello_world/app/static/assets/icons/user.png differ diff --git a/appengine/flexible/hello_world/app/static/assets/icons/userCheck.png b/appengine/flexible/hello_world/app/static/assets/icons/userCheck.png new file mode 100644 index 00000000000..55fef5eebf7 Binary files /dev/null and b/appengine/flexible/hello_world/app/static/assets/icons/userCheck.png differ diff --git a/appengine/flexible/hello_world/app/static/assets/icons/whatsapp__primary.png b/appengine/flexible/hello_world/app/static/assets/icons/whatsapp__primary.png new file mode 100644 index 00000000000..eaff6d4d9c9 Binary files /dev/null and b/appengine/flexible/hello_world/app/static/assets/icons/whatsapp__primary.png differ diff --git a/appengine/flexible/hello_world/app/static/assets/icons/whatsapp__white.png b/appengine/flexible/hello_world/app/static/assets/icons/whatsapp__white.png new file mode 100644 index 00000000000..60102c17ed1 Binary files /dev/null and b/appengine/flexible/hello_world/app/static/assets/icons/whatsapp__white.png differ diff --git a/appengine/flexible/hello_world/app/static/assets/img/cupcake_recipe.png b/appengine/flexible/hello_world/app/static/assets/img/cupcake_recipe.png new file mode 100644 index 00000000000..aadf951ac71 Binary files /dev/null and b/appengine/flexible/hello_world/app/static/assets/img/cupcake_recipe.png differ diff --git a/appengine/flexible/hello_world/app/static/assets/img/platzi.png b/appengine/flexible/hello_world/app/static/assets/img/platzi.png new file mode 100644 index 00000000000..8b18d2c8b00 Binary files /dev/null and b/appengine/flexible/hello_world/app/static/assets/img/platzi.png differ diff --git a/appengine/flexible/hello_world/app/static/assets/img/qrcode.png b/appengine/flexible/hello_world/app/static/assets/img/qrcode.png new file mode 100644 index 00000000000..adc19f88165 Binary files /dev/null and b/appengine/flexible/hello_world/app/static/assets/img/qrcode.png differ diff --git a/appengine/flexible/hello_world/app/static/assets/img/recetario.png b/appengine/flexible/hello_world/app/static/assets/img/recetario.png new file mode 100644 index 00000000000..c9208915553 Binary files /dev/null and b/appengine/flexible/hello_world/app/static/assets/img/recetario.png differ diff --git a/appengine/flexible/hello_world/app/static/assets/img/servicioskuo.png b/appengine/flexible/hello_world/app/static/assets/img/servicioskuo.png new file mode 100644 index 00000000000..ecfcc1660fe Binary files /dev/null and b/appengine/flexible/hello_world/app/static/assets/img/servicioskuo.png differ diff --git a/appengine/flexible/hello_world/app/static/assets/img/servicioskuo__white.png b/appengine/flexible/hello_world/app/static/assets/img/servicioskuo__white.png new file mode 100644 index 00000000000..5280ebc7f7d Binary files /dev/null and b/appengine/flexible/hello_world/app/static/assets/img/servicioskuo__white.png differ diff --git a/appengine/flexible/hello_world/app/static/assets/img/user1.png b/appengine/flexible/hello_world/app/static/assets/img/user1.png new file mode 100644 index 00000000000..21e080e76f1 Binary files /dev/null and b/appengine/flexible/hello_world/app/static/assets/img/user1.png differ diff --git a/appengine/flexible/hello_world/app/static/assets/img/user2.png b/appengine/flexible/hello_world/app/static/assets/img/user2.png new file mode 100644 index 00000000000..719008592b8 Binary files /dev/null and b/appengine/flexible/hello_world/app/static/assets/img/user2.png differ diff --git a/appengine/flexible/hello_world/app/static/assets/img/user3.png b/appengine/flexible/hello_world/app/static/assets/img/user3.png new file mode 100644 index 00000000000..1460f95860d Binary files /dev/null and b/appengine/flexible/hello_world/app/static/assets/img/user3.png differ diff --git a/appengine/flexible/hello_world/app/static/assets/js/auth/autenticacion.js b/appengine/flexible/hello_world/app/static/assets/js/auth/autenticacion.js new file mode 100644 index 00000000000..b3048a55c45 --- /dev/null +++ b/appengine/flexible/hello_world/app/static/assets/js/auth/autenticacion.js @@ -0,0 +1,106 @@ +class Autenticacion { + autEmailPass (email, password) { + + + firebase.auth().signInWithEmailAndPassword(email, password) + + .then(result => { + console.log(result) + if (result.user.emailVerified) { + $('#avatar').attr('src', 'imagenes/usuario_auth.png') + Materialize.toast(`Bienvenido ${result.user.displayName}`, 5000) + } else { + firebase.auth().signOut() + Materialize.toast( + `Por favor realiza la verificación de la cuenta`, + 5000 + ) + } + }) + + $('.modal').modal('close') + } + + crearCuentaEmailPass (email, password, nombres) { + firebase + .auth() + .createUserWithEmailAndPassword(email, password) + .then(result => { + result.user.updateProfile({ + displayName: nombres + }) + + const configuracion = { + url: 'http://localhost/firebase-blogeek-platzi' + } + + result.user.sendEmailVerification(configuracion).catch(error => { + console.error(error) + Materialize.toast(error.message, 4000) + }) + + firebase.auth().signOut() + + Materialize.toast( + `Bienvenido ${nombres}, debes realizar el proceso de verificación`, + 4000 + ) + + $('.modal').modal('close') + }) + .catch(error => { + console.error(error) + Materialize.toast(error.message, 4000) + }) + } + + authCuentaGoogle () { + const provider = new firebase.auth.GoogleAuthProvider() + + firebase.auth().signInWithPopup(provider).then(result => { + $('#avatar').attr('src', result.user.photoURL) + $('.modal').modal('close') + Materialize.toast(`Bienvenido ${result.user.displayName} !! `, 4000) + }) + .catch(error =>{ + console.error(error) + Materialize.toast(`Error al autenticarse con google: ${error} `, 4000) + }) + } + + authCuentaFacebook () { + const provider = new firebase.auth.FacebookAuthProvider(); + + firebase.auth().signInWithPopup(provider) + + .then(result => { + $('#avatar').attr('src', result.user.photoURL) + $('.modal').modal('close') + Materialize.toast(`Bienvenido ${result.user.displayName} !! `, 4000) + }) + .catch(error =>{ + console.error(error) + Materialize.toast(`Error al autenticarse con Facebook: ${error} `, 4000) + }) + } + + authTwitter () { + var provider = new firebase.auth.TwitterAuthProvider(); + firebase.auth().signInWithPopup(provider).then(result => { + console.error(result); + var token = result.credential.accessToken; + var secret = result.credential.secret; + + $('#avatar').attr('src', result.user.photoURL); + $('.modal').modal('close'); + Materialize.toast(`Bienvenido ${result.user.displayName} !! `, 4000) + }) + .catch(error =>{ + console.error(error) + Materialize.toast(`Error al autenticarse con Twitter: ${error} `, 4000) + }) + } + + + +} \ No newline at end of file diff --git a/appengine/flexible/hello_world/app/static/assets/js/auth/autentication.js b/appengine/flexible/hello_world/app/static/assets/js/auth/autentication.js new file mode 100644 index 00000000000..f9d2c8e7c3b --- /dev/null +++ b/appengine/flexible/hello_world/app/static/assets/js/auth/autentication.js @@ -0,0 +1,110 @@ +class Autentication { + autEmailPass (email, password) { + + + firebase.auth().signInWithEmailAndPassword(email, password) + + .then(result => { + console.log(result) + if (result.user.emailVerified) { + $('#avatar').attr('src', 'imagenes/usuario_auth.png') + Materialize.toast(`Bienvenido ${result.user.displayName}`, 5000) + } else { + firebase.auth().signOut() + Materialize.toast( + `Por favor realiza la verificación de la cuenta`, + 5000 + ) + } + }) + + $('.modal').modal('close') + } + + crearCuentaEmailPass (email, password, nombres) { + firebase + .auth() + .createUserWithEmailAndPassword(email, password) + .then(result => { + result.user.updateProfile({ + displayName: nombres + }) + + const configuracion = { + url: 'http://localhost/firebase-blogeek-platzi' + } + + result.user.sendEmailVerification(configuracion).catch(error => { + console.error(error) + Materialize.toast(error.message, 4000) + }) + + firebase.auth().signOut() + + Materialize.toast( + `Bienvenido ${nombres}, debes realizar el proceso de verificación`, + 4000 + ) + + $('.modal').modal('close') + }) + .catch(error => { + console.error(error) + Materialize.toast(error.message, 4000) + }) + } + + authCuentaGoogle () { + console.log("authCuentaGoogle()") + + const provider = new firebase.auth.GoogleAuthProvider() + //provider.addScope('phonenumbers') + + firebase.auth().signInWithPopup(provider).then(result => { + console.log(result) + //$('#avatar').attr('src', result.user.photoURL) + //$('.modal').modal('close') + M.toast({html: `Bienvenido ${result.user.displayName} !! `, displayLength:4000}) + + }) + .catch(error =>{ + console.error(error) + M.toast({html: `Error al autenticarse con google: ${error} `, displayLength:4000}) + }) + } + + authCuentaFacebook () { + const provider = new firebase.auth.FacebookAuthProvider(); + + firebase.auth().signInWithPopup(provider) + + .then(result => { + // $('#avatar').attr('src', result.user.photoURL) + // $('.modal').modal('close') + M.toast({html: `Bienvenido ${result.user.displayName} !! `, displayLength:4000}) + }) + .catch(error =>{ + console.error(error) + M.toast({html: `Error al autenticarse con Facebook: ${error} `, displayLength:4000}) + }) + } + + authTwitter () { + var provider = new firebase.auth.TwitterAuthProvider(); + firebase.auth().signInWithPopup(provider).then(result => { + console.error(result); + var token = result.credential.accessToken; + var secret = result.credential.secret; + // $('#avatar').attr('src', result.user.photoURL); + $('.modal').modal('close'); + M.toast({html: `Bienvenido ${result.user.displayName} !! `, displayLength:4000}) + }) + .catch(error =>{ + console.error(error) + M.toast({html: `Error al autenticarse con Twitter: ${error} `, displayLength:4000}) + }) + } + + + + } \ No newline at end of file diff --git a/appengine/flexible/hello_world/app/static/assets/js/auth/authController.js b/appengine/flexible/hello_world/app/static/assets/js/auth/authController.js new file mode 100644 index 00000000000..023f1f05add --- /dev/null +++ b/appengine/flexible/hello_world/app/static/assets/js/auth/authController.js @@ -0,0 +1,34 @@ +$(() => { + + const objAuth = new Autentication() + + $("#btnRegistroEmail").click(() => { + const nombres = $('#nombreContactoReg').val(); + const email = $('#emailContactoReg').val(); + const password = $('#passwordReg').val(); + const auth = new Autentication() + auth.crearCuentaEmailPass(email, password, nombres) + }); + + $("#btnInicioEmail").click(() => { + const email = $('#emailSesion').val(); + const password = $('#passwordSesion').val(); + const auth = new Autentication() + auth.autEmailPass(email, password) + }); + + $("#authGoogle").click(() => objAuth.authCuentaGoogle()); + $("#authFB").click(() => objAuth.authCuentaFacebook() ); + $("#authTwitter").click(() => objAuth.authCuentaTwitter() ); + + $('#btnRegistrarse').click(() => { + $('#modalSesion').modal('close'); + $('#modalRegistro').modal('open'); + }); + + $('#btnIniciarSesion').click(() => { + $('#modalRegistro').modal('close'); + $('#modalSesion').modal('open'); + }); + +}); \ No newline at end of file diff --git a/appengine/flexible/hello_world/app/static/assets/js/auth/general.js b/appengine/flexible/hello_world/app/static/assets/js/auth/general.js new file mode 100644 index 00000000000..8d0e15674b4 --- /dev/null +++ b/appengine/flexible/hello_world/app/static/assets/js/auth/general.js @@ -0,0 +1,11 @@ +$(() => { + // Init Firebase + firebase.initializeApp(firebaseConfig); + //firebase.analytics(); +}); +// +{/* */} + +// +{/* */} diff --git a/appengine/flexible/hello_world/app/static/assets/js/bookingProcess.js b/appengine/flexible/hello_world/app/static/assets/js/bookingProcess.js new file mode 100644 index 00000000000..0167cf9fa02 --- /dev/null +++ b/appengine/flexible/hello_world/app/static/assets/js/bookingProcess.js @@ -0,0 +1,5 @@ +function bookingProcess(url, contenedor) { + $.get(url, {}, function (data) { + $("#" + contenedor).html(data); + }); +} \ No newline at end of file diff --git a/appengine/flexible/hello_world/app/static/assets/js/clientStripe copy.js b/appengine/flexible/hello_world/app/static/assets/js/clientStripe copy.js new file mode 100644 index 00000000000..1d52af1eebd --- /dev/null +++ b/appengine/flexible/hello_world/app/static/assets/js/clientStripe copy.js @@ -0,0 +1,118 @@ +// A reference to Stripe.js initialized with your real test publishable API key. +var stripe = Stripe("pk_test_51IcKJFHn1TLFiHT2yyAyH4Mw5ICQ7YduTCu4nBvQyqfkYt4dmC55n3Ns3RJVD1ZhhRmjwmmnjrNvZKFgRSIEGWdF00hBIKrvSw"); + +// The items the customer wants to buy +var purchase = { + items: [{ id: "xl-tshirt" }] +}; + +// Disable the button until we have Stripe set up on the page +document.querySelector("button").disabled = true; +fetch("/create-payment-intent", { + method: "POST", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify(purchase) +}) + .then(function(result) { + return result.json(); + }) + .then(function(data) { + var elements = stripe.elements(); + + var style = { + base: { + color: "#32325d", + fontFamily: 'Arial, sans-serif', + fontSmoothing: "antialiased", + fontSize: "16px", + "::placeholder": { + color: "#32325d" + } + }, + invalid: { + fontFamily: 'Arial, sans-serif', + color: "#fa755a", + iconColor: "#fa755a" + } + }; + + var card = elements.create("card", { style: style }); + // Stripe injects an iframe into the DOM + card.mount("#card-element"); + + card.on("change", function (event) { + // Disable the Pay button if there are no card details in the Element + document.querySelector("button").disabled = event.empty; + document.querySelector("#card-error").textContent = event.error ? event.error.message : ""; + }); + + var form = document.getElementById("payment-form"); + form.addEventListener("submit", function(event) { + event.preventDefault(); + // Complete payment when the submit button is clicked + payWithCard(stripe, card, data.clientSecret); + }); + }); + +// Calls stripe.confirmCardPayment +// If the card requires authentication Stripe shows a pop-up modal to +// prompt the user to enter authentication details without leaving your page. +var payWithCard = function(stripe, card, clientSecret) { + loading(true); + stripe + .confirmCardPayment(clientSecret, { + payment_method: { + card: card + } + }) + .then(function(result) { + if (result.error) { + // Show error to your customer + showError(result.error.message); + } else { + // The payment succeeded! + orderComplete(result.paymentIntent.id); + } + }); +}; + +/* ------- UI helpers ------- */ + +// Shows a success message when the payment is complete +var orderComplete = function(paymentIntentId) { + loading(false); + document + .querySelector(".result-message a") + .setAttribute( + "href", + "https://dashboard.stripe.com/test/payments/" + paymentIntentId + ); + document.querySelector(".result-message").classList.remove("hidden"); + document.querySelector("button").disabled = true; +}; + +// Show the customer the error from Stripe if their card fails to charge +var showError = function(errorMsgText) { + loading(false); + var errorMsg = document.querySelector("#card-error"); + errorMsg.textContent = errorMsgText; + setTimeout(function() { + errorMsg.textContent = ""; + }, 4000); +}; + +// Show a spinner on payment submission +var loading = function(isLoading) { + if (isLoading) { + // Disable the button and show a spinner + document.querySelector("button").disabled = true; + document.querySelector("#spinner").classList.remove("hidden"); + document.querySelector("#button-text").classList.add("hidden"); + } else { + document.querySelector("button").disabled = false; + document.querySelector("#spinner").classList.add("hidden"); + document.querySelector("#button-text").classList.remove("hidden"); + } +}; diff --git a/appengine/flexible/hello_world/app/static/assets/js/clientStripe.js b/appengine/flexible/hello_world/app/static/assets/js/clientStripe.js new file mode 100644 index 00000000000..f54c8e6262b --- /dev/null +++ b/appengine/flexible/hello_world/app/static/assets/js/clientStripe.js @@ -0,0 +1,124 @@ +// A reference to Stripe.js initialized with your real test publishable API key. +var stripe = Stripe("pk_test_51IcKJFHn1TLFiHT2yyAyH4Mw5ICQ7YduTCu4nBvQyqfkYt4dmC55n3Ns3RJVD1ZhhRmjwmmnjrNvZKFgRSIEGWdF00hBIKrvSw"); + +// The items the customer wants to buy +var purchase = { + items: [{ id: "xl-tshirt" }] +}; + +// Disable the button until we have Stripe set up on the page +//document.querySelector(".bookingContainer__button").disabled = true; +document.querySelector("button").disabled = true; +fetch("/create-payment-intent", { + method: "POST", + mode: "no-cors", + headers: {"Content-Type": "application/json"}, + body: JSON.stringify(purchase) +}) +// .then(function(result) { +// return result.json(); +// }) +.then(function(data) { + var elements = stripe.elements(); + var style = { + base: { + color: "#fff", + fontFamily: 'Arial, sans-serif', + fontSmoothing: "antialiased", + fontSize: "20px", + "::placeholder": { + color: "#fff" + }, + "::selection":{ + "background-color": "yellow", + } + }, + invalid: { + fontFamily: 'Arial, sans-serif', + color: "#fa755a", + iconColor: "#fa755a" + } + }; + + var card = elements.create("card", { style: style }); + // Stripe injects an iframe into the DOM + card.mount("#card-element"); + + card.on("change", function (event) { + // Disable the Pay button if there are no card details in the Element + // document.querySelector(".booking__button").disabled = event.empty; + document.querySelector("button").disabled = event.empty; + document.querySelector("#card-error").textContent = event.error ? event.error.message : ""; + }); + + var form = document.getElementById("payment-form"); + form.addEventListener("submit", function(event) { + event.preventDefault(); + // Complete payment when the submit button is clicked + payWithCard(stripe, card, data.clientSecret); + }); +}); + +// Calls stripe.confirmCardPayment +// If the card requires authentication Stripe shows a pop-up modal to +// prompt the user to enter authentication details without leaving your page. +var payWithCard = function(stripe, card, clientSecret) { +loading(true); +stripe +.confirmCardPayment(clientSecret, { +payment_method: { +card: card +} +}) +.then(function(result) { +if (result.error) { +// Show error to your customer +showError(result.error.message); +} else { +// The payment succeeded! +orderComplete(result.paymentIntent.id); +} +}); +}; + +/* ------- UI helpers ------- */ + +// Shows a success message when the payment is complete +var orderComplete = function(paymentIntentId) { +loading(false); +document +.querySelector(".result-message a") +.setAttribute( +"href", +"https://dashboard.stripe.com/test/payments/" + paymentIntentId +); +document.querySelector(".result-message").classList.remove("hidden"); +// document.querySelector(".booking__button").disabled = true; +document.querySelector("button").disabled = true; +}; + +// Show the customer the error from Stripe if their card fails to charge +var showError = function(errorMsgText) { + loading(false); + var errorMsg = document.querySelector("#card-error"); + errorMsg.textContent = errorMsgText; + setTimeout(function() { + errorMsg.textContent = ""; + }, 4000); +}; + +// Show a spinner on payment submission +var loading = function(isLoading) { + if (isLoading) { + // Disable the button and show a spinner + // document.querySelector(".booking__button").disabled = true; + document.querySelector("button").disabled = true; + document.querySelector("#spinner").classList.remove("hidden"); + document.querySelector("#button-text").classList.add("hidden"); + } else { + // document.querySelector(".booking__button").disabled = false; + document.querySelector("button").disabled = false; + document.querySelector("#spinner").classList.add("hidden"); + document.querySelector("#button-text").classList.remove("hidden"); + } +}; diff --git a/appengine/flexible/hello_world/app/static/assets/js/datePicker.js b/appengine/flexible/hello_world/app/static/assets/js/datePicker.js new file mode 100644 index 00000000000..983c9d447fb --- /dev/null +++ b/appengine/flexible/hello_world/app/static/assets/js/datePicker.js @@ -0,0 +1,50 @@ +const Calendar = document.querySelector('.datepicker') + M.Datepicker.init(Calendar,{ + format: 'dd mmmm yyyy', + firstDay: 1, + minDate: new Date(2021,2,25), + maxDate: new Date(2021,4,31), + i18n: { + cancel: 'Cancelar', + done: 'Confirmar fecha', + months: [ + 'Enero', + 'Febrero', + 'Marzo', + 'Abril', + 'Mayo', + 'Junio', + 'Julio', + 'Agosto', + 'Septiembre', + 'Octubre', + 'Noviembre', + 'Diciembre', + ], + monthsShort: [ + 'Ene', + 'Feb', + 'Mar', + 'Abr', + 'May', + 'Jun', + 'Jul', + 'Ago', + 'Sep', + 'Oct', + 'Nov', + 'Dic' + ], + weekdaysShort: [ + 'Domingo', + 'Lunes', + 'Martes', + 'Miércoles', + 'Jueves', + 'Viernes', + 'Sábado', + ], + weekdaysAbbrev: + ['D','L','M','M','J','V','S',], + } + }); \ No newline at end of file diff --git a/appengine/flexible/hello_world/app/static/assets/js/general.js b/appengine/flexible/hello_world/app/static/assets/js/general.js new file mode 100644 index 00000000000..d3a3d6e1eea --- /dev/null +++ b/appengine/flexible/hello_world/app/static/assets/js/general.js @@ -0,0 +1,57 @@ +$(() => { + $('.tooltipped').tooltip({ delay: 50 }) + $('.modal').modal() + + // TODO: Adicionar el service worker + + // Init Firebase nuevamente + firebase.initializeApp(firebaseConfig); + + // TODO: Registrar LLave publica de messaging + + // TODO: Solicitar permisos para las notificaciones + + // TODO: Recibir las notificaciones cuando el usuario esta foreground + + // TODO: Recibir las notificaciones cuando el usuario esta background + + // TODO: Listening real time + + // TODO: Firebase observador del cambio de estado + //$('#btnInicioSesion').text('Salir') + //$('#avatar').attr('src', user.photoURL) + //$('#avatar').attr('src', 'imagenes/usuario_auth.png') + //$('#btnInicioSesion').text('Iniciar Sesion') + //$('#avatar').attr('src', 'imagenes/usuario.png') + + // TODO: Evento boton inicio sesion + $('#btnInicioSesion').click(() => { + //$('#avatar').attr('src', 'imagenes/usuario.png') + // Materialize.toast(`Error al realizar SignOut => ${error}`, 4000) + + + $('#emailSesion').val('') + $('#passwordSesion').val('') + $('#modalSesion').modal('open') + }) + + $('#avatar').click(() => { + firebase.auth().signOut() + .then(() => { + $('#avatar').attr('src', 'imagenes/usuario.png') + Materialize.toast(`SignOut correcto`, 4000) + }) + .catch(error =>{ + Materialize.toast(`Error al realizar SignOut ${error}`, 4000) + }) + }) + + $('#btnTodoPost').click(() => { + $('#tituloPost').text('Posts de la Comunidad') + }) + + $('#btnMisPost').click(() => { + //$('#tituloPost').text('Mis Posts') + //Materialize.toast(`Debes estar autenticado para ver tus posts`, 4000) + }) +}) \ No newline at end of file diff --git a/appengine/flexible/hello_world/app/static/assets/js/googleMapsPlaces.js b/appengine/flexible/hello_world/app/static/assets/js/googleMapsPlaces.js new file mode 100644 index 00000000000..9a44c2f1ee7 --- /dev/null +++ b/appengine/flexible/hello_world/app/static/assets/js/googleMapsPlaces.js @@ -0,0 +1,16 @@ +function initMap() { + console.log('initMap') + const input = document.getElementById("user__address"); + const options = { + componentRestrictions: { country: "mx" }, + // fields: ["formatted_address", "geometry", "name"], + // strictBounds: false, + // types: ["establishment"], + }; + const autocomplete = new google.maps.places.Autocomplete(input, options); + + autocomplete.addListener("place_changed", () => { + const place = autocomplete.getPlace(); + }); + +} \ No newline at end of file diff --git a/appengine/flexible/hello_world/app/static/assets/js/init-firebase.js b/appengine/flexible/hello_world/app/static/assets/js/init-firebase.js new file mode 100644 index 00000000000..095ed649d03 --- /dev/null +++ b/appengine/flexible/hello_world/app/static/assets/js/init-firebase.js @@ -0,0 +1,11 @@ +const firebaseConfig = { + apiKey: "AIzaSyA_o-A2iVVJFPOkboDeVkIVbS0pxGd-xfc", + authDomain: "pyrecetario.firebaseapp.com", + projectId: "pyrecetario", + storageBucket: "pyrecetario.appspot.com", + messagingSenderId: "798373820311", + appId: "1:798373820311:web:73720f55f4eda5207136d2" +}; + +// Initialize Firebase +firebase.initializeApp(firebaseConfig); diff --git a/appengine/flexible/hello_world/app/static/assets/js/post/post.js b/appengine/flexible/hello_world/app/static/assets/js/post/post.js new file mode 100644 index 00000000000..077444c06cf --- /dev/null +++ b/appengine/flexible/hello_world/app/static/assets/js/post/post.js @@ -0,0 +1,124 @@ +class Post { + constructor () { + // TODO inicializar firestore y settings + + } + + crearPost (uid, emailUser, titulo, descripcion, imagenLink, videoLink) { + + } + + consultarTodosPost () { + + } + + consultarPostxUsuario (emailUser) { + + } + + obtenerTemplatePostVacio () { + return `
+
+
Crea el primer Post a la comunidad
+
+
+ + + + + +
+
+ + +
+
+ Video +
+
+

Crea el primer Post a la comunidad

+
+ +
` + } + + obtenerPostTemplate ( + autor, + titulo, + descripcion, + videoLink, + imagenLink, + fecha + ) { + if (imagenLink) { + return `
+
+
${titulo}
+
+
+ + + + + +
+
+ Imagen Video +
+
+ Ver Video +
+
+

${descripcion}

+
+ +
` + } + + return `
+
+
${titulo}
+
+
+ + + + + +
+
+ + +
+
+ Video +
+
+

${descripcion}

+
+ +
` + } +} diff --git a/appengine/flexible/hello_world/app/static/assets/js/post/postController.js b/appengine/flexible/hello_world/app/static/assets/js/post/postController.js new file mode 100644 index 00000000000..77eddfbd3ca --- /dev/null +++ b/appengine/flexible/hello_world/app/static/assets/js/post/postController.js @@ -0,0 +1,59 @@ +$(() => { + $('#btnModalPost').click(() => { + $('#tituloNewPost').val('') + $('#descripcionNewPost').val('') + $('#linkVideoNewPost').val('') + $('#btnUploadFile').val('') + $('.determinate').attr('style', `width: 0%`) + sessionStorage.setItem('imgNewPost', null) + + // TODO: Validar que el usuario esta autenticado + + // Materialize.toast(`Para crear el post debes estar autenticado`, 4000) + + $('#modalPost').modal('open') + }) + + $('#btnRegistroPost').click(() => { + const post = new Post() + + // TODO: Validar que el usuario esta autenticado + + // Materialize.toast(`Para crear el post debes estar autenticado`, 4000) + + const titulo = $('#tituloNewPost').val() + const descripcion = $('#descripcionNewPost').val() + const videoLink = $('#linkVideoNewPost').val() + const imagenLink = sessionStorage.getItem('imgNewPost') == 'null' + ? null + : sessionStorage.getItem('imgNewPost') + + post + .crearPost( + user.uid, + user.email, + titulo, + descripcion, + imagenLink, + videoLink + ) + .then(resp => { + Materialize.toast(`Post creado correctamente`, 4000) + $('.modal').modal('close') + }) + .catch(err => { + Materialize.toast(`Error => ${err}`, 4000) + }) + }) + + $('#btnUploadFile').on('change', e => { + // TODO: Validar que el usuario esta autenticado + + // Materialize.toast(`Para crear el post debes estar autenticado`, 4000) + + const file = e.target.files[0] + + // TODO: Referencia al storage + + }) +}) diff --git a/appengine/flexible/hello_world/app/static/assets/js/timePicker.js b/appengine/flexible/hello_world/app/static/assets/js/timePicker.js new file mode 100644 index 00000000000..d2a2ab7c8f9 --- /dev/null +++ b/appengine/flexible/hello_world/app/static/assets/js/timePicker.js @@ -0,0 +1,7 @@ +const Hour = document.querySelector('.timepicker') + M.Timepicker.init(Hour, { + i18n: { + cancel: 'Cancelar', + done: 'Confirmar', + } + }); \ No newline at end of file diff --git a/appengine/flexible/hello_world/app/static/assets/js/util/util.js b/appengine/flexible/hello_world/app/static/assets/js/util/util.js new file mode 100644 index 00000000000..7e756f19cf7 --- /dev/null +++ b/appengine/flexible/hello_world/app/static/assets/js/util/util.js @@ -0,0 +1,13 @@ +class Utilidad { + static obtenerFecha (timeStamp) { + const d = new Date(timeStamp) + let month = '' + (d.getMonth() + 1) + let day = '' + d.getDate() + let year = d.getFullYear() + + if (month.length < 2) month = '0' + month + if (day.length < 2) day = '0' + day + + return [day, month, year].join('/') + } +} diff --git a/appengine/flexible/hello_world/app/static/assets/video/rolling_pin.mov b/appengine/flexible/hello_world/app/static/assets/video/rolling_pin.mov new file mode 100644 index 00000000000..6c17ce908ce Binary files /dev/null and b/appengine/flexible/hello_world/app/static/assets/video/rolling_pin.mov differ diff --git a/appengine/flexible/hello_world/app/templates/404.html b/appengine/flexible/hello_world/app/templates/404.html new file mode 100644 index 00000000000..a08cfba2b5e --- /dev/null +++ b/appengine/flexible/hello_world/app/templates/404.html @@ -0,0 +1,90 @@ +{% extends 'base.html' %} + +{% block head %} + {{super() }} + +{% endblock %} +{% block title %} + {{ super() }} 404 +{% endblock %} + +{% block content %} +
+
+
+

Pronto podrás reservar servicios.

+

Por ahora solo hemos hecho pruebas piloto en tu localidad.

+
+
+

Ingresa tu correo para avisarte y recibir descuentos exclusivos

+
+
  • + +
  • +
  • + +
  • +
  • + +
  • +
    +
    + +{% endblock %} \ No newline at end of file diff --git a/appengine/flexible/hello_world/app/templates/base.html b/appengine/flexible/hello_world/app/templates/base.html new file mode 100644 index 00000000000..913f0930672 --- /dev/null +++ b/appengine/flexible/hello_world/app/templates/base.html @@ -0,0 +1,55 @@ +{% extends 'bootstrap/base.html' %} +{% import "bootstrap/utils.html" as utils %} + +{% block head %} + {{ super() }} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Kuo: Servicios en la comodidad de tu hogar. + +{% endblock %} + +{% block body %} + {% block navbar %} + {% include 'navbar.html' %} + {% endblock %} + + {{ utils.flashed_messages() }} + + {% block content %}{% endblock %} + + {% block scripts %} + {{ super() }} + {% endblock %} +{% endblock %} + + \ No newline at end of file diff --git a/appengine/flexible/hello_world/app/templates/celular.html b/appengine/flexible/hello_world/app/templates/celular.html new file mode 100644 index 00000000000..e67c8a5b2aa --- /dev/null +++ b/appengine/flexible/hello_world/app/templates/celular.html @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Ingresa tu número celular + + +
    + Servicios Kuo +
    +
    +
    +
    +
    +

    Por favor ingresa tu número celular

    +

    Nos permite mantener contacto contigo y lograr que tu servicio se realice satisfactoriamente.

    +
    +
      + +
    + + Agregar e ir a pagar servicio + Icono de continuar + +
    +
    + + + + + \ No newline at end of file diff --git a/appengine/flexible/hello_world/app/templates/checkout.html b/appengine/flexible/hello_world/app/templates/checkout.html new file mode 100644 index 00000000000..9661b463a89 --- /dev/null +++ b/appengine/flexible/hello_world/app/templates/checkout.html @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Confirma y paga tu servicio + + + +
    +
    + + + +
    + + diff --git a/appengine/flexible/hello_world/app/templates/confirmacion.html b/appengine/flexible/hello_world/app/templates/confirmacion.html new file mode 100644 index 00000000000..0548e4982c9 --- /dev/null +++ b/appengine/flexible/hello_world/app/templates/confirmacion.html @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Servicio Kuo confirmado + + +
    + Servicios Kuo +
    +
    + Icono de confirmación +

    Has reservado exitosamente tu masaje relajante a domicilio

    +

    Te enviamos un correo electrónico de confirmación con los siguientes pasos a seguir.

    +
    +
    +

    Tu reservación

    +
    +

    Fecha y lugar de servicio

    +
    +
    + Fecha de servicio +

    {{date}}

    +
    +
    + Fecha de servicio +

    {{hour}}

    +
    +
    + Fecha de servicio +

    {{address}}

    +
    +
    +
    +
    +

    Tus datos

    +
    +
    + Fecha de servicio +

    {{customer_name}}

    +
    +
    + Fecha de servicio +

    {{customer_phone}}

    +
    +
    + Fecha de servicio +

    {{customer_email}}

    +
    +
    +
    +
    +
    +

    ¿Tienes dudas o quieres modificar algo?

    + Email Enviar correo + WhatsApp Enviar WhatsApp +
    +
    +

    Descubre nuestros servicios

    +
    +
    +
    +

    Masaje relajante

    +

    $300 MXN - 60 min

    + información +
    +
    +

    Manicure y esmalte

    +

    Muy pronto...

    +
    +
    +

    Consulta médica

    +

    Muy pronto...

    +
    +
    +

    Atención psicológica

    +

    Muy pronto...

    +
    +
    +
    +
    + + + \ No newline at end of file diff --git a/appengine/flexible/hello_world/app/templates/datos.html b/appengine/flexible/hello_world/app/templates/datos.html new file mode 100644 index 00000000000..2f7fb090fca --- /dev/null +++ b/appengine/flexible/hello_world/app/templates/datos.html @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Ingresa tus datos + + +
    + Servicios Kuo +
    +
    +
    +
    +
    +

    ¿A nombre de quién quedará la reserva?

    +
    +
      + +
    +
      + +
    +
      + +
    +
    +
    +

    Total:

    ${{amount}} {{currency}}

    +
    + +
    +
    +
    + + + \ No newline at end of file diff --git a/appengine/flexible/hello_world/app/templates/faq.html b/appengine/flexible/hello_world/app/templates/faq.html new file mode 100644 index 00000000000..9e032fde1b4 --- /dev/null +++ b/appengine/flexible/hello_world/app/templates/faq.html @@ -0,0 +1,46 @@ +{% extends 'base.html' %} +{% import 'macros.html' as macros %} +{% import 'bootstrap/wtf.html' as wtf %} + +{% block title %} + {{ super() }} + FAQ +{% endblock %} + +{% block content %} + +
    +
    + +
    +
    + Responsive image +
    +
    +

    + Sistema de gestión gastronómico 100% online +

    +
    +
    +
    +
    +
    +
    +

    + Recetario es un sistema de gestión gastronómica 100% online que te ayuda a: +

    +

    + Almacenar recetas de manera segura en la nube, calcular costos de recetas, cantidad de ingredientes de uno o más pedidos , costo de ingredientes en un pedido, manejar historial de pedidos y más. +

    + +
    +
    +



    +
    + +
    +{% endblock %} \ No newline at end of file diff --git a/appengine/flexible/hello_world/app/templates/firebase_index.html b/appengine/flexible/hello_world/app/templates/firebase_index.html new file mode 100644 index 00000000000..d64a2bf572b --- /dev/null +++ b/appengine/flexible/hello_world/app/templates/firebase_index.html @@ -0,0 +1,89 @@ + + + + + + Welcome to Firebase Hosting + + + + + + + + + + + + + + + + + + + +
    +

    Welcome

    +

    Firebase Hosting Setup Complete

    +

    You're seeing this because you've successfully setup Firebase Hosting. Now it's time to go build something extraordinary!

    + Open Hosting Documentation +
    +

    Firebase SDK Loading…

    + + + + diff --git a/appengine/flexible/hello_world/app/templates/hello.html b/appengine/flexible/hello_world/app/templates/hello.html new file mode 100644 index 00000000000..41a612ebf80 --- /dev/null +++ b/appengine/flexible/hello_world/app/templates/hello.html @@ -0,0 +1,23 @@ +{% extends 'base.html' %} +{% import 'macros.html' as macros %} + +{% block title %} + {{ super() }} + Bienvenido +{% endblock %} + +{% block content %} + {% if user_ip %} +

    Hello World Platzi, tu IP es {{ user_ip }}

    + {% else %} + Ir a inicio + {% endif %} + +
      + {% for todo in todos %} + {{ macros.render_todo(todo) }} + {% endfor %} +
    +{% endblock %} + + diff --git a/appengine/flexible/hello_world/app/templates/index.html b/appengine/flexible/hello_world/app/templates/index.html new file mode 100644 index 00000000000..dbf655221cc --- /dev/null +++ b/appengine/flexible/hello_world/app/templates/index.html @@ -0,0 +1,167 @@ +{% extends 'base.html' %} + +{% block content %} +
    +
    +
    +

    Disfruta de tus servicios favoritos en tu hogar

    +

    Ahorra y dedica tiempo a lo más importante

    +
    + Agendar un servicio +
    +
    +

    ¿Cómo funciona?

    +
    +
    +
    +

    1

    +

    Agenda tu servicio en la plataforma

    +
    +
    +

    2

    +

    Recibe información de la KuoPartner

    +
    +
    +

    3

    +

    ¡Disfruta de tu servicio!

    +
    +
    +
    +
    +
    +

    Descubre los beneficios de reservar tus servicios a domicilio

    +
    +
    +
    +

    Tienes el poder de elegir fecha y hora

    +
    +
    +

    Olvida tener que esperar a ser atendida(o)

    +
    +
    +

    No tienes que perder tiempo transportándote

    +
    +
    +

    Ahorra tiempo y dedícalo a otras actividades

    +
    +
    +
    + Agendar un servicio +
    +
    +
    +
    + Seguridad en el servicio a domicilio +

    Seguridad

    +

    Monitoreamos el servicio en tiempo real y nuestro equipo de soporte está listo para atenderte.

    +
    +
    + Atención por WhatsApp +

    Atención directa

    +

    Habla con nosotros vía WhatsApp. Sin robots, habla con personales reales.

    +
    +
    + KuoPartners capacitados +

    KuoPartners

    +

    Nuestras servidoras pasan por un estricto proceso de selección para integrarse a nuestra plataforma.

    +
    +
    +
    +
    +

    Conoce la experiencia de nuestros usuarios

    +
    +
    +
    + Icono de testimonios +
    + Calificación de 5 estrellas +

    "Estoy encantada, la chica fue sumamente puntual y amable."

    +
    +
    + + +

    Yaneli Luna

    +
    +
    +
    + Icono de testimonios +
    + Calificación de 5 estrellas +

    "Recomiendo el masaje relajante, me ayudó a sacar ese estrés que se va acumulando."

    +
    +
    + + +

    Rodrigo Juárez

    +
    +
    +
    + Icono de testimonios +
    + Calificación de 5 estrellas +

    "Tenía duda de la seguridad pero todo salió perfecto y siempre atentos a mis dudas, gracias!."

    +
    +
    + +

    Aketzali Carbajal

    +
    +
    +
    +
    +
    +
    +

    Descubre nuestros servicios

    +
    +
    +
    +

    Masaje relajante

    +

    $360 MXN - 30 min

    + información +
    +
    +

    Manicure y esmalte

    +

    Muy pronto...

    +
    +
    +

    Consulta médica

    +

    Muy pronto...

    +
    +
    +

    Atención psicológica

    +

    Muy pronto...

    +
    +
    +
    +
    +
    + + +{% endblock %} + + \ No newline at end of file diff --git a/appengine/flexible/hello_world/app/templates/index_pull.html b/appengine/flexible/hello_world/app/templates/index_pull.html new file mode 100644 index 00000000000..e69de29bb2d diff --git a/appengine/flexible/hello_world/app/templates/ingredient.html b/appengine/flexible/hello_world/app/templates/ingredient.html new file mode 100644 index 00000000000..d1bd2e6546d --- /dev/null +++ b/appengine/flexible/hello_world/app/templates/ingredient.html @@ -0,0 +1,82 @@ +{% extends 'base.html' %} +{% import 'macros.html' as macros %} +{% import 'bootstrap/wtf.html' as wtf %} + +{% block title %} + {{super()}} ver ingrediente selecionada +{% endblock %} + +{% block content %} + +
    +
    + +
    +

    {{title}}

    +
    +
    +
    + {% if properties %} +
    + + +
    + {% for i,j in properties.items() %} +
    +
    + +
    +
    + +
    +
    + {% endfor %} + {% endif %} +
    +
    +

    + +
    + +
    +
    +
    +
    + + +{% endblock %} + + + + diff --git a/appengine/flexible/hello_world/app/templates/ingredient_create.html b/appengine/flexible/hello_world/app/templates/ingredient_create.html new file mode 100644 index 00000000000..33f72949ff6 --- /dev/null +++ b/appengine/flexible/hello_world/app/templates/ingredient_create.html @@ -0,0 +1,165 @@ +{% extends 'base.html' %} +{% import 'bootstrap/wtf.html' as wtf %} +{% import 'macros.html' as macros %} + +{% block content %} + +
    +
    +
    + {{title}} +
    +
    + +
    + +
    +
    +
    + +
    + + +
    +
    +
    +
    +
    + +
    +
    + +
    +
    +
    +
    + +
    + + +
    + + +
    + +
    +
    + +
    + +
    +
    +
    +
    + +
    + +
    +
    +
    +
    + +
    + + +
    +
    +
    +
    +
    + +
    + +
    + +
    +
    +
    +
    + +

    + +
    + +
    +
    +
    +
    + + +
    + + + + +{% endblock %} + + + + diff --git a/appengine/flexible/hello_world/app/templates/ingredient_update.html b/appengine/flexible/hello_world/app/templates/ingredient_update.html new file mode 100644 index 00000000000..1e9897a33ef --- /dev/null +++ b/appengine/flexible/hello_world/app/templates/ingredient_update.html @@ -0,0 +1,86 @@ +{% extends 'base.html' %} +{% import 'macros.html' as macros %} +{% import 'bootstrap/wtf.html' as wtf %} + +{% block title %} + {{super()}} ver ingrediente selecionada +{% endblock %} + +{% block content %} +
    +
    +
    + {{title}} +
    + +
    + +
    +
    +
    + +
    + +
    + +
    +
    +
    +
    + +
    + +
    +
    +
    +
    + +
    + +
    +
    +
    +
    + +
    + +
    +
    +
    +
    + +

    + + +
    +
    + + +
    + +
    +
    + + + +{% endblock %} + + + + diff --git a/appengine/flexible/hello_world/app/templates/ingredients_list.html b/appengine/flexible/hello_world/app/templates/ingredients_list.html new file mode 100644 index 00000000000..f9331d57bd0 --- /dev/null +++ b/appengine/flexible/hello_world/app/templates/ingredients_list.html @@ -0,0 +1,29 @@ +{% extends 'base.html' %} +{% import 'macros.html' as macros %} +{% import 'bootstrap/wtf.html' as wtf %} + +{% block title %} + {{super()}} Listar ingredientes +{% endblock %} + +{% block navbar %} + {{super()}} +{% endblock %} + +{% block content %} + {{super()}} + +
    + {% if ingredients %} +

    Listar ingredientes

    +
      + {% for r in ingredients %} + {{ macros.render_ingredients(r) }} + {% endfor %} +
    + {% else %} +

    No hay ingredientes para mostrar

    + {% endif %} +
    + +{% endblock %} \ No newline at end of file diff --git a/appengine/flexible/hello_world/app/templates/iniciarSesion.html b/appengine/flexible/hello_world/app/templates/iniciarSesion.html new file mode 100644 index 00000000000..3c32a5a9b94 --- /dev/null +++ b/appengine/flexible/hello_world/app/templates/iniciarSesion.html @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Entrar a mi cuenta + + +
    + Servicios Kuo +
    +
    +
    +
    +
    +

    ¡Qué gusto tenerte de vuelta! Ingresa tus datos para acceder

    +
    +
      + +
    +
      + +
    +
    +
    +

    Total:

    $500 MXN

    +
    + +
    +
    +
    +
    +

    ¿Olvidaste tu contraseña? recupérala aquí.

    +
    +
    +

    O inicia sesión con

    +
    + Iniciar sesión con Facebook + Iniciar sesión con Google + Iniciar sesión con Twitter +
    +
    + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/appengine/flexible/hello_world/app/templates/kuoPartner.html b/appengine/flexible/hello_world/app/templates/kuoPartner.html new file mode 100644 index 00000000000..12fd44185f9 --- /dev/null +++ b/appengine/flexible/hello_world/app/templates/kuoPartner.html @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Enviar solicitud para ser KuoPartner + \ No newline at end of file diff --git a/appengine/flexible/hello_world/app/templates/login.html b/appengine/flexible/hello_world/app/templates/login.html new file mode 100644 index 00000000000..afa84841010 --- /dev/null +++ b/appengine/flexible/hello_world/app/templates/login.html @@ -0,0 +1,33 @@ +{% extends 'base.html' %} +{% import 'macros.html' as macros %} +{% import 'bootstrap/wtf.html' as wtf %} + +{% block title %} + {{ super() }} Login +{% endblock %} + +{% block content %} +
    + +
    +{% endblock %} + + + + diff --git a/appengine/flexible/hello_world/app/templates/macros.html b/appengine/flexible/hello_world/app/templates/macros.html new file mode 100644 index 00000000000..ab72f23a03a --- /dev/null +++ b/appengine/flexible/hello_world/app/templates/macros.html @@ -0,0 +1,163 @@ +{% macro render_todo(todo) %} +
  • {{ todo.id }}
  • +{% endmacro %} + +{% macro render_dropdown(r) %} + +{% endmacro %} + +{% macro render_dropdown_products(r) %} + {% for i in r %} + + {% endfor %} +{% endmacro %} + +{% macro render_dropdown_paymentsMethods(r) %} + {% for i in r %} + + {% endfor %} +{% endmacro %} + +{% macro render_recipes(r) %} +
  • +
    +
    + + {{ r.id }} + +
    +
    +

    + {{ r.id }} + {% if r.get('product') %} (Producto Terminado) {% endif %} +

    +
    +
    +
  • +{% endmacro %} + +{% macro render_users(r) %} +
  • +
    +
    + + {{ r }} + +
    +
    +

    + {{ r }} +

    +
    +
    +
  • +{% endmacro %} + +{% macro render_orders(r) %} +
  • + +
  • +{% endmacro %} + +{% macro render_stores(r) %} +
  • + +
  • +{% endmacro %} +{% macro render_vendors(r) %} +
  • + +
  • +{% endmacro %} +{% macro render_products(r) %} +
  • +
    +
    + {{ r.id }} +
    + +
    +
  • +{% endmacro %} + +{% macro render_ingredients(r) %} +
  • +
    +
    + + {{ r.id }} + +
    + +
    +
  • +{% endmacro %} +{% macro render_products(r) %} +
  • + +
  • +{% endmacro %} + +{% macro render_ingredient_properties(r) %} +
  • {{ r }}
  • +{% endmacro %} + +{% macro render_recipe_content(k,v) %} +
  • {{ k }} : {{v}}
  • +{% endmacro %} + +{% macro render_dropdown_ingredients_selected(r,opt=False) %} + {% if r.id == opt %} + + {% else %} + + {% endif %} +{% endmacro %} + +{% macro render_dropdown_unit_selected(r,opt=False) %} + {% if r.id == opt %} + + {% else %} + + {% endif %} +{% endmacro %} \ No newline at end of file diff --git a/appengine/flexible/hello_world/app/templates/masajes.html b/appengine/flexible/hello_world/app/templates/masajes.html new file mode 100644 index 00000000000..bb7abedbc38 --- /dev/null +++ b/appengine/flexible/hello_world/app/templates/masajes.html @@ -0,0 +1,197 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Kuo: Masaje relajante a domicilio. + + +
    + Servicios Kuo +
    +
    +
    +
    +

    Descubre la experiencia de un masaje relajante en tu hogar

    +

    Libera el estrés y mejora tu vida en 30 minutos de relajación total.

    +
    + Agendar experiencia + Reservar +
    +
    +

    ¿Cómo funciona?

    +
    +
    +
    +

    1

    +

    Agenda tu servicio en la plataforma

    +
    +
    +

    2

    +

    Recibe información de la KuoPartner

    +
    +
    +

    3

    +

    ¡Disfruta de tu servicio!

    +
    +
    +
    +
    +
    +

    ¿Qué incluye?

    +
    +
    + Masaje relajante +

    Masaje relajante para liberar el estrés acumulado.

    +
    +
    + Música relajante +

    Acompaña tu experiencia con melodías relajantes

    +
    +
    + Masaje relajante +

    Disfruta de los beneficios de un aceite especial para masajes.

    +
    +
    + Agendar masaje +
    +
    +

    Conoce los beneficios de un masaje relajante

    +
    +
    +
    +

    Potente liberador de estrés.

    +
    +
    +

    Elimina dolores musculares

    +
    +
    +

    Aumenta el oxígeno en órganos y tejidos

    +
    +
    +

    Estimula el apetito sexual

    +
    +
    +

    Recupera energía causada por el cansancio

    +
    +
    +
    +
    +
    +
    +
    + Seguridad en el servicio a domicilio +

    Seguridad

    +

    Monitoreamos el servicio en tiempo real y nuestro equipo de soporte está listo para atenderte.

    +
    +
    + Atención por WhatsApp +

    Atención directa

    +

    Habla con nosotros vía WhatsApp. Sin robots, habla con personales reales.

    +
    +
    + KuoPartners capacitados +

    KuoPartners

    +

    Nuestras servidoras pasan por un estricto proceso de selección para integrarse a nuestra plataforma.

    +
    +
    + Agendar masaje +
    +
    +

    Conoce la experiencia de nuestros usuarios

    +
    +
    +
    + Icono de testimonios +
    + Calificación de 5 estrellas +

    "Estoy encantada, la chica fue sumamente puntual y amable. Sin duda voy a volver a pedir un Kuo."

    +
    +
    + +

    Yaneli Luna

    +
    +
    +
    + Icono de testimonios +
    + Calificación de 5 estrellas +

    "El masaje relajante me ayudo a sacar ese estrés que se va acumulando."

    +
    +
    + +

    Rodrigo Juárez

    +
    +
    +
    + Icono de testimonios +
    + Calificación de 5 estrellas +

    "Tenía duda de la seguridad pero todo salió perfecto y siempre atentos a mis dudas, gracias!."

    +
    +
    + +

    Aketzali Carbajal

    +
    +
    +
    +
    +
    +
    + + + \ No newline at end of file diff --git a/appengine/flexible/hello_world/app/templates/navbar.html b/appengine/flexible/hello_world/app/templates/navbar.html new file mode 100644 index 00000000000..8d9e38b4070 --- /dev/null +++ b/appengine/flexible/hello_world/app/templates/navbar.html @@ -0,0 +1,5 @@ +
    + Servicios Kuo +
    + + diff --git a/appengine/flexible/hello_world/app/templates/nuevaContrasena.html b/appengine/flexible/hello_world/app/templates/nuevaContrasena.html new file mode 100644 index 00000000000..7881043671e --- /dev/null +++ b/appengine/flexible/hello_world/app/templates/nuevaContrasena.html @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Crear nueva contraseña + + +
    + Servicios Kuo +
    +
    +

    Escribe una nueva contraseña para tu cuenta

    +
    +
      + +
    +
      + +
    + + Actualizar contraseña + Icono de continuar + +
    +
    + + \ No newline at end of file diff --git a/appengine/flexible/hello_world/app/templates/p.html b/appengine/flexible/hello_world/app/templates/p.html new file mode 100644 index 00000000000..c69ee48ae78 --- /dev/null +++ b/appengine/flexible/hello_world/app/templates/p.html @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Kuo: Masaje relajante a domicilio. + + + + +
    + Servicios Kuo +
    +
    +
    +
    +

    Pronto podrás reservar servicios.

    +

    Por ahora solo hemos hecho pruebas piloto en tu localidad.

    +
    +
    +

    Ingresa tu correo para avisarte y recibir descuentos exclusivos

    +
    +
  • + +
  • +
  • + +
  • +
  • + +
  • +
    +
    + + + \ No newline at end of file diff --git a/appengine/flexible/hello_world/app/templates/pago.html b/appengine/flexible/hello_world/app/templates/pago.html new file mode 100644 index 00000000000..9c6ccef10b8 --- /dev/null +++ b/appengine/flexible/hello_world/app/templates/pago.html @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Confirma y paga tu servicio + + +
    + Servicios Kuo +
    +
    +
    +
    +
    +

    Confirma y paga tu servicio

    +

    Tu pago está protegido y encriptado con tecnología SSL.

    + +
    +
    + + + + +
    + +
    +
    + + Aplicar +
    +
    +
    +
    +
    +

    Total:

    ${{amount}} {{currency}}

    +
    + +
    +
    +
    + + + \ No newline at end of file diff --git a/appengine/flexible/hello_world/app/templates/recipe_create.html b/appengine/flexible/hello_world/app/templates/recipe_create.html new file mode 100644 index 00000000000..6e80129738a --- /dev/null +++ b/appengine/flexible/hello_world/app/templates/recipe_create.html @@ -0,0 +1,211 @@ +{% extends 'base.html' %} +{% import 'bootstrap/wtf.html' as wtf %} +{% import 'macros.html' as macros %} + +{% block title %} + {{super()}} Registrar receta +{% endblock %} + +{% block content %} +
    + +
    +
    + Registrar Receta + + +
    +
    + +
    + + +
    + + +
    +
    +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    + + + + + + + + +
    +
    +
    +
    + + + +
    + {% if form %} + {% for i in zipped %} +
    +
    + {{ingredients__list}} +
    +
    + +
    +
    + +
    + +
    + {% endfor %} + {% else %} + + +
    +
    + +
    +
    + +
    +
    + +
    + +
    + {% endif %} + + +
    +
    +

    + +
    + + +
    +
    +
    + +
    + +
    + + + + + + + + + + +{% endblock %} + + + + diff --git a/appengine/flexible/hello_world/app/templates/recipe_estimate.html b/appengine/flexible/hello_world/app/templates/recipe_estimate.html new file mode 100644 index 00000000000..2bb9af87b4c --- /dev/null +++ b/appengine/flexible/hello_world/app/templates/recipe_estimate.html @@ -0,0 +1,83 @@ +{% extends 'base.html' %} +{% import 'bootstrap/wtf.html' as wtf %} + +{% block title %} + {{super()}} Calcular costos +{% endblock %} + +{% block content %} +
    +
    +
    Panel de visualizacion {{title}}
    + + + + + + + + + + + + + + {% for i in ingredients %} + + + + + + + + + + {% endfor %} + +
    #IngredienteCantidad necesariaCantidad de la presentacionUnidadCantidad a comprarPrecio
    1{{i.id}}{{ info[i.id].get('newQuantity') }}{{ info[i.id].get('quantity') }}{{i.get('unit')}}{{ info[i.id].get('newNeed') }}{{ info[i.id].get('newPrice') }}
    +
    +
    +
    +
    Panel de totalizacion
    + + + + + + + + + + + + + + + +
    Cantidad solicitadaPorciones totalesTotal costo
    {{ amount }}{{ servings }}{{ total }}
    +
    +
    +
    +
    + +
    + {% if admin %} +
    + +
    +
    + +
    + + + +
    +
    + {% endif %} +
    +











    +{% endblock %} + + + + diff --git a/appengine/flexible/hello_world/app/templates/recipe_update.html b/appengine/flexible/hello_world/app/templates/recipe_update.html new file mode 100644 index 00000000000..f7f225df289 --- /dev/null +++ b/appengine/flexible/hello_world/app/templates/recipe_update.html @@ -0,0 +1,225 @@ +{% extends 'base.html' %} +{% import 'bootstrap/wtf.html' as wtf %} + +{% block title %} + {{super()}} ver receta selecionada +{% endblock %} + +{% block content %} + {% if form %} +
    +
    +
    + {{title}} + + +
    +
    + +
    + +
    + + +
    +
    +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    + + + + +
    +
    + +
    + {% if ingredients %} + {% for i in ingredients %} +
    +
    + + + +
    +
    +
    + +
    +
    + +
    +
    + +
    + +
    +
    + {% endfor %} + {% endif %} +
    +
    +

    + + +
    +
    + {% if admin %} +
    +
    + + + + +
    +
    + {% endif %} +


    + + +
    + + + {% endif %} +{% endblock %} + + + + diff --git a/appengine/flexible/hello_world/app/templates/recipes_list.html b/appengine/flexible/hello_world/app/templates/recipes_list.html new file mode 100644 index 00000000000..34726a47cc6 --- /dev/null +++ b/appengine/flexible/hello_world/app/templates/recipes_list.html @@ -0,0 +1,29 @@ +{% extends 'base.html' %} +{% import 'macros.html' as macros %} +{% import 'bootstrap/wtf.html' as wtf %} + +{% block title %} + {{super()}} {{cil}} +{% endblock %} + +{% block navbar %} + {{super()}} +{% endblock %} + +{% block content %} + {{super()}} + +
    + {% if recipes %} +

    Listar recetas

    +
      + {% for r in recipes %} + {{ macros.render_recipes(r) }} + {% endfor %} +
    + {% else %} +

    No hay recetas para mostrar

    + {% endif %} +
    + +{% endblock %} \ No newline at end of file diff --git a/appengine/flexible/hello_world/app/templates/registro.html b/appengine/flexible/hello_world/app/templates/registro.html new file mode 100644 index 00000000000..f4ea3e1f5ec --- /dev/null +++ b/appengine/flexible/hello_world/app/templates/registro.html @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Crear una cuenta + + +
    + Servicios Kuo +
    +
    +
    +
    +
    +

    Crea una cuenta para reservar servicios más rápido

    +
    +
      + +
    +
      + +
    +
      + +
    +
      + +
    +
    + + +
    +
    +
    +
    +
    +

    O crea una cuenta con

    +
    + Crear cuenta con Facebook + Crear cuenta con Google + Crear cuenta con Twitter +
    +
    + + + + + + + \ No newline at end of file diff --git a/appengine/flexible/hello_world/app/templates/reserva.html b/appengine/flexible/hello_world/app/templates/reserva.html new file mode 100644 index 00000000000..23b54880c28 --- /dev/null +++ b/appengine/flexible/hello_world/app/templates/reserva.html @@ -0,0 +1,191 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Reservación de tu servicio + + +
    + Servicios Kuo +
    +
    +
    +
    +
    +

    ¿Dónde y cuándo quieres tu masaje?

    +
    +
      + +
    +
      + +
    +
      + +
    +
    +

    Personaliza tu masaje relajante

    +
      +

      Duración:

      + + + +
    +
      +

      Elige para cuántas personas:

      + + + + +
    +
    +
    +
    +

    Total:

    $500 MXN

    +
    + +
    +
    +
    + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/appengine/flexible/hello_world/app/templates/seleccion.html b/appengine/flexible/hello_world/app/templates/seleccion.html new file mode 100644 index 00000000000..8cc8005deab --- /dev/null +++ b/appengine/flexible/hello_world/app/templates/seleccion.html @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Proceso de selección de un KuoPartner + + +
    + Servicios Kuo +
    + + + + + + + + + \ No newline at end of file diff --git a/appengine/flexible/hello_world/main.py b/appengine/flexible/hello_world/main.py index 967eb1a5097..622ff147162 100644 --- a/appengine/flexible/hello_world/main.py +++ b/appengine/flexible/hello_world/main.py @@ -13,19 +13,24 @@ # limitations under the License. # [START gae_flex_quickstart] +import qrcode # allow make qrCode +import unittest +import stripe import logging from flask import Flask +from flask import request, make_response, redirect, session, url_for +from flask import render_template +from flask import jsonify +from app import create_app -app = Flask(__name__) +from app.common_functions import generarQR +from app.firestore_service import get_reservation, put_reservation, put_customer_into_reservation +app = create_app() -@app.route('/') -def hello(): - """Return a friendly HTTP greeting.""" - return 'Hello World!' - +stripe.api_key = "sk_test_51IcKJFHn1TLFiHT2Z1W5PRfz8PxzdDigtAqyFaY2zcbmUB1RQ1z4M0c1dzwvWwFIskNQBboBfU5QbkLA0qYPlC6Q00xmL9uwpJ" @app.errorhandler(500) def server_error(e): @@ -36,6 +41,226 @@ def server_error(e): """.format(e), 500 +@app.route('/') +def index(): + # user__ip = request.remote_addr + # session['user__ip'] = user__ip + # response = make_response(redirect('/auth/login')) + response = render_template('index.html') + return response + + +@app.route('/masajes') +def masajes(): + return render_template('masajes.html') + + +@app.route('/kuoPartner') +def kuoPartner(): + return render_template('kuoPartner.html') + + +@app.route('/seleccion') +def seleccion(): + return render_template('seleccion.html') + + +@app.route('/unresolved') +def unresolved(): + return render_template('p.html') + + +@app.route('/reserva', methods=['GET','POST']) +def reserva(): + if request.method == 'POST': + reservation__data = {} + formData = request.form + print(formData.to_dict()) + + reservation = put_reservation(formData) + print('reservation.id: ', reservation.id) + # return redirect(url_for('pago', reservation=reservation.id)) + # return make_response(redirect('/pago', reservation=reservation.id)) + return redirect(url_for('datos', reservation=reservation.id)) + else: + return render_template('reserva.html') + + +@app.route('/datos/', methods=['POST','GET']) +def datos(reservation): + if request.method == 'POST': + reservation__data = {} + formData = request.form.to_dict() + print(formData) + + try: + put_customer_into_reservation(reservation,formData) + return redirect(url_for('pago', reservation=reservation.id)) + except Exception as e: + print(type(e)) + print(e.args) + print(e) + + return redirect(url_for('datos', reservation=reservation.id)) + else: + reservation__bd = get_reservation(reservation) + price_id = reservation__bd.to_dict()['products'] + print('price_id: ', price_id) + + price__strip = calculate_order_amount(price_id) + print('price: ', price__strip) + + context = { + 'reservation' : reservation__bd, + 'price' : price__strip, + } + return render_template('datos.html', **context) + + +@app.route('/pago/', methods=['POST','GET']) +def pago(reservation): + print('reservation', reservation) + context = {} + + if request.method == 'POST': + print('POST') + print('request.data', request.data) + + try: + data = json.loads(request.data) + print(data) + + intent = stripe.PaymentIntent.create( + amount=calculate_order_amount(reservation), + currency='usd' + ) + + return jsonify({ + 'clientSecret': intent['client_secret'] + }) + except Exception as e: + print(type(e)) # the exception instance + print(e.args) # arguments stored in .args + print(e) # __str__ allows args to be printed directly, + # but may be overridden in exception subclasses + return jsonify(error=str(e)), 403 + + + # si todo resulta bien no deberia ir a pago sino a confirmacion.html + return render_template('pago.html') + + + else: + print('GET') + print(reservation) + + + + + + # context = { + # 'reservation': reservation__bd, + # 'amount' : price__strip['unit_amount_decimal'], + # 'currency' : price__strip['currency'], + # } + + # comprobar el estado a pagar de la reserva + # comprobar el price en stripe porque será necesario + # comprobar el precio final para poder colocarselo por javascript + + return render_template('checkout.html',**context) + + +@app.route('/confirmacion/', methods=['POST','GET']) +def confirmacion(reservation): + reservation__bd = get_reservation(reservation) + reservation__bd = reservation__bd.to_dict() + + date = reservation__bd['date'] + hour = reservation__bd['hour'] + address = reservation__bd['address'] + customer_name = reservation__bd['customer'] + customer_phone = "" ##reservation__bd['customer_phone'] + customer_email = "" ##reservation__bd['customer_email'] + context = { + 'reservation' : reservation__bd, + 'date' : date, + 'hour' : hour, + 'address' : address, + 'customer_name' : customer_name, + 'customer_phone': customer_phone, + 'customer_email': customer_email, + } + return render_template('confirmacion.html',**context) + + +@app.route('/registro') +def registro(): + context = {} + return render_template('registro.html',**context) + + +@app.route('/iniciarSesion') +def iniciarSesion(): + context = {} + return render_template('iniciarSesion.html',**context) + + +""" +it is use in create_payment() +""" +def calculate_order_amount(price_id): + # buscar API Stripe para buscar amount + try: + price = stripe.Price.retrieve(price_id) + except Exception as e: + print(e) + price = {} + # print(price) + # print(price['unit_amount_decimal']) + # print(price['currency']) + + return price + + +@app.route('/api/create-payment-intent', methods=['POST']) +def create_payment(): + """ + Procedure to checkout + http://domain.com/api/create-payment-intent + """ + try: + data = json.loads(request.data) + print(data) + + intent = stripe.PaymentIntent.create( + amount=calculate_order_amount(data['items']), + currency='usd' + ) + + return jsonify({ + 'clientSecret': intent['client_secret'] + }) + except Exception as e: + print(type(e)) # the exception instance + print(e.args) # arguments stored in .args + print(e) # __str__ allows args to be printed directly, + # but may be overridden in exception subclasses + return jsonify(error=str(e)), 403 + + +@app.route('/api/sandboxReset', methods=['GET']) +def sandboxReset(): + try: + return {'message': 'Done' },200 + except Exception as inst: + print(type(inst)) # the exception instance + print(inst.args) # arguments stored in .args + print(inst) # __str__ allows args to be printed directly, + # but may be overridden in exception subclasses + return {'message': 'Error importing or exporting data'},400 + + if __name__ == '__main__': # This is used when running locally. Gunicorn is used to run the # application on Google App Engine. See entrypoint in app.yaml. diff --git a/appengine/flexible/hello_world/mainsss.py b/appengine/flexible/hello_world/mainsss.py new file mode 100644 index 00000000000..e1a92a2425b --- /dev/null +++ b/appengine/flexible/hello_world/mainsss.py @@ -0,0 +1,37 @@ + +# +#Para TEST UNITARIOS +# +@app.cli.command() +def test(): + tests = unittest.TestLoader().discover('tests') + unittest.TextTestRunner().run(tests) + +# +#Para manejo de errores +# +@app.errorhandler(500) +def server_error(error): + context = { + 'admin' : session['admin'], + } + return render_template('400.html', error=error, **context) + + +@app.errorhandler(404) +def error(error): + context={} + if session.get('admin'): + context['admin']= session['admin'] + return render_template('404.html', error=error, **context) + + + + + +@app.route('/qrcode') +def qrcode(): + pass + + + diff --git a/appengine/flexible/hello_world/requirements.txt b/appengine/flexible/hello_world/requirements.txt index 11f7dc72170..a8929542a58 100644 --- a/appengine/flexible/hello_world/requirements.txt +++ b/appengine/flexible/hello_world/requirements.txt @@ -1,2 +1,12 @@ Flask==1.1.2 gunicorn==20.1.0 +flask-bootstrap +pillow +qrcode +flask-wtf +flask-testing +blinker +firebase-admin +flask-login +Flask-RESTful +stripe \ No newline at end of file