Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
7a42783
[ADD] estate: create initial module shell for real estate management
pusu-odoo Aug 11, 2025
46dd052
[ADD] estate: define property model with essential fields and rules
pusu-odoo Aug 11, 2025
e15da5e
[ADD] estate: introduce basic access rights for property management
pusu-odoo Aug 11, 2025
5f0c9c8
[ADD] estate: first UI menus, default values, and property state mana…
pusu-odoo Aug 12, 2025
15c7949
[ADD] estate: custom list, form and search views for properties
pusu-odoo Aug 18, 2025
030c353
[ADD] estate: relational models for property types, tags, buyers and …
pusu-odoo Aug 18, 2025
94c5b92
[ADD] estate: Enhance property and offer pages with auto updates and …
pusu-odoo Aug 19, 2025
d62d928
[ADD] estate: Add property and offer action buttons with smart restri…
pusu-odoo Aug 19, 2025
e06b60d
[ADD] estate: Add data validation rules for prices and offer acceptance
pusu-odoo Aug 19, 2025
167ed52
[IMP] estate: Improve UI, ordering, filters, and constraints
pusu-odoo Aug 21, 2025
9b593fa
[ADD] estate: extend CRUD rules and user form with property management
pusu-odoo Aug 21, 2025
182a5cd
[ADD] estate_account: generate invoice on property sale
pusu-odoo Aug 22, 2025
668311e
[ADD] estate: introduce Kanban view for properties
pusu-odoo Aug 22, 2025
a7acb9e
[IMP] estate: align views and models with coding guidelines
pusu-odoo Aug 25, 2025
51bd52b
[IMP] estate: improve offer handling and property visibility rules
pusu-odoo Aug 26, 2025
1e91a59
[ADD] awesome_owl: build initial owl components with examples
pusu-odoo Aug 29, 2025
5c55b4f
[ADD] awesome_dashboard: implement base dashboard with layout, items,…
pusu-odoo Aug 29, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 32 additions & 2 deletions awesome_dashboard/static/src/dashboard.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,40 @@
/** @odoo-module **/

import { Component } from "@odoo/owl";
import { Component, onWillStart } from "@odoo/owl";
import { registry } from "@web/core/registry";
import { Layout } from "@web/search/layout";
import { useService } from "@web/core/utils/hooks";
import { DashboardItem } from "./dashboard_item";

class AwesomeDashboard extends Component {
static template = "awesome_dashboard.AwesomeDashboard";
static components = { Layout, DashboardItem } ;

setup(){
this.actions = useService("action");
this.statisticsService = useService("awesome_dashboard.statistics");
this.display = { controlPanel: {} };

onWillStart(async () => {
this.statistics = await this.statisticsService.loadStatistics();
})
}

openCustomersView(){
this.actions.doAction("base.action_partner_form");
}

openAllLeads(){
this.actions.doAction({
type: "ir.actions.act_window",
name: "All leads",
res_model: "crm.lead",
views: [
[false, "list"],
[false, "form"]
]
});
}
}

registry.category("actions").add("awesome_dashboard.dashboard", AwesomeDashboard);
registry.category("actions").add("awesome_dashboard.dashboard", AwesomeDashboard);
3 changes: 3 additions & 0 deletions awesome_dashboard/static/src/dashboard.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.o_dashboard{
background-color: grey;
}
39 changes: 38 additions & 1 deletion awesome_dashboard/static/src/dashboard.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,44 @@
<templates xml:space="preserve">

<t t-name="awesome_dashboard.AwesomeDashboard">
hello dashboard
<Layout display="display" className="'o_dashboard h-100'">
<t t-set-slot="layout-buttons">
<button class="btn btn-primary" t-on-click="openCustomersView">Customers</button>
<button class="btn btn-primary" t-on-click="openAllLeads">Leads</button>
</t>
<div class="d-flex flex-wrap">
<DashboardItem>
Average amount of t-shirt by order this month
<div class="fs-1 fw-bold text-success text-center">
<t t-esc="statistics.average_quantity"/>
</div>
</DashboardItem>
<DashboardItem size="2">
Average time for an order to go from 'new' to 'sent' or 'cancelled'
<div class="fs-1 fw-bold text-success text-center">
<t t-esc="statistics.average_time"/>
</div>
</DashboardItem>
<DashboardItem>
Number of new orders this month
<div class="fs-1 fw-bold text-success text-center">
<t t-esc="statistics.nb_new_orders"/>
</div>
</DashboardItem>
<DashboardItem>
Number of cancelled orders this month
<div class="fs-1 fw-bold text-success text-center">
<t t-esc="statistics.nb_cancelled_orders"/>
</div>
</DashboardItem>
<DashboardItem>
Total amount of new orders this month
<div class="fs-1 fw-bold text-success text-center">
<t t-esc="statistics.total_amount"/>
</div>
</DashboardItem>
</div>
</Layout>
</t>

