├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── LICENSE ├── MANIFEST.in ├── README.md ├── frappe_better_list_view ├── __init__.py ├── config │ ├── __init__.py │ ├── desktop.py │ └── docs.py ├── frappe_better_list_view │ └── __init__.py ├── hooks.py ├── modules.txt ├── patches.txt ├── public │ ├── build.json │ └── js │ │ ├── better_list_view.bundle.js │ │ └── better_list_view_v12.bundle.js └── version.py ├── images └── row_bg.png ├── pyproject.toml ├── requirements.txt └── setup.py /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "[BUG]" 5 | labels: bug 6 | assignees: kid1194 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Additional context** 27 | Add any other context about the problem here. 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "[REQ]" 5 | labels: enhancement 6 | assignees: kid1194 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright 2022 Level Up Marketing & Development Services 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this 6 | software and associated documentation files (the "Software"), to deal in the Software 7 | without restriction, including without limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons 9 | to whom the Software is furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all copies 12 | or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 15 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 16 | PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 17 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 18 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 19 | USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include MANIFEST.in 2 | include requirements.txt 3 | include *.json 4 | include *.md 5 | include *.py 6 | include *.txt 7 | recursive-include frappe_better_list_view *.css 8 | recursive-include frappe_better_list_view *.csv 9 | recursive-include frappe_better_list_view *.html 10 | recursive-include frappe_better_list_view *.ico 11 | recursive-include frappe_better_list_view *.js 12 | recursive-include frappe_better_list_view *.json 13 | recursive-include frappe_better_list_view *.md 14 | recursive-include frappe_better_list_view *.png 15 | recursive-include frappe_better_list_view *.py 16 | recursive-include frappe_better_list_view *.svg 17 | recursive-include frappe_better_list_view *.txt 18 | recursive-exclude frappe_better_list_view *.pyc -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Frappe Better List View 2 | 3 | A small **Frappe** list view plugin that allows customization. 4 | 5 | ![v1.4.0](https://img.shields.io/badge/v1.4.0-2024/05/30-green?style=plastic) 6 | 7 | --- 8 | 9 | ### Table of Contents 10 | - [Requirements](#requirements) 11 | - [Setup](#setup) 12 | - [Install](#install) 13 | - [Update](#update) 14 | - [Uninstall](#uninstall) 15 | - [Usage](#usage) 16 | - [Options](#options) 17 | - [Methods](#methods) 18 | - [Example](#example) 19 | - [Issues](#issues) 20 | - [License](#license) 21 | 22 | --- 23 | 24 | ### Requirements 25 | - Frappe >= v12.0.0 26 | 27 | --- 28 | 29 | ### Setup 30 | 31 | ⚠️ *Important* ⚠️ 32 | 33 | *Do not forget to replace [sitename] with the name of your site in all commands.* 34 | 35 | #### Install 36 | 1. Go to bench directory 37 | 38 | ``` 39 | cd ~/frappe-bench 40 | ``` 41 | 42 | 2. Get plugin from Github 43 | 44 | ``` 45 | bench get-app https://github.com/kid1194/frappe-better-list-view 46 | ``` 47 | 48 | 3. Build plugin 49 | 50 | ``` 51 | bench build --app frappe_better_list_view 52 | ``` 53 | 54 | 4. Install plugin on a specific site 55 | 56 | ``` 57 | bench --site [sitename] install-app frappe_better_list_view 58 | ``` 59 | 60 | 5. (Optional) Restart bench to clear cache 61 | 62 | ``` 63 | bench restart 64 | ``` 65 | 66 | #### Update 67 | 1. Go to app directory 68 | 69 | ``` 70 | cd ~/frappe-bench/apps/frappe_better_list_view 71 | ``` 72 | 73 | 2. Get updates from Github 74 | 75 | ``` 76 | git pull 77 | ``` 78 | 79 | 3. Go to bench directory 80 | 81 | ``` 82 | cd ~/frappe-bench 83 | ``` 84 | 85 | 4. Build plugin 86 | 87 | ``` 88 | bench build --app frappe_better_list_view 89 | ``` 90 | 91 | 5. Update a specific site 92 | 93 | ``` 94 | bench --site [sitename] migrate 95 | ``` 96 | 97 | 6. (Optional) Restart bench to clear cache 98 | 99 | ``` 100 | bench restart 101 | ``` 102 | 103 | #### Uninstall 104 | 1. Go to bench directory 105 | 106 | ``` 107 | cd ~/frappe-bench 108 | ``` 109 | 110 | 2. Uninstall plugin from a specific site 111 | 112 | ``` 113 | bench --site [sitename] uninstall-app frappe_better_list_view 114 | ``` 115 | 116 | 3. Remove plugin from bench 117 | 118 | ``` 119 | bench remove-app frappe_better_list_view 120 | ``` 121 | 122 | 4. (Optional) Restart bench to clear cache 123 | 124 | ``` 125 | bench restart 126 | ``` 127 | 128 | --- 129 | 130 | ### Usage 131 | 132 | #### Options 133 | ##### 1. `status` 🔴 134 | 135 | Status object to enable or disable ListView. 136 | 137 | **Keys:** 138 | | Key | Type | Description | 139 | | :--- | :--- | :--- | 140 | | `enable` | Boolean | Enabled or disabled status.

Default: `true` | 141 | | `message` | String | Disabled message.

Default: `ListView is disabled.` | 142 | | `color` | String | Message text color.

Colors: `green`, `blue`, `orange`, `gray`, `red`

Default: `red` | 143 | 144 | **Example:** 145 | ``` 146 | { 147 | enable: false, 148 | message: __('ListView is disabled.'), 149 | color: 'red' 150 | } 151 | ``` 152 | 153 | ##### 2. `query_fields` 154 | 155 | List of additional fields to fetch but not display. 156 | 157 | **Example:** 158 | ``` 159 | ['is_approved', 'is_paid'] 160 | ``` 161 | 162 | ##### 3. `query_filters` 163 | 164 | List of additional filters for the fetch query. 165 | 166 | **Example:** 167 | - Object: 168 | ``` 169 | {is_approved: 1, is_paid: 0} 170 | ``` 171 | - Array: 172 | ``` 173 | [ 174 | ['is_approved', '=', 1], 175 | ['is_paid', '=', 0] 176 | ] 177 | ``` 178 | 179 | ##### 4. `page_length` 180 | 181 | Number of rows to display per page. 182 | - Default: `20` 183 | - Minimum: `20` 184 | 185 | **Example:** 186 | ``` 187 | 50 188 | ``` 189 | 190 | ##### 5. `parser` 191 | 192 | Function to modify the list data before display. 193 | 194 | **Arguments:** 195 | | Name | Type | Description | 196 | | :--- | :--- | :--- | 197 | | `data` | Array | Data list before display. | 198 | | `render` | Function | ⚠️ Must be called after data parsing is done to render ListView. | 199 | | `error` | Function | ⚠️ Must be called when an error is raised to ignore all data modification. | 200 | 201 | ⚠️ *Important* ⚠️ 202 | 203 | If an error isn't caught inside the parser function, all data modification will be ignored and original data will be rendered automatically instead. 204 | 205 | **Examples:** 206 | ``` 207 | function(data, render, error) { 208 | let names = []; 209 | data.forEach(function(row) { 210 | names.push(row.name); 211 | }); 212 | frappe.db.get_list('Doctype', { 213 | fields: ['name', 'value'], 214 | filters: { 215 | name: ['in', names], 216 | } 217 | }).then(function(list) { 218 | list.forEach(function(vals) { 219 | data.forEach(function(row) { 220 | if (vals.name === row.name) { 221 | row.value = vals.value; 222 | } 223 | }); 224 | }); 225 | // Render modified data 226 | render(); 227 | }).catch(function(e) { 228 | console.error(e.message, e.stack); 229 | // Render original data instead 230 | error(); 231 | }); 232 | } 233 | ``` 234 | 235 | ##### 6. `set_row_background` 236 | 237 | Function to set the row background color. 238 | 239 | **Arguments:** 240 | | Name | Type | Description | 241 | | :--- | :--- | :--- | 242 | | `row` | Plain Object | ListView row data object. | 243 | 244 | 245 | **Return:** 246 | | Type | Description | 247 | | :--- | :--- | 248 | | `String` | Row background color.

Color Type: `CSS Key`, `Hex`, `RGB`, `RGBA` or `HSLA`. | 249 | | `Null` | No row background color. | 250 | 251 | 252 | **CSS Colors & Keys:** 253 |

254 | Frappe Better List View 255 |

256 | 257 | **Examples:** 258 | ``` 259 | function(row) { 260 | let cost = cint(row.cost); 261 | if (cost > 1000) return 'danger'; 262 | if (cost > 800) return '#ffeeba'; 263 | if (cost > 600) return 'rgb(190,229,235)'; 264 | if (cost > 400) return 'rgba(190,229,235,1)'; 265 | if (cost < 200) return 'hsla(133.7,41.2%,83.3%,1)'; 266 | } 267 | ``` 268 | 269 | #### Methods 270 | ##### 1. `toggle_status` 271 | 272 | Method to enable or disable ListView on demand. It can be called from within `onload` event. 273 | 274 | **Parameters:** 275 | | Name | Type | Description | 276 | | :--- | :--- | :--- | 277 | | `enable` | Boolean | Enabled or disabled status.

Default: `true` | 278 | | `message` | String | Disabled message.

Default: `ListView is disabled.` | 279 | | `color` | String | Message text color.

Colors: `green`, `blue`, `orange`, `gray`, `red`

Default: `red` | 280 | 281 | 282 | **Example:** 283 | ``` 284 | frappe.listview_settings['DocType'] = { 285 | onload: function(listview) { 286 | if (!frappe.user_roles.includes('Some Role')) { 287 | listview.toggle_status(false, __('ListView is disabled.'), 'red'); 288 | } 289 | } 290 | }; 291 | ``` 292 | 293 | #### Example 294 | 295 | ``` 296 | frappe.listview_settings['DocType'] = { 297 | /* 298 | *--------------------------------------------------- 299 | *---------- 🔴 Plugin Custom Options 🔴 ------------ 300 | *--------------------------------------------------- 301 | */ 302 | 303 | /* 304 | * 1. ListView status 305 | */ 306 | status: { 307 | enable: false, 308 | message: __('ListView is disabled.'), 309 | color: 'red' 310 | }, 311 | /* 312 | * 2. Fields to fetch but not display 313 | */ 314 | query_fields: ['is_approved', 'is_paid'], 315 | /* 316 | * 3. Additional filters (array or object) for fetch query 317 | */ 318 | query_filters: { 319 | is_approved: 1, 320 | is_paid: 1, 321 | }, 322 | /* 323 | * 4. Only 50 rows will be displayed per page 324 | */ 325 | page_length: 50, 326 | /* 327 | * 5. List data modify function 328 | */ 329 | parser: function(data, render, error) { 330 | let names = []; 331 | data.forEach(function(row) { 332 | names.push(row.name); 333 | }); 334 | if (!names.length) { 335 | return render(); 336 | } 337 | frappe.db.get_list('Doctype', { 338 | fields: ['name', 'price'], 339 | filters: { 340 | name: ['in', names], 341 | is_approved: 1, 342 | } 343 | }).then(function(list) { 344 | list.forEach(function(vals) { 345 | data.forEach(function(row) { 346 | if (vals.name === row.name) { 347 | row.price = vals.price; 348 | } 349 | }); 350 | }); 351 | // Render modified data 352 | render(); 353 | }).catch(function(e) { 354 | console.error(e.message, e.stack); 355 | // Render original data instead 356 | error(); 357 | }); 358 | }, 359 | /* 360 | * 6. Custom row background color 361 | */ 362 | set_row_background: function(row) { 363 | if (!cint(row.is_approved)) return 'info'; 364 | }, 365 | 366 | 367 | /* 368 | *--------------------------------------------------- 369 | *-------- 🔵 ListView Options & Events 🔵 ---------- 370 | *--------------------------------------------------- 371 | */ 372 | 373 | /* 374 | * 1. Onload event 375 | * 376 | * ListView status can be toggled and changed using 377 | * the method "toggle_status" added by the plugin. 378 | */ 379 | onload: function(listview) { 380 | if (!frappe.user_roles.includes('Some Role')) { 381 | listview.toggle_status(false, __('ListView is disabled.'), 'red'); 382 | } 383 | }, 384 | /* 385 | * 2. Custom indicator method 386 | * 387 | * Additional fields listed in the "query_fields" option above 388 | * are added to the "doc" object and can be accessed directly. 389 | */ 390 | get_indicator: function(doc) { 391 | if (doc.is_paid) { 392 | return [__('Paid'), 'blue', 'is_paid,=,Yes|is_approved,=,Yes']; 393 | } 394 | if (doc.is_approved) { 395 | return [__('Approved'), 'green', 'is_paid,=,No|is_approved,=,Yes']; 396 | } 397 | return [__('Pending'), 'gray', 'is_paid,=,No|is_approved,=,No']; 398 | }, 399 | /* 400 | * 2. Column data formatters 401 | * 402 | * Additional fields listed in the "query_fields" option above 403 | * are added to the "doc" object and can be accessed directly. 404 | */ 405 | formatters: { 406 | name: function(value, field, doc) { 407 | let html = value; 408 | if (doc.is_approved) { 409 | html += ' '; 410 | } 411 | return html; 412 | }, 413 | }, 414 | }; 415 | ``` 416 | 417 | --- 418 | 419 | ### Issues 420 | If you find a bug, please create a [bug report](https://github.com/kid1194/frappe-better-list-view/issues/new?assignees=kid1194&labels=bug&template=bug_report.md&title=%5BBUG%5D) and let us know about it. 421 | 422 | --- 423 | 424 | ### License 425 | This plugin has been released under the [MIT License](https://github.com/kid1194/frappe-better-list-view/blob/main/LICENSE). 426 | -------------------------------------------------------------------------------- /frappe_better_list_view/__init__.py: -------------------------------------------------------------------------------- 1 | # Frappe Better List View © 2024 2 | # Author: Ameen Ahmed 3 | # Company: Level Up Marketing & Software Development Services 4 | # Licence: Please refer to LICENSE file 5 | 6 | 7 | __version__ = "1.4.0" -------------------------------------------------------------------------------- /frappe_better_list_view/config/__init__.py: -------------------------------------------------------------------------------- 1 | # Frappe Better List View © 2024 2 | # Author: Ameen Ahmed 3 | # Company: Level Up Marketing & Software Development Services 4 | # Licence: Please refer to LICENSE file -------------------------------------------------------------------------------- /frappe_better_list_view/config/desktop.py: -------------------------------------------------------------------------------- 1 | # Frappe Better List View © 2024 2 | # Author: Ameen Ahmed 3 | # Company: Level Up Marketing & Software Development Services 4 | # Licence: Please refer to LICENSE file 5 | 6 | 7 | from frappe import _ 8 | 9 | 10 | def get_data(): 11 | return [ 12 | { 13 | "module_name": "Frappe Better List View", 14 | "color": "blue", 15 | "icon": "octicon octicon-list-unordered", 16 | "type": "module", 17 | "label": _("Frappe Better List View") 18 | } 19 | ] -------------------------------------------------------------------------------- /frappe_better_list_view/config/docs.py: -------------------------------------------------------------------------------- 1 | # Frappe Better List View © 2024 2 | # Author: Ameen Ahmed 3 | # Company: Level Up Marketing & Software Development Services 4 | # Licence: Please refer to LICENSE file 5 | 6 | 7 | """ 8 | Configuration for docs 9 | """ 10 | 11 | 12 | def get_context(context): 13 | context.brand_html = "Frappe Better List View" 14 | -------------------------------------------------------------------------------- /frappe_better_list_view/frappe_better_list_view/__init__.py: -------------------------------------------------------------------------------- 1 | # Frappe Better List View © 2024 2 | # Author: Ameen Ahmed 3 | # Company: Level Up Marketing & Software Development Services 4 | # Licence: Please refer to LICENSE file -------------------------------------------------------------------------------- /frappe_better_list_view/hooks.py: -------------------------------------------------------------------------------- 1 | # Frappe Better List View © 2024 2 | # Author: Ameen Ahmed 3 | # Company: Level Up Marketing & Software Development Services 4 | # Licence: Please refer to LICENSE file 5 | 6 | 7 | from .version import is_version_gt 8 | 9 | 10 | app_name = "frappe_better_list_view" 11 | app_title = "Frappe Better List View" 12 | app_publisher = "Ameen Ahmed (Level Up)" 13 | app_description = "Frappe list view plugin that allows modification." 14 | app_icon = "octicon octicon-list-unordered" 15 | app_color = "blue" 16 | app_email = "kid1194@gmail.com" 17 | app_license = "MIT" 18 | 19 | 20 | app_include_js = [ 21 | 'better_list_view.bundle.js' 22 | ] if is_version_gt(13) else ([ 23 | '/assets/frappe_better_list_view/js/better_list_view.js' 24 | ] if is_version_gt(12) else [ 25 | '/assets/frappe_better_list_view/js/better_list_view_v12.js' 26 | ]) -------------------------------------------------------------------------------- /frappe_better_list_view/modules.txt: -------------------------------------------------------------------------------- 1 | Frappe Better List View -------------------------------------------------------------------------------- /frappe_better_list_view/patches.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kid1194/frappe-better-list-view/c8bb8d3c9338f4dc6a4d6f17a3d1bae326c841ff/frappe_better_list_view/patches.txt -------------------------------------------------------------------------------- /frappe_better_list_view/public/build.json: -------------------------------------------------------------------------------- 1 | { 2 | "frappe_better_list_view/js/better_list_view.js": [ 3 | "public/js/better_list_view.bundle.js" 4 | ], 5 | "frappe_better_list_view/js/better_list_view_v12.js": [ 6 | "public/js/better_list_view_v12.bundle.js" 7 | ] 8 | } -------------------------------------------------------------------------------- /frappe_better_list_view/public/js/better_list_view.bundle.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Frappe Better List View © 2024 3 | * Author: Ameen Ahmed 4 | * Company: Level Up Marketing & Software Development Services 5 | * Licence: Please refer to LICENSE file 6 | */ 7 | 8 | 9 | frappe.views.ListView = class ListView extends frappe.views.ListView { 10 | constructor(opts) { 11 | super(opts); 12 | this._is_enabled = true; 13 | this._row_class = 'level list-row'; 14 | this._row_backgrounds = [ 15 | 'active', 'primary', 'secondary', 16 | 'success', 'danger', 'warning', 'info', 17 | ]; 18 | } 19 | toggle_status(enable, message, color) { 20 | if (enable) { 21 | this._is_enabled = true; 22 | this.page.clear_inner_toolbar(); 23 | this.set_primary_action(); 24 | } else { 25 | this._is_enabled = false; 26 | this.page.hide_actions_menu(); 27 | this.page.clear_primary_action(); 28 | this.page.clear_inner_toolbar(); 29 | message = message || __('ListView is disabled.'); 30 | color = color || 'red'; 31 | let colors = { 32 | green: 'success', 33 | blue: 'info', 34 | orange: 'warning', 35 | gray: 'muted', 36 | red: 'danger' 37 | }; 38 | this.page.add_inner_message(message) 39 | .removeClass('text-muted') 40 | .addClass('text-' + (colors[color] || colors.red)); 41 | } 42 | } 43 | set_primary_action() { 44 | if (this._is_enabled) super.set_primary_action(); 45 | else this.page.clear_primary_action(); 46 | } 47 | toggle_actions_menu_button() { 48 | if (this._is_enabled) super.toggle_actions_menu_button(); 49 | } 50 | setup_events() { 51 | super.setup_events(); 52 | if ( 53 | $.isPlainObject(this.settings.status) 54 | && this.settings.status.enabled != null 55 | ) { 56 | this.toggle_status( 57 | this.settings.status.enabled, 58 | this.settings.status.message, 59 | this.settings.status.color 60 | ); 61 | } 62 | } 63 | get_args() { 64 | let args = super.get_args(); 65 | if (args.doctype !== this.doctype) { 66 | console.error(__('ListView invalid super args.')); 67 | return args; 68 | } 69 | if ( 70 | $.isArray(this.settings.query_fields) 71 | && this.settings.query_fields.length 72 | ) { 73 | for (let i = 0, l = this.settings.query_fields.length, f; i < l; i++) { 74 | f = frappe.model.get_full_column_name( 75 | this.settings.query_fields[i], 76 | this.doctype 77 | ); 78 | if (args.fields.indexOf(f) < 0) args.fields.push(f); 79 | } 80 | } 81 | if ( 82 | $.isPlainObject(this.settings.query_filters) 83 | && !$.isEmptyObject(this.settings.query_filters) 84 | ) { 85 | for (let key in this.settings.query_filters) { 86 | this._add_query_filter(args, key); 87 | } 88 | } else if ( 89 | $.isArray(this.settings.query_filters) 90 | && this.settings.query_filters.length 91 | ) { 92 | for (let i = 0, l = this.settings.query_filters.length; i < l; i++) { 93 | this._add_query_filter(args, i); 94 | } 95 | } 96 | if (cint(this.settings.page_length) >= 20) 97 | args.page_length = cint(this.settings.page_length); 98 | return args; 99 | } 100 | render_list() { 101 | if ( 102 | this._data_rendered 103 | || !$.isFunction(this.settings.parser) 104 | ) { 105 | delete this._data_rendered; 106 | return super.render_list(); 107 | } 108 | var me = this, 109 | clone = []; 110 | for (let i = 0, l = this.data.length; i < l; i++) { 111 | clone[i] = $.extend(true, {}, this.data[i]); 112 | } 113 | let promise = new Promise(function(res, rej) { 114 | try { 115 | me.settings.parser(me.data, res, rej); 116 | } catch(_) { rej(); } 117 | }); 118 | promise.then( 119 | function() { clone = null; }, 120 | function() { me.data = clone; } 121 | ); 122 | promise.catch(function() { me.data = clone; }); 123 | promise.finally(function() { 124 | me._data_rendered = 1; 125 | me.render_list(); 126 | }); 127 | } 128 | get_list_row_html(doc) { 129 | let html = super.get_list_row_html(doc); 130 | if (!$.isFunction(this.settings.set_row_background)) return html; 131 | let color = this.settings.set_row_background(doc); 132 | if ( 133 | !color 134 | || Object.prototype.toString.call(color) !== '[object String]' 135 | || !color.length 136 | ) return html; 137 | if (this._row_backgrounds.indexOf(color) >= 0) { 138 | html = html.replace(this._row_class, this._row_class + ' table-' + color); 139 | } else if (/^(\#([a-z0-9]{3,})|(rgb(a|)|hsla)\(([0-9\,\%\.]+)\))$/i.test(color)) { 140 | html = html.replace(this._row_class, this._row_class + '" style="background-color:' + color + '"'); 141 | } 142 | return html; 143 | } 144 | _add_query_filter(args, field) { 145 | let qry = this._get_query_filter(field); 146 | if (qry && args.filters.indexOf(qry) < 0) 147 | args.filters.push(qry); 148 | } 149 | _get_query_filter(field) { 150 | let ret = [ 151 | this.doctype, field, '=', 152 | this.settings.query_filters[field] 153 | ]; 154 | if ($.isArray(ret[3])) { 155 | let cond = ret[3]; 156 | if (cond.length < 2) return; 157 | if (cond.length > 2) ret[1] = cond.shift(); 158 | ret[2] = cond[0]; 159 | ret[3] = cond[1]; 160 | } 161 | return ret; 162 | } 163 | }; -------------------------------------------------------------------------------- /frappe_better_list_view/public/js/better_list_view_v12.bundle.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Frappe Better List View © 2024 3 | * Author: Ameen Ahmed 4 | * Company: Level Up Marketing & Software Development Services 5 | * Licence: Please refer to LICENSE file 6 | */ 7 | 8 | 9 | frappe.views.ListView = class ListView extends frappe.views.ListView { 10 | constructor(opts) { 11 | super(opts); 12 | this._is_enabled = true; 13 | this._row_class = 'level list-row'; 14 | this._row_backgrounds = [ 15 | 'active', 'primary', 'secondary', 16 | 'success', 'danger', 'warning', 'info', 17 | ]; 18 | } 19 | toggle_status(enable, message, color) { 20 | if (enable) { 21 | this._is_enabled = true; 22 | this.page.clear_inner_toolbar(); 23 | this.set_primary_action(); 24 | } else { 25 | this._is_enabled = false; 26 | this.page.hide_actions_menu(); 27 | this.page.clear_primary_action(); 28 | this.page.clear_inner_toolbar(); 29 | message = message || __('ListView is disabled.'); 30 | color = color || 'red'; 31 | var colors = { 32 | green: 'success', 33 | blue: 'info', 34 | orange: 'warning', 35 | gray: 'muted', 36 | red: 'danger' 37 | }; 38 | this.page.add_inner_message(message) 39 | .removeClass('text-muted') 40 | .addClass('text-' + (colors[color] || colors.red)); 41 | } 42 | } 43 | set_primary_action() { 44 | if (this._is_enabled) super.set_primary_action(); 45 | else this.page.clear_primary_action(); 46 | } 47 | toggle_actions_menu_button() { 48 | if (this._is_enabled) super.toggle_actions_menu_button(); 49 | } 50 | setup_events() { 51 | super.setup_events(); 52 | if ( 53 | $.isPlainObject(this.settings.status) 54 | && this.settings.status.enabled != null 55 | ) { 56 | this.toggle_status( 57 | this.settings.status.enabled, 58 | this.settings.status.message, 59 | this.settings.status.color 60 | ); 61 | } 62 | } 63 | get_args() { 64 | var args = super.get_args(); 65 | if (args.doctype !== this.doctype) { 66 | console.error(__('Invalid list args.')); 67 | return args; 68 | } 69 | if ( 70 | $.isArray(this.settings.query_fields) 71 | && this.settings.query_fields.length 72 | ) { 73 | for (var i = 0, l = this.settings.query_fields.length, f; i < l; i++) { 74 | f = frappe.model.get_full_column_name( 75 | this.settings.query_fields[i], 76 | this.doctype 77 | ); 78 | if (args.fields.indexOf(f) < 0) args.fields.push(f); 79 | } 80 | } 81 | if ( 82 | $.isPlainObject(this.settings.query_filters) 83 | && !$.isEmptyObject(this.settings.query_filters) 84 | ) { 85 | for (var key in this.settings.query_filters) { 86 | this._add_query_filter(args, key); 87 | } 88 | } else if ( 89 | $.isArray(this.settings.query_filters) 90 | && this.settings.query_filters.length 91 | ) { 92 | for (var i = 0, l = this.settings.query_filters.length; i < l; i++) { 93 | this._add_query_filter(args, i); 94 | } 95 | } 96 | if (cint(this.settings.page_length) >= 20) 97 | args.page_length = cint(this.settings.page_length); 98 | return args; 99 | } 100 | render() { 101 | if ( 102 | this._data_rendered 103 | || !$.isFunction(this.settings.parser) 104 | ) { 105 | delete this._data_rendered; 106 | return super.render_list(); 107 | } 108 | var me = this, 109 | clone = []; 110 | for (var i = 0, l = this.data.length; i < l; i++) { 111 | clone[i] = $.extend(true, {}, this.data[i]); 112 | } 113 | var promise = new Promise(function(res, rej) { 114 | try { 115 | me.settings.parser(me.data, res, rej); 116 | } catch(_) { rej(); } 117 | }); 118 | promise.then( 119 | function() { clone = null; }, 120 | function() { me.data = clone; } 121 | ); 122 | promise.catch(function() { me.data = clone; }); 123 | promise.finally(function() { 124 | me._data_rendered = 1; 125 | me.render_list(); 126 | }); 127 | } 128 | get_list_row_html(doc) { 129 | var html = super.get_list_row_html(doc); 130 | if (!$.isFunction(this.settings.set_row_background)) return html; 131 | var color = this.settings.set_row_background(doc); 132 | if ( 133 | !color 134 | || Object.prototype.toString.call(color) !== '[object String]' 135 | || !color.length 136 | ) return html; 137 | if (this._row_backgrounds.indexOf(color) >= 0) { 138 | html = html.replace(this._row_class, this._row_class + ' table-' + color); 139 | } else if (/^(\#([a-z0-9]{3,})|(rgb(a|)|hsla)\(([0-9\,\%\.]+)\))$/i.test(color)) { 140 | html = html.replace(this._row_class, this._row_class + '" style="background-color:' + color + '"'); 141 | } 142 | return html; 143 | } 144 | _add_query_filter(args, field) { 145 | var qry = this._get_query_filter(field); 146 | if (qry && args.filters.indexOf(qry) < 0) 147 | args.filters.push(qry); 148 | } 149 | _get_query_filter(field) { 150 | var ret = [ 151 | this.doctype, field, '=', 152 | this.settings.query_filters[field] 153 | ]; 154 | if ($.isArray(ret[3])) { 155 | var cond = ret[3]; 156 | if (cond.length < 2) return; 157 | if (cond.length > 2) ret[1] = cond.shift(); 158 | ret[2] = cond[0]; 159 | ret[3] = cond[1]; 160 | } 161 | return ret; 162 | } 163 | }; -------------------------------------------------------------------------------- /frappe_better_list_view/version.py: -------------------------------------------------------------------------------- 1 | # Frappe Better List View © 2024 2 | # Author: Ameen Ahmed 3 | # Company: Level Up Marketing & Software Development Services 4 | # Licence: Please refer to LICENSE file 5 | 6 | 7 | from frappe import __version__ 8 | 9 | 10 | # [Internal] 11 | __frappe_version__ = int(__version__.split(".")[0]) 12 | 13 | 14 | # [Hooks] 15 | def is_version_gt(num: int): 16 | return __frappe_version__ > num -------------------------------------------------------------------------------- /images/row_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kid1194/frappe-better-list-view/c8bb8d3c9338f4dc6a4d6f17a3d1bae326c841ff/images/row_bg.png -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "frappe_better_list_view" 3 | authors = [ 4 | {name = "Ameen Ahmed (Level Up)", email = "kid1194@gmail.com"} 5 | ] 6 | description = "Frappe list view plugin that allows modification." 7 | keywords = ["frappe", "list view", "better list view"] 8 | classifiers = [ 9 | "Development Status :: 5 - Production/Stable", 10 | "License :: OSI Approved :: MIT License", 11 | "Programming Language :: JavaScript" 12 | ] 13 | requires-python = ">=3.6" 14 | readme = "README.md" 15 | dynamic = ["version"] 16 | dependencies = [ 17 | "frappe>=12.0.0" 18 | ] 19 | 20 | [project.urls] 21 | Documentation = "https://github.com/kid1194/frappe-better-list-view" 22 | Source = "https://github.com/kid1194/frappe-better-list-view" 23 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | frappe>=12.0.0 -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # Frappe Better List View © 2024 2 | # Author: Ameen Ahmed 3 | # Company: Level Up Marketing & Software Development Services 4 | # Licence: Please refer to LICENSE file 5 | 6 | 7 | from setuptools import setup, find_packages 8 | 9 | from frappe_better_list_view import __version__ 10 | 11 | 12 | with open('requirements.txt') as f: 13 | install_requires = f.read().strip().split('\n') 14 | 15 | 16 | setup( 17 | name='frappe_better_list_view', 18 | version=__version__, 19 | description='Frappe list view plugin that allows modification.', 20 | author='Ameen Ahmed (Level Up)', 21 | author_email='kid1194@gmail.com', 22 | packages=find_packages(), 23 | zip_safe=False, 24 | include_package_data=True, 25 | install_requires=install_requires 26 | ) 27 | --------------------------------------------------------------------------------