[Workflow] Customizing Frappe UI such as Frappe CRM & HR from custom App

Like many in community, we too faced challenges while trying to customize Frappe UI-based applications. Sometimes, client needs some customisations that are critical. Am sharing the workflow we are following for the same, for feedback and for others benefit.

The Challenge
While Frappe’s Desk based application and modules are highly customizable using client scripts, server scripts etc… Frappe UI based apps built using Vue.js (e.g., CRM, Gameplan), etc are almost impossible to customise out of the box. Overriding or extending these apps without modifying the core can be tricky.

  • The Vue.js frontend code of most frappe-ui apps resides inside the frontend folder outside of app scope and most apps don’t use frappe bundler.
  • Customising components directly in the app is not a scalable or maintainable approach, as updates to the core app can overwrite your changes.
  • We wanted a cleaner way to extend functionality without touching the core app, much like we do with hooks in frappe.

The below workflow allow you to override a single component for example same as you would do in frappe website. Simply copy the component from the original app and put it in your app.

Please see the detailed workflow and solution in this post:

Github - example code overriding Frappe CRM

Files of interest:

  1. ./frontend/vite.config.js
  2. `./frontend/package.json
  3. `./frontend/custom-build.js

Below script does most of the heavylifting:

const fs = require('fs-extra');
const path = require('path');
const { execSync } = require('child_process');


const crmAppPath = path.resolve(__dirname, '../../crm/frontend');
const overrideSrcPath = path.resolve(__dirname, './src');
const overrideFilesPath = path.resolve(__dirname, './src_override');

console.log('Starting  :  Copying original src.');
fs.copySync(path.join(crmAppPath, 'src'), overrideSrcPath);
console.log('Completed :  Copying original src.');

console.log('Starting  :  Overriding src.');
fs.copySync(overrideFilesPath, overrideSrcPath);
console.log('Completed :  Overriding src.');

execSync('yarn install', { stdio: 'inherit' });

Note: If you are using Frappe Cloud, please be advised this has not been tested in FC and you may need to build before pushing to FC.

Looking forward to hear how others have tackled this and feedback!

13 Likes

We tried this method. The method still overwrites the code in SRC.
There are couple of drawbacks we found.

  1. The changes are not reflecting(hot reloading). The changes are only reflecting after build
  2. After Build the the corresponding files in the SRC folder are overridden. This defeats the purpose of extending the app. this disturbs the core code(copied original code) and makes it difficult to integrate the code from the main CRM app

Alternative approaches could be

  1. Telling the package to use the files under src_override instead of files in src could be one approach so that the files in src is still untouched.
  2. Option to inherit the files in src and provide options to extend.

I am not sure how to achieve the above. But if there is a method available, then it would be useful.

Original apps source is untouched. It should ideally only override the component you have put in the `src_override’ folder.

Yes, there are many ways we could potentially improve this. Ideal way is frappe_ui come up with some standard means to enable overriding.

This just an example of how we are currently extending frappe_ui apps.

1 Like