</templates>
18 changes: 18 additions & 0 deletions awesome_dashboard/static/src/dashboard_item.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Component } from "@odoo/owl";

export class DashboardItem extends Component{
static template = "awesome_dashboard.DashboardItem"
static props = {
slots: {
type: Object,
shape: {
default: Object
},
},
size: {
type: Number,
default: 1,
optional: true,
},
};
}
12 changes: 12 additions & 0 deletions awesome_dashboard/static/src/dashboard_item.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8" ?>
<templates xml:space="preserve">

<t t-name="awesome_dashboard.DashboardItem">
<div class="card m-2 border-dark" t-attf-style="width: {{18*props.size}}rem;">
<div class="card-body">
<t t-slot="default"/>
</div>
</div>
</t>

</templates>
Empty file.
17 changes: 17 additions & 0 deletions awesome_dashboard/static/src/statistics_service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/** @odoo-module **/

import { registry } from "@web/core/registry";
import { memoize } from "@web/core/utils/functions";
import { rpc } from "@web/core/network/rpc";

const statisticsService = {
async: ["loadStatistics"],
start() {
return {
loadStatistics : memoize(()=>rpc("/awesome_dashboard/statistics"))
};
},
};

// Register the service
registry.category("services").add("awesome_dashboard.statistics", statisticsService);
19 changes: 19 additions & 0 deletions awesome_owl/static/src/Card/card.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Component , useState } from "@odoo/owl";

export class Card extends Component {
static template = "awesome_owl.Card";

static props = {
title: String,
// content: { type: String, optional: true},
slots: Object
};

setup(){
this.state = useState({ isOpen: true });
}

toggleContent() {
this.state.isOpen = !this.state.isOpen;
}
}
29 changes: 29 additions & 0 deletions awesome_owl/static/src/Card/card.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>

<templates xml:space="preserve">

<t t-name="awesome_owl.Card">
<div class="card" style="width: 18rem; margin: 12px; border: 1px solid #ccc; border-radius: 8px; box-shadow: 2px 2px 6px rgba(0,0,0,0.1);">
<div class="card-body">

<!-- Card header with title + toggle button -->
<div class="d-flex justify-content-between align-items-center mb-2">
<h5 class="card-title mb-0">
<t t-esc="props.title"/>
</h5>
<input class="form-check-input"
type="checkbox"
role="switch"
t-att-checked="state.isOpen"
t-on-change="toggleContent"/>
</div>

<!-- Content: only visible if open -->
<t t-if="state.isOpen">
<t t-slot="default"/>
</t>
</div>
</div>
</t>

</templates>
18 changes: 18 additions & 0 deletions awesome_owl/static/src/Counter/counter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
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({count: 0});
}

increment() {
if(this.props.onChange) {
this.props.onChange();
}
this.state.count++;
}
}
16 changes: 16 additions & 0 deletions awesome_owl/static/src/Counter/counter.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8" ?>

<templates xml:space="preserve">

<t t-name="awesome_owl.Counter">
<!-- <div style="display: inline-flex; align-items: center; gap: 8px;"> -->
<p style="font-weight: bold; margin: 2px;">Counter: <t t-esc="state.count"/></p>
<button type="button"
t-on-click="increment"
style="background: blue; color: white; padding: 6px 12px; border: none; border-radius: 6px; cursor: pointer;">
Increment
</button>
<!-- </div> -->
</t>

</templates>
13 changes: 13 additions & 0 deletions awesome_owl/static/src/TodoList/todo_item.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Component } from "@odoo/owl";

