# Introduction

Freshworks developer platform now supports Vue local development natively in the FDK through Webpack 5 (opens new window). This documentation is intended to walk you through the implementation in the FDK, and the process to follow to get started with the Vue development using the FDK.

TIP

FDK supports both Vue version 2 and version 3, make sure you choose the right template while creating a new app.

# Implementation

The FDK comes built-in with Webpack 5 and a Webpack configuration file to mount during the compilation and the build phases, whenever the FDK detects the project is developed with Vue, the project is compiled using Webpack with default Webpack configuration.

Though the FDK has a default Webpack configuration for Vue, It is possible to provide custom configurations, the guidelines to define custom Webpack configurations are addressed in the latter part of this documentation.

# Create your first Vue app

To create a new Vue project,

  1. Update the FDK to the latest version.

  2. Create a new folder named my_app and open the terminal/command prompt inside the newly created folder

vel@freshworks:~$ mkdir my_app && cd my_app
1
  1. Run fdk create and select the product of your choice.

Choose Product

  1. Once you have chosen the desired product, select either your_first_vue_app or your_first_vue3_app template,

Choose Product

OR

Choose Product

  1. After creating the project, run npm install to install all the dependencies and devDependencies.
vel@freshworks:~/my_app$ npm install
1

# Vue app folder structure

The Vue App in the Freshworks ecosystem is similar to the Vue app created using vue-cli or a Vue app bundled using the Webpack, with some minor changes in the folder structure to support integration with the FDK.

The folder structure of the Vue app is explained below

  ├── app
  │   ├── icon.svg
  │   └── index.html
  ├── babel.config.js
  ├── config
  │   └── iparams.json
  ├── manifest.json
  ├── package-lock.json
  ├── package.json
  ├── public
  │   └── index.html
  └── src
      ├── App.vue
      ├── assets
      │   ├── icon.svg
      │   ├── logo.png
      │   └── vuejs.svg
      ├── components
      │   └── Sidebar.vue
      └── main.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# The manifest.json file

The manifest.json file contains the app metadata about your app, such as app locations, platform version, and other app related information.

# The package.json file

The package.json file contains the information about the framework used in the app, the dependencies, and devDependencies used by the app, and configurations if any.

# The app folder

The app folder contains the built/compiled assets of the app. The content of the app folder is served by the FDK in http://localhost:10001/iframe during fdk run

WARNING

  • Do not delete, replace or modify the index.html file inside the app folder, any changes made will be overwritten during the build. If you need to add or remove anything in the app/index.html, make sure you do it in public/index.html as it serves as the template file for app/index.html

  • Replace the icon.svg file in the app folder, if you choose to use a custom icon for the app. Make sure you change the name of the icon in manifest.json to the replaced/newly added image.

  • If you choose to use a custom Webpack config, make sure the output always points to the app folder or its subfolders.

# The Config folder

The config folder contains the installation parameter of the app.

WARNING

# The src folder

The src folder contains your Vue components and services.

# The public folder

The public folder contains an index.html file which serves as a template to the app/index.html. Adding CSS or scripts to the app can be done in the public/index.html.

# Run your first Vue app using the FDK

Running a Vue app using FDK is similar to running any other app,

  • Open the app folder in terminal and run fdk run

    vel@freshworks:~/my_app$ fdk run
    
    1
  • The FDK runs the app with Webpack if package.json is present in the root folder of the project with the following object,

      "fdkConfig": {
        "frontendFramework": "vue",
        "customConfig": ""
      }
    
    1
    2
    3
    4
  • the frontendFramework key in the fdkConfig objects denotes the framework of the project, FDK currently supports

    1. React
    2. Vue
    3. Vue3
  • the customConfig key denotes the path of the custom Webpack config you want to provide, although the customConfig not mandatory, the FDK will use the default config if any of the following scenarios hold.

    1. when there is no customConfig key in fdkConfig
    2. when customConfig is an empty string
    3. when the path provided is not valid.

WARNING

The path to the custom Webpack config module should be relative to the app's root folder.

# Lifecycle of an FDK Vue App

The lifecycle/App execution flow of a Vue app in FDK is shown in the image below.

Vue Flowchart

# Usage of existing frontend platform features in Vue

All the frontend features and interfaces should work as they would in the normal frontend app created using vanillaJS or JQuery, Although there few features that had to be implemented differently due to the restrictions imposed by Vue.

# Injecting the freshclient.js

