Traceback (most recent call last):
  File "/home/frappe/frappe-bench/apps/frappe/frappe/utils/jinja.py", line 80, in render_template
    return get_jenv().from_string(template).render(context)
  File "/home/frappe/frappe-bench/env/lib/python3.6/site-packages/jinja2/asyncsupport.py", line 76, in render
    return original_render(self, *args, **kwargs)
  File "/home/frappe/frappe-bench/env/lib/python3.6/site-packages/jinja2/environment.py", line 1008, in render
    return self.environment.handle_exception(exc_info, True)
  File "/home/frappe/frappe-bench/env/lib/python3.6/site-packages/jinja2/environment.py", line 780, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/home/frappe/frappe-bench/env/lib/python3.6/site-packages/jinja2/_compat.py", line 37, in reraise
    raise value.with_traceback(tb)
  File "<template>", line 1, in top-level template code
  File "/home/frappe/frappe-bench/apps/frappe/frappe/./templates/web.html", line 1, in top-level template code
    {% extends base_template_path %}
  File "/home/frappe/frappe-bench/apps/frappe/frappe/./templates/base.html", line 70, in top-level template code
    {% block content %}
  File "/home/frappe/frappe-bench/apps/frappe/frappe/./templates/web.html", line 60, in block "content"
    {{ main_content() }}
  File "/home/frappe/frappe-bench/env/lib/python3.6/site-packages/jinja2/sandbox.py", line 440, in call
    return __context.call(__obj, *args, **kwargs)
  File "/home/frappe/frappe-bench/env/lib/python3.6/site-packages/jinja2/runtime.py", line 574, in _invoke
    rv = self._func(*arguments)
  File "/home/frappe/frappe-bench/apps/frappe/frappe/./templates/web.html", line 15, in template
    {% block page_container %}
  File "/home/frappe/frappe-bench/apps/frappe/frappe/./templates/web.html", line 30, in block "page_container"
    {%- block page_content -%}{%- endblock -%}
  File "<template>", line 141, in block "page_content"
jinja2.exceptions.UndefinedError: 'page_length' is undefined

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/frappe/frappe-bench/apps/frappe/frappe/website/render.py", line 50, in render
    data = render_page_by_language(path)
  File "/home/frappe/frappe-bench/apps/frappe/frappe/website/render.py", line 177, in render_page_by_language
    return render_page(path)
  File "/home/frappe/frappe-bench/apps/frappe/frappe/website/render.py", line 193, in render_page
    return build(path)
  File "/home/frappe/frappe-bench/apps/frappe/frappe/website/render.py", line 200, in build
    return build_page(path)
  File "/home/frappe/frappe-bench/apps/frappe/frappe/website/render.py", line 218, in build_page
    html = frappe.render_template(context.source, context)
  File "/home/frappe/frappe-bench/apps/frappe/frappe/utils/jinja.py", line 82, in render_template
    throw(title="Jinja Template Error", msg="<pre>{template}</pre><pre>{tb}</pre>".format(template=template, tb=get_traceback()))
  File "/home/frappe/frappe-bench/apps/frappe/frappe/__init__.py", line 377, in throw
    msgprint(msg, raise_exception=exc, title=title, indicator='red', is_minimizable=is_minimizable)
  File "/home/frappe/frappe-bench/apps/frappe/frappe/__init__.py", line 356, in msgprint
    _raise_exception()
  File "/home/frappe/frappe-bench/apps/frappe/frappe/__init__.py", line 316, in _raise_exception
    raise raise_exception(msg)
frappe.exceptions.ValidationError: <pre>{% extends "templates/web.html" %}

{% block title %}¡Bienvenidos/as!{% endblock %}
{% block header %}
<!-- <h1>¡Bienvenidos/as!</h1> -->
{% endblock header %}