export class TodoItem extends Component {
static template = "awesome_owl.TodoItem";

static props = {
id: Number,
description: String,
isCompleted: Boolean,
toggleState: Function, // callback to toggle the state
removeTodo: Function // callback to remove the todo
};
}
41 changes: 41 additions & 0 deletions awesome_owl/static/src/TodoList/todo_item.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<templates xml:space="preserve">
<t t-name="awesome_owl.TodoItem">

<!-- Flexbox row for item layout -->
<div class="d-flex justify-content-between align-items-center">

<!-- Left side: checkbox + id + description -->
<div class="d-flex align-items-center">

<!-- Checkbox -->
<input type="checkbox"
class="form-check-input me-2"
t-att-checked="props.isCompleted"
t-on-change="() => props.toggleState(props.id)"/>

<!-- Task id + description -->
<span class="fw-medium">
<span class="me-2"><t t-esc="props.id"/></span>
<span t-att-class="props.isCompleted ? 'text-muted text-decoration-line-through' : ''">
<t t-esc="props.description"/>
</span>
</span>
</div>

<!-- Right side: status badge + delete button -->
<div class="d-flex align-items-center gap-2">
<span t-if="props.isCompleted" class="badge bg-success">Done</span>
<span t-else="" class="badge bg-warning text-dark">Pending</span>

<!-- Delete button (Bootstrap icon trash) -->
<button type="button"
class="btn btn-primary"
title="Delete task"
t-on-click="() => props.removeTodo(props.id)">
<i class="fa fa-trash"></i>
</button>
</div>
</div>
</t>
</templates>
45 changes: 45 additions & 0 deletions awesome_owl/static/src/TodoList/todo_list.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { Component ,useState} from "@odoo/owl";
import { TodoItem } from "./todo_item";
import { useAutoFocus } from "../utils";

export class TodoList extends Component {
static template = "awesome_owl.TodoList";
static props = {};
static components = { TodoItem };

setup() {
this.todos = useState([]);
this.nextId = 1;

// hook will automatically focus the input on mount
this.inputRef = useAutoFocus("taskInput");
}

addTodo(ev) {
// Check if Enter was pressed
if (ev.keyCode === 13 && ev.target.value.trim() !== "") {
this.todos.push({
id: this.nextId++,
description: ev.target.value.trim(),
isCompleted: false,
});
// Clear input
ev.target.value = "";
}
}

toggleState(id) {
const todo = this.todos.find(t => t.id === id);
if (todo) {
todo.isCompleted = !todo.isCompleted;
}
}

removeTodo(id) {
const index = this.todos.findIndex((elem) => elem.id === id);
if (index >= 0) {
// remove the element at index from list
this.todos.splice(index, 1);
}
}
}
48 changes: 48 additions & 0 deletions awesome_owl/static/src/TodoList/todo_list.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<templates xml:space="preserve">
<t t-name="awesome_owl.TodoList">
<!-- Card wrapper for nice layout -->
<div class="card shadow-sm m-3">
<!-- Header -->
<div class="card-header bg-primary text-white fw-bold">
Todo List
</div>

<!-- Body -->
<div class="card-body p-3">
<!-- Input for adding a new task -->
<div class="mb-3">
<input type="text"
class="form-control"
placeholder="Enter a new task"
t-ref="taskInput"
t-on-keyup="addTodo"/>
</div>

<!-- List of tasks -->
<ul class="list-group list-group-flush">
<!-- If there are todos -->
<t t-if="todos.length != 0">
<t t-foreach="todos" t-as="todo" t-key="todo.id">
<!-- Conditional class for completed vs pending -->
<li t-att-class="todo.isCompleted ? 'list-group-item list-group-item-success text-decoration-line-through' : 'list-group-item'"
aria-disabled="true">
<TodoItem id="todo.id"
description="todo.description"
isCompleted="todo.isCompleted"
toggleState.bind="toggleState"
removeTodo.bind="removeTodo"/>
</li>
</t>
</t>
<!-- If empty -->
<t t-else="">
<li class="list-group-item text-muted fst-italic text-center">
No tasks yet.
</li>
</t>
</ul>
</div>
</div>
</t>
</templates>
Loading