The fresh_client.js is the interface that bridges your app and the developer platform. The fresh_client.js enables you to access the platform features such as request, db, interface, and instance through the client object.

In the normal vanilla Freshworks application, the `fresh_client.js' is included in the template.html as script src like shown below

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1.0" />
		<meta http-equiv="X-UA-Compatible" content="ie=edge" />
		<script src="https://static.freshdev.io/fdk/2.0/assets/fresh_client.js"></script>
	</head>
	<body>
		<div id="app"></div>
	</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12

Like in the vanillaJS application, you can include the fresh_client.js directly in the public/index.html.

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1.0" />
		<meta http-equiv="X-UA-Compatible" content="ie=edge" />
		<script src="https://static.freshdev.io/fdk/2.0/assets/fresh_client.js"></script>
	</head>
	<body>
		<div id="app"></div>
		<!-- built files will be auto injected -->
	</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13

# Passing the client Object to the child components.

Once the fresh_client.js is loaded, all the platform features can be accessed in the Vue app through the client object, the client object can then be passed down to the child components as a prop like shown below

App.vue

<template >
  <div>
      <Sidebar :client="client" />
  </div>
</template>
1
2
3
4
5

you can find a sample app that addresses passing down of props to the child component in this link

# Render App in multiple app locations

One of the most significant features of the Freshworks developer platform is to render an app in multiple locations, and it can be achieved by defining multiple template html files in manifest.json like shown in the example below

manifest.json

{
	"platform-version": "2.1",
	"product": {
		"freshdesk": {
			"location": {
				"ticket_sidebar": {
					"url": "index.html",
					"icon": "icon.svg"
				},
				"full_page_app": {
					"url": "full_page.html",
					"icon": "icon.svg"
				}
			}
		}
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

Since react is a Single Page Application framework it is not possible to define multiple html files for a single app but you can make use of the instance to achieve the same behavior and render different Vue components based on the app location instead of the template html file

App.vue


<template >
  <div v-if="location === 'username'">
      <Sidebar :client="client" />
  </div>
  <div v-if="location === 'full_page_app'">
      <Fullpage :client="client" />
  </div>
  <div v-if="location === 'modal'">
      <Modal :client="client" />
  </div>
</template>


<script>
import Sidebar from "./components/Sidebar.vue";
import Fullpage from "./components/Fullpage.vue";
import Modal from "./components/Modal.vue";

export default {
  mounted() {
    this.get();
  },
  name: "App",
  components: {
    Sidebar,
  },
  data() {
    return {
      client: {},
      location: ''
    };
  },
  methods: {
    get() {
      app.initialized().then((client) => {
        this.client = client;
        client.instance.context().then((data) => {
           this.location = data.location;
			});
      });
    },
  },
};

</script>

<style scoped>

</style>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

manifest.json

{
	"platform-version": "2.1",
	"product": {
		"freshdesk": {
			"location": {
				"ticket_sidebar": {
					"url": "index.html",
					"icon": "icon.svg"
				},
				"full_page_app": {
					"url": "index.html",
					"icon": "icon.svg"
				}
			}
		}
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

The logic discussed above can also be applied to interface methods like modals.

TIP

All the app locations and interface methods in a Vue app should point to the same template HTML file, for eg: index.html or the custom HTML defined by you in the Webpack config, though it is possible to use multiple HTML files and initialize fresh_client.js in all the HTML files, it is not recommended.

# Custom Webpack Config

FDK comes with a provision to provide custom webpack configuration, you can define the custom webpack configuration by providing a path to the configuration on the package.json file of the app.

The path to the configuration is provided in the configPath of the fdkConfig in package.json

package.json

{
  "name": "vue-webpack-setup",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "fdkConfig": {
    "frontendFramework": "vue3",
    "configPath": "webpack-config/webpack.config.js"    // path to Your Custom Webpack config, 
  },
  "scripts": {
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "clean-webpack-plugin": "^3.0.0",
    "core-js": "^3.9.1",
    "html-webpack-plugin": "^5.3.1",
    "mini-css-extract-plugin": "^1.4.0",
    "vue": "^3.0.0",
    "vue-loader-3": "npm:vue-loader@^16.1.2",
    "vue-router": "^4.0.5"
  },
  "devDependencies": {
    "@babel/core": "^7.13.10",
    "@babel/preset-env": "^7.13.10",
    "vue-loader": "^16.1.2",
    "@vue/compiler-sfc": "^3.0.7"
  },
  "browserslist": [
    "Chrome >= 45",
    "Firefox ESR",
    "Edge >= 12",
    "Explorer >= 10",
    "iOS >= 9",
    "Safari >= 9",
    "Android >= 4.4",
    "Opera >= 30",
    "> 1%",
    "last 2 versions",
    "not dead"
  ]
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

# Default config

The code snippet shown below is the default Webpack configuration that comes with the FDK, you can choose to make whatever changes you wish to the configuration, but make sure you follow the guidelines given below

  1. The output should always point to or be inside the app directory, so the app can be packed properly during fdk pack
  2. If you use any new dependencies in the configuration, make sure you install the dependencies inside the project root.
  3. Prefix the paths with ${process.cwd() so the FDK can locate the files inside your app folder
'use strict';

const { VueLoaderPlugin } = require('vue-loader-3');
const htmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
 
module.exports = {
  entry: {
    main: ['@babel/polyfill', `${process.cwd()}/src/main.js`]
  },
  output: {
    filename: '[name].[contenthash:8].js',
    path: `${process.cwd()}/app/scripts`,
    chunkFilename: '[name].[contenthash:8].js',
    publicPath: './scripts'
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: [
          /node_modules/,
          /app/
        ],
        use: {
          loader: 'babel-loader'
        }
      },
      {
        test: /\.vue$/,
        loader: 'vue-loader-3'
      },
      {
        test: /\.(css|scss)$/,
        use: [
            'style-loader',
            'css-loader'
        ]
    },
      {
        test: /\.(eot|ttf|woff|woff2)(\?\S*)?$/,
        loader: 'file-loader'
      },
      {
        test: /\.(png|jpe?g|gif|webm|mp4|svg)$/,
        loader: 'file-loader',
        options: {
          name: '[name][contenthash:8].[ext]',
          outputPath: '/assets/img',
          esModule: false
        }
      }
    ]
  },
  plugins: [
    new VueLoaderPlugin(),
    new CleanWebpackPlugin(),
    new MiniCssExtractPlugin({
      filename: '[name].[contenthash:8].css',
      chunkFilename: '[name].[contenthash:8].css'
    }),
    new htmlWebpackPlugin({
      template: `${process.cwd()}/public/index.html`,
      filename: `${process.cwd()}/app/index.html`
    })
  ],
  optimization: {
    moduleIds: 'deterministic',
    runtimeChunk: 'single',
    splitChunks: {
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          priority: -10,
          chunks: 'all'
        }
      }
    }
  }
};

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83

# Sample Custom config

A sample custom config to place the built javascript assets inside js folder instead of the default scripts folder.

'use strict';

const { VueLoaderPlugin } = require('vue-loader-3');
const htmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
 
module.exports = {
  entry: {
    main: ['@babel/polyfill', `${process.cwd()}/src/main.js`]
  },
  output: {
    filename: '[name].[contenthash:8].js',
    path: `${process.cwd()}/app/scripts`,
    chunkFilename: '[name].[contenthash:8].js',
    publicPath: './js'
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: [
          /node_modules/,
          /app/
        ],
        use: {
          loader: 'babel-loader'
        }
      },
      {
        test: /\.vue$/,
        loader: 'vue-loader-3'
      },
      {
        test: /\.(css|scss)$/,
        use: [
            'style-loader',
            'css-loader'
        ]
    },
      {
        test: /\.(eot|ttf|woff|woff2)(\?\S*)?$/,
        loader: 'file-loader'
      },
      {
        test: /\.(png|jpe?g|gif|webm|mp4|svg)$/,
        loader: 'file-loader',
        options: {
          name: '[name][contenthash:8].[ext]',
          outputPath: '/assets/images',
          esModule: false
        }
      }
    ]
  },
  plugins: [
    new VueLoaderPlugin(),
    new CleanWebpackPlugin(),
    new MiniCssExtractPlugin({
      filename: '[name].[contenthash:8].css',
      chunkFilename: '[name].[contenthash:8].css'
    }),
    new htmlWebpackPlugin({
      template: `${process.cwd()}/public/index.html`,
      filename: `${process.cwd()}/app/index.html`
    })
  ],
  optimization: {
    moduleIds: 'deterministic',
    runtimeChunk: 'single',
    splitChunks: {
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          priority: -10,
          chunks: 'all'
        }
      }
    }
  }
};

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83