{% block page_content %}
<div class="row">
	<div class="col-lg-10 col-sm-12">
		<div class="input-group input-group-sm mb-3">
			<input type="search" class="form-control" placeholder="{{_('Product Search')}}"
				aria-label="{{_('Product Search')}}" aria-describedby="product-search"
				value="{{ frappe.form_dict.search or '' }}" style="height: 45px;"
			>
		</div>
	</div>

	<div class="col-4 pl-0">
		<button class="btn btn-light btn-sm btn-block d-md-none"
			type="button"
			data-toggle="collapse"
			data-target="#product-filters"
			aria-expanded="false"
			aria-controls="product-filters"
			style="white-space: nowrap;"
		>
			{{ _('Toggle Filters') }}
		</button>
	</div>
</div>

<div class="row">
	<!-- <div class="col-12 order-2 col-lg-4 order-md-1 products-list"> -->
		{% if items %}
			{% for item in items %}
				{% include "erpnext/www/all-products/item_row.html" %}
			{% endfor %}
		{% else %}
			{% include "erpnext/www/all-products/not_found.html" %}
		{% endif %}
	<!-- </div> -->
	{#<div class="col-12 order-1 col-md-4 order-md-2">

		{% if frappe.form_dict.start or frappe.form_dict.field_filters or frappe.form_dict.attribute_filters or frappe.form_dict.search %}
		<a class="mb-3 d-inline-block" href="/all-products">{{ _('Clear filters') }}</a>
		{% endif  %}

		<div class="collapse d-md-block" id="product-filters">
			{% for field_filter in field_filters %}
				{%- set item_field =  field_filter[0] %}
				{%- set values =  field_filter[1] %}
				<div class="mb-4">
					<!-- <h6>{{ item_field.label }}</h6> -->

					{% if values | len > 20 %}
					<!-- show inline filter if values more than 20 -->
					<input type="text" class="form-control form-control-sm mb-2 product-filter-filter"/>
					{% endif %}

					{% if values %}
					<div class="filter-options">
						{% for value in values %}
						<div class="custom-control custom-checkbox" data-value="{{ value }}">
							<input type="checkbox"
								class="product-filter field-filter custom-control-input"
								id="{{value}}"
								data-filter-name="{{ item_field.fieldname }}"
								data-filter-value="{{ value }}"
							>
							<label class="custom-control-label" for="{{value}}">
								{{ value }}
							</label>
						</div>
						{% endfor %}
					</div>
					{% else %}
					<i class="text-muted">{{ _('No values') }}</i>
					{% endif %}
				</div>
			{% endfor %}

			{% for attribute in attribute_filters %}
				<div class="mb-4">
					<h6>{{ attribute.name }}</h6>

					{% if values | len > 20 %}
					<!-- show inline filter if values more than 20 -->
					<input type="text" class="form-control form-control-sm mb-2 product-filter-filter"/>
					{% endif %}

					{% if attribute.item_attribute_values %}
					<div class="filter-options">
						{% for attr_value in attribute.item_attribute_values %}
						<div class="custom-control custom-checkbox" data-value="{{ value }}">
							<input type="checkbox"
								class="product-filter attribute-filter custom-control-input"
								id="{{attr_value.name}}"
								data-attribute-name="{{ attribute.name }}"
								data-attribute-value="{{ attr_value.attribute_value }}"
								{% if attr_value.checked %} checked {% endif %}
							>
							<label class="custom-control-label" for="{{attr_value.name}}">
								{{ attr_value.attribute_value }}
							</label>
						</div>
						{% endfor %}
					</div>
					{% else %}
					<i class="text-muted">{{ _('No values') }}</i>
					{% endif %}
				</div>
			{% endfor %}
		</div>

		<script>
			frappe.ready(() => {
				$('.product-filter-filter').on('keydown', frappe.utils.debounce((e) => {
					const $input = $(e.target);
					const keyword = ($input.val() || '').toLowerCase();
					const $filter_options = $input.next('.filter-options');

					$filter_options.find('.custom-control').show();
					$filter_options.find('.custom-control').each((i, el) => {
						const $el = $(el);
						const value = $el.data('value').toLowerCase();
						if (!value.includes(keyword)) {
							$el.hide();
						}
					});
				}, 300));
			})
		</script>
	</div>#}
</div>
<div class="row">
	<div class="col-12">
		{% if frappe.form_dict.start|int > 0 %}
		<button class="btn btn-outline-secondary btn-prev" data-start="{{ frappe.form_dict.start|int - page_length }}">{{ _("Prev") }}</button>
		{% endif %}
		{% if items|length >= page_length %}
		<button class="btn btn-outline-secondary btn-next" data-start="{{ frappe.form_dict.start|int + page_length }}">{{ _("Next") }}</button>
		{% endif %}
	</div>
</div>

<script>
	frappe.ready(() => {
		$('.btn-prev, .btn-next').click((e) => {
			const $btn = $(e.target);
			$btn.prop('disabled', true);
			const start = $btn.data('start');
			let query_params = frappe.utils.get_query_params();
			query_params.start = start;
			let path = window.location.pathname + '?' + frappe.utils.get_url_from_dict(query_params);
			window.location.href = path;
		});
	});
</script>

{% endblock %}



{% block script %}<script>$(() => {
	class ProductListing {
		constructor() {
			this.bind_filters();
			this.bind_search();
			this.restore_filters_state();
		}

		bind_filters() {
			this.field_filters = {};
			this.attribute_filters = {};

			$('.product-filter').on('change', frappe.utils.debounce((e) => {
				const $checkbox = $(e.target);
				const is_checked = $checkbox.is(':checked');

				if ($checkbox.is('.attribute-filter')) {
					const {
						attributeName: attribute_name,
						attributeValue: attribute_value
					} = $checkbox.data();

					if (is_checked) {
						this.attribute_filters[attribute_name] = this.attribute_filters[attribute_name] || [];
						this.attribute_filters[attribute_name].push(attribute_value);
					} else {
						this.attribute_filters[attribute_name] = this.attribute_filters[attribute_name] || [];
						this.attribute_filters[attribute_name] = this.attribute_filters[attribute_name].filter(v => v !== attribute_value);
					}

					if (this.attribute_filters[attribute_name].length === 0) {
						delete this.attribute_filters[attribute_name];
					}
				} else if ($checkbox.is('.field-filter')) {
					const {
						filterName: filter_name,
						filterValue: filter_value
					} = $checkbox.data();

					if (is_checked) {
						this.field_filters[filter_name] = this.field_filters[filter_name] || [];
						this.field_filters[filter_name].push(filter_value);
					} else {
						this.field_filters[filter_name] = this.field_filters[filter_name] || [];
						this.field_filters[filter_name] = this.field_filters[filter_name].filter(v => v !== filter_value);
					}

					if (this.field_filters[filter_name].length === 0) {
						delete this.field_filters[filter_name];
					}
				}

				const query_string = get_query_string({
					field_filters: JSON.stringify(if_key_exists(this.field_filters)),
					attribute_filters: JSON.stringify(if_key_exists(this.attribute_filters)),
				});
				window.history.pushState('filters', '', '/all-products?' + query_string);

				$('.page_content input').prop('disabled', true);
				this.get_items_with_filters()
					.then(html => {
						$('.products-list').html(html);
					})
					.then(data => {
						$('.page_content input').prop('disabled', false);
						return data;
					})
					.catch(() => {
						$('.page_content input').prop('disabled', false);
					});
			}, 1000));
		}

		make_filters() {

		}

		bind_search() {
			$('input[type=search]').on('keydown', (e) => {
				if (e.keyCode === 13) {
					// Enter
					const value = e.target.value;
					if (value) {
						window.location.search = 'search=' + e.target.value;
					} else {
						window.location.search = '';
					}
				}
			});
		}

		restore_filters_state() {
			const filters = frappe.utils.get_query_params();
			let {field_filters, attribute_filters} = filters;

			if (field_filters) {
				field_filters = JSON.parse(field_filters);
				for (let fieldname in field_filters) {
					const values = field_filters[fieldname];
					const selector = values.map(value => {
						return `input[data-filter-name="${fieldname}"][data-filter-value="${value}"]`;
					}).join(',');
					$(selector).prop('checked', true);
				}
				this.field_filters = field_filters;
			}
			if (attribute_filters) {
				attribute_filters = JSON.parse(attribute_filters);
				for (let attribute in attribute_filters) {
					const values = attribute_filters[attribute];
					const selector = values.map(value => {
						return `input[data-attribute-name="${attribute}"][data-attribute-value="${value}"]`;
					}).join(',');
					$(selector).prop('checked', true);
				}
				this.attribute_filters = attribute_filters;
			}
		}

		get_items_with_filters() {
			const { attribute_filters, field_filters } = this;
			const args = {
				field_filters: if_key_exists(field_filters),
				attribute_filters: if_key_exists(attribute_filters)
			};

			return new Promise((resolve, reject) => {
				frappe.call('erpnext.portal.product_configurator.utils.get_products_html_for_website', args)
					.then(r => {
						if (r.exc) reject(r.exc);
						else resolve(r.message);
					})
					.fail(reject);
			});
		}
	}

	new ProductListing();

	function get_query_string(object) {
		const url = new URLSearchParams();
		for (let key in object) {
			const value = object[key];
			if (value) {
				url.append(key, value);
			}
		}
		return url.toString();
	}

	function if_key_exists(obj) {
		let exists = false;
		for (let key in obj) {
			if (obj.hasOwnProperty(key) && obj[key]) {
				exists = true;
				break;
			}
		}
		return exists ? obj : undefined;
	}
});

</script>
{% endblock %}</pre><pre>Traceback (most recent call last):
  File "/home/frappe/frappe-bench/apps/frappe/frappe/utils/jinja.py", line 80, in render_template
    return get_jenv().from_string(template).render(context)
  File "/home/frappe/frappe-bench/env/lib/python3.6/site-packages/jinja2/asyncsupport.py", line 76, in render
    return original_render(self, *args, **kwargs)
  File "/home/frappe/frappe-bench/env/lib/python3.6/site-packages/jinja2/environment.py", line 1008, in render
    return self.environment.handle_exception(exc_info, True)
  File "/home/frappe/frappe-bench/env/lib/python3.6/site-packages/jinja2/environment.py", line 780, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/home/frappe/frappe-bench/env/lib/python3.6/site-packages/jinja2/_compat.py", line 37, in reraise
    raise value.with_traceback(tb)
  File "<template>", line 1, in top-level template code
  File "/home/frappe/frappe-bench/apps/frappe/frappe/./templates/web.html", line 1, in top-level template code
    {% extends base_template_path %}
  File "/home/frappe/frappe-bench/apps/frappe/frappe/./templates/base.html", line 70, in top-level template code
    {% block content %}
  File "/home/frappe/frappe-bench/apps/frappe/frappe/./templates/web.html", line 60, in block "content"
    {{ main_content() }}
  File "/home/frappe/frappe-bench/env/lib/python3.6/site-packages/jinja2/sandbox.py", line 440, in call
    return __context.call(__obj, *args, **kwargs)
  File "/home/frappe/frappe-bench/env/lib/python3.6/site-packages/jinja2/runtime.py", line 574, in _invoke
    rv = self._func(*arguments)
  File "/home/frappe/frappe-bench/apps/frappe/frappe/./templates/web.html", line 15, in template
    {% block page_container %}
  File "/home/frappe/frappe-bench/apps/frappe/frappe/./templates/web.html", line 30, in block "page_container"
    {%- block page_content -%}{%- endblock -%}
  File "<template>", line 141, in block "page_content"
jinja2.exceptions.UndefinedError: 'page_length' is undefined
</pre>