diff --git a/awesome_owl/__init__.py b/awesome_owl/__init__.py
new file mode 100644
index 0000000..457bae2
--- /dev/null
+++ b/awesome_owl/__init__.py
@@ -0,0 +1,3 @@
+# -*- coding: utf-8 -*-
+
+from . import controllers
\ No newline at end of file
diff --git a/awesome_owl/__manifest__.py b/awesome_owl/__manifest__.py
new file mode 100644
index 0000000..98c9595
--- /dev/null
+++ b/awesome_owl/__manifest__.py
@@ -0,0 +1,51 @@
+# -*- coding: utf-8 -*-
+{
+ 'name': "Awesome Owl",
+
+ 'summary': """
+ Starting module for "Discover the JS framework, chapter 1: Owl components"
+ """,
+
+ 'description': """
+ Starting module for "Discover the JS framework, chapter 1: Owl components"
+ """,
+
+ 'author': "Odoo",
+ 'website': "https://www.odoo.com",
+
+ # Categories can be used to filter modules in modules listing
+ # Check https://github.com/odoo/odoo/blob/15.0/odoo/addons/base/data/ir_module_category_data.xml
+ # for the full list
+ 'category': 'Tutorials/AwesomeOwl',
+ 'version': '0.1',
+
+ # any module necessary for this one to work correctly
+ 'depends': ['base', 'web'],
+ 'application': True,
+ 'installable': True,
+ 'data': [
+ 'views/templates.xml',
+ ],
+ 'assets': {
+ 'awesome_owl.assets_playground': [
+ # bootstrap
+ ('include', 'web._assets_helpers'),
+ 'web/static/src/scss/pre_variables.scss',
+ 'web/static/lib/bootstrap/scss/_variables.scss',
+ ('include', 'web._assets_bootstrap_backend'),
+
+ # required for fa icons
+ 'web/static/src/libs/fontawesome/css/font-awesome.css',
+
+ # include base files from framework
+ ('include', 'web._assets_core'),
+
+ 'web/static/src/core/utils/functions.js',
+ 'web/static/src/core/browser/browser.js',
+ 'web/static/src/core/registry.js',
+ 'web/static/src/core/assets.js',
+ 'awesome_owl/static/src/**/*',
+ ],
+ },
+ 'license': 'AGPL-3'
+}
diff --git a/awesome_owl/controllers/__init__.py b/awesome_owl/controllers/__init__.py
new file mode 100644
index 0000000..457bae2
--- /dev/null
+++ b/awesome_owl/controllers/__init__.py
@@ -0,0 +1,3 @@
+# -*- coding: utf-8 -*-
+
+from . import controllers
\ No newline at end of file
diff --git a/awesome_owl/controllers/controllers.py b/awesome_owl/controllers/controllers.py
new file mode 100644
index 0000000..bccfd6f
--- /dev/null
+++ b/awesome_owl/controllers/controllers.py
@@ -0,0 +1,10 @@
+from odoo import http
+from odoo.http import request, route
+
+class OwlPlayground(http.Controller):
+ @http.route(['/awesome_owl'], type='http', auth='public')
+ def show_playground(self):
+ """
+ Renders the owl playground page
+ """
+ return request.render('awesome_owl.playground')
diff --git a/awesome_owl/static/src/card/card.js b/awesome_owl/static/src/card/card.js
new file mode 100644
index 0000000..6265bfa
--- /dev/null
+++ b/awesome_owl/static/src/card/card.js
@@ -0,0 +1,8 @@
+/** @odoo-module **/
+
+import { Component, useState } from "@odoo/owl";
+
+export class Card extends Component {
+ static template = "awesome_owl.card";
+ static props = ['title', 'content'];
+}
\ No newline at end of file
diff --git a/awesome_owl/static/src/card/card.xml b/awesome_owl/static/src/card/card.xml
new file mode 100644
index 0000000..ce29d41
--- /dev/null
+++ b/awesome_owl/static/src/card/card.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
diff --git a/awesome_owl/static/src/counter/counter.js b/awesome_owl/static/src/counter/counter.js
new file mode 100644
index 0000000..36eb3f5
--- /dev/null
+++ b/awesome_owl/static/src/counter/counter.js
@@ -0,0 +1,21 @@
+/** @odoo-module **/
+
+import { Component, useState } from "@odoo/owl";
+
+export class Counter extends Component {
+ static template = "awesome_owl.counter";
+ static props = {
+ onChange: {type: Function, optional: true}
+ }
+
+ setup() {
+ this.state = useState({ value: 0});
+ }
+
+ increment() {
+ this.state.value++;
+ if (this.props.onChange) {
+ this.props.onChange();
+ }
+ }
+ }
diff --git a/awesome_owl/static/src/counter/counter.xml b/awesome_owl/static/src/counter/counter.xml
new file mode 100644
index 0000000..2932702
--- /dev/null
+++ b/awesome_owl/static/src/counter/counter.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+ Counter
+ Counter:
+
+
+
+
+
diff --git a/awesome_owl/static/src/main.js b/awesome_owl/static/src/main.js
new file mode 100644
index 0000000..58596c2
--- /dev/null
+++ b/awesome_owl/static/src/main.js
@@ -0,0 +1,41 @@
+/** @odoo-module **/
+
+import { browser } from "@web/core/browser/browser";
+import { mount, whenReady } from "@odoo/owl";
+import { Playground } from "./playground";
+import { templates } from "@web/core/assets";
+
+// Mount the Playground component when the document.body is ready
+whenReady( () => {
+ mount(Playground, document.body, { templates, dev: true, name: "Owl Tutorial" });
+});
+
+
+
+/**
+ * This code is iterating over the cause property of an error object to console.error a string
+ * containing the stack trace of the error and any errors that caused it.
+ * @param {Event} ev
+ */
+function logError(ev) {
+ ev.preventDefault();
+ let error = ev ?.error || ev.reason;
+
+ if (error.seen) {
+ // If an error causes the mount to crash, Owl will reject the mount promise and throw the
+ // error. Therefore, this if statement prevents the same error from appearing twice.
+ return;
+ }
+ error.seen = true;
+
+ let errorMessage = error.stack;
+ while (error.cause) {
+ errorMessage += "\nCaused by: "
+ errorMessage += error.cause.stack;
+ error = error.cause;
+ }
+ console.error(errorMessage);
+}
+
+browser.addEventListener("error", (ev) => {logError(ev)});
+browser.addEventListener("unhandledrejection", (ev) => {logError(ev)});
diff --git a/awesome_owl/static/src/playground.js b/awesome_owl/static/src/playground.js
new file mode 100644
index 0000000..d3a3717
--- /dev/null
+++ b/awesome_owl/static/src/playground.js
@@ -0,0 +1,19 @@
+/** @odoo-module **/
+
+import { Component, useState } from "@odoo/owl";
+import { Counter } from "./counter/counter";
+import { Card } from "./card/card";
+import { TodoList } from "./todo_list/todo_list";
+
+export class Playground extends Component {
+ static template = "awesome_owl.playground";
+ static components = { Counter, Card, TodoList };
+
+ setup() {
+ this.sum = useState({value: 0});
+ }
+
+ incrementSum(){
+ this.sum.value++;
+ }
+}
\ No newline at end of file
diff --git a/awesome_owl/static/src/playground.xml b/awesome_owl/static/src/playground.xml
new file mode 100644
index 0000000..5c87abb
--- /dev/null
+++ b/awesome_owl/static/src/playground.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+ The sum is
+
+
+
+
+
+
+
+
diff --git a/awesome_owl/static/src/todo_list/todo_item.js b/awesome_owl/static/src/todo_list/todo_item.js
new file mode 100644
index 0000000..1b38ddb
--- /dev/null
+++ b/awesome_owl/static/src/todo_list/todo_item.js
@@ -0,0 +1,13 @@
+/** @odoo-module **/
+
+import { Component } from "@odoo/owl";
+
+export class TodoItem extends Component {
+ static template = "awesome_owl.TodoItem";
+ static props = {
+ todo: {
+ type: Object,
+ shape: { id: Number, description: String, isCompleted: Boolean }
+ }
+ };
+}
\ No newline at end of file
diff --git a/awesome_owl/static/src/todo_list/todo_item.xml b/awesome_owl/static/src/todo_list/todo_item.xml
new file mode 100644
index 0000000..9905048
--- /dev/null
+++ b/awesome_owl/static/src/todo_list/todo_item.xml
@@ -0,0 +1,8 @@
+
+
+
+
+ .
+
+
+
\ No newline at end of file
diff --git a/awesome_owl/static/src/todo_list/todo_list.js b/awesome_owl/static/src/todo_list/todo_list.js
new file mode 100644
index 0000000..7cfcd08
--- /dev/null
+++ b/awesome_owl/static/src/todo_list/todo_list.js
@@ -0,0 +1,17 @@
+/** @odoo-module **/
+
+import { Component, useState } from "@odoo/owl";
+import { TodoItem } from "./todo_item";
+
+export class TodoList extends Component {
+ static template = "awesome_owl.todo_list";
+ static components = { TodoItem };
+
+ setup() {
+ this.todos = useState([
+ {id: 3, description: "buy milk", isCompleted: false },
+ { id: 1, description: "have a shower", isCompleted: true },
+ { id: 2, description: "sing loudly", isCompleted: false }
+ ]);
+ }
+}
\ No newline at end of file
diff --git a/awesome_owl/static/src/todo_list/todo_list.xml b/awesome_owl/static/src/todo_list/todo_list.xml
new file mode 100644
index 0000000..83537a0
--- /dev/null
+++ b/awesome_owl/static/src/todo_list/todo_list.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/awesome_owl/views/templates.xml b/awesome_owl/views/templates.xml
new file mode 100644
index 0000000..aa54c1a
--- /dev/null
+++ b/awesome_owl/views/templates.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+