diff --git a/app/javascript/entrypoints/widget.js b/app/javascript/entrypoints/widget.js
index 5c1fac7a9..8ef1cdddf 100644
--- a/app/javascript/entrypoints/widget.js
+++ b/app/javascript/entrypoints/widget.js
@@ -10,6 +10,7 @@ import router from '../widget/router';
import { directive as onClickaway } from 'vue3-click-away';
import { domPurifyConfig } from '../shared/helpers/HTMLSanitizer';
import { plugin, defaultConfig } from '@formkit/vue';
+import { createPinia } from 'pinia';
import {
startsWithPlus,
@@ -21,9 +22,11 @@ const i18n = createI18n({
locale: 'en',
messages: i18nMessages,
});
+const pinia = createPinia();
const app = createApp(App);
app.use(i18n);
+app.use(pinia);
app.use(store);
app.use(router);
app.use(VueDOMPurifyHTML, domPurifyConfig);
diff --git a/app/javascript/widget/api/article.js b/app/javascript/widget/api/article.js
deleted file mode 100644
index 0c9d6675a..000000000
--- a/app/javascript/widget/api/article.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import endPoints from 'widget/api/endPoints';
-import { API } from 'widget/helpers/axios';
-
-export const getMostReadArticles = async (slug, locale) => {
- const urlData = endPoints.getMostReadArticles(slug, locale);
- return API.get(urlData.url, { params: urlData.params });
-};
diff --git a/app/javascript/widget/api/endPoints.js b/app/javascript/widget/api/endPoints.js
index e6ada5914..17e315800 100755
--- a/app/javascript/widget/api/endPoints.js
+++ b/app/javascript/widget/api/endPoints.js
@@ -97,15 +97,6 @@ const triggerCampaign = ({ websiteToken, campaignId, customAttributes }) => ({
},
});
-const getMostReadArticles = (slug, locale) => ({
- url: `/hc/${slug}/${locale}/articles.json`,
- params: {
- page: 1,
- sort: 'views',
- status: 1,
- },
-});
-
export default {
createConversation,
sendMessage,
@@ -115,5 +106,4 @@ export default {
getAvailableAgents,
getCampaigns,
triggerCampaign,
- getMostReadArticles,
};
diff --git a/app/javascript/widget/components/pageComponents/Home/Article/ArticleContainer.vue b/app/javascript/widget/components/pageComponents/Home/Article/ArticleContainer.vue
new file mode 100644
index 000000000..135a82b2d
--- /dev/null
+++ b/app/javascript/widget/components/pageComponents/Home/Article/ArticleContainer.vue
@@ -0,0 +1,74 @@
+
+
+
+
+
+
diff --git a/app/javascript/widget/store/index.js b/app/javascript/widget/store/index.js
index 22115d5b1..a63696bb6 100755
--- a/app/javascript/widget/store/index.js
+++ b/app/javascript/widget/store/index.js
@@ -10,7 +10,6 @@ import events from 'widget/store/modules/events';
import globalConfig from 'shared/store/globalConfig';
import message from 'widget/store/modules/message';
import campaign from 'widget/store/modules/campaign';
-import article from 'widget/store/modules/articles';
export default createStore({
modules: {
@@ -24,6 +23,5 @@ export default createStore({
globalConfig,
message,
campaign,
- article,
},
});
diff --git a/app/javascript/widget/store/modules/articles.js b/app/javascript/widget/store/modules/articles.js
deleted file mode 100644
index 0f858012b..000000000
--- a/app/javascript/widget/store/modules/articles.js
+++ /dev/null
@@ -1,55 +0,0 @@
-import { getMostReadArticles } from 'widget/api/article';
-
-const state = {
- records: [],
- uiFlags: {
- isError: false,
- hasFetched: false,
- isFetching: false,
- },
-};
-
-export const getters = {
- uiFlags: $state => $state.uiFlags,
- popularArticles: $state => $state.records,
-};
-
-export const actions = {
- fetch: async ({ commit }, { slug, locale }) => {
- commit('setIsFetching', true);
- commit('setError', false);
-
- try {
- const { data } = await getMostReadArticles(slug, locale);
- const { payload = [] } = data;
-
- if (payload.length) {
- commit('setArticles', payload);
- }
- } catch (error) {
- commit('setError', true);
- } finally {
- commit('setIsFetching', false);
- }
- },
-};
-
-export const mutations = {
- setArticles($state, data) {
- $state.records = data;
- },
- setError($state, value) {
- $state.uiFlags.isError = value;
- },
- setIsFetching($state, value) {
- $state.uiFlags.isFetching = value;
- },
-};
-
-export default {
- namespaced: true,
- state,
- getters,
- actions,
- mutations,
-};
diff --git a/app/javascript/widget/stores/api/articleAPI.js b/app/javascript/widget/stores/api/articleAPI.js
new file mode 100644
index 000000000..597d0785a
--- /dev/null
+++ b/app/javascript/widget/stores/api/articleAPI.js
@@ -0,0 +1,13 @@
+import { API } from 'widget/helpers/axios';
+
+export default {
+ index: params => {
+ const url = `/hc/${params.slug}/${params.locale}/articles.json`;
+ const urlParams = {
+ page: 1,
+ sort: 'views',
+ status: 1,
+ };
+ return API.get(url, { params: urlParams });
+ },
+};
diff --git a/app/javascript/widget/stores/articleStore.js b/app/javascript/widget/stores/articleStore.js
new file mode 100644
index 000000000..3c4ea63d6
--- /dev/null
+++ b/app/javascript/widget/stores/articleStore.js
@@ -0,0 +1,6 @@
+import articleAPI from './api/articleAPI';
+import { createResourceStore } from './piniaStoreFactory';
+
+export const useArticleStore = createResourceStore('articles', {
+ api: articleAPI,
+});
diff --git a/app/javascript/widget/stores/piniaStoreFactory.js b/app/javascript/widget/stores/piniaStoreFactory.js
new file mode 100644
index 000000000..d93ab00d7
--- /dev/null
+++ b/app/javascript/widget/stores/piniaStoreFactory.js
@@ -0,0 +1,105 @@
+import { defineStore } from 'pinia';
+
+export const createResourceStore = (
+ storeName,
+ { api, customGetters = {}, customActions = {} }
+) => {
+ return defineStore(storeName, {
+ state: () => ({
+ records: [],
+ uiFlags: {
+ isFetching: false,
+ isCreating: false,
+ isUpdating: false,
+ isDeleting: false,
+ },
+ error: null,
+ }),
+
+ getters: {
+ getRecords: state => state.records,
+ getUIFlags: state => state.uiFlags,
+ getError: state => state.error,
+ ...customGetters,
+ },
+
+ actions: {
+ async index(params = {}) {
+ this.uiFlags.isFetching = true;
+ this.error = null;
+
+ try {
+ const {
+ data: { payload = [] },
+ } = await api.index(params);
+ this.records = [...payload];
+ } catch (error) {
+ this.error = error;
+ throw error;
+ } finally {
+ this.uiFlags.isFetching = false;
+ }
+ },
+
+ async create(payload) {
+ this.uiFlags.isCreating = true;
+ this.error = null;
+
+ try {
+ const { data } = await api.post(payload);
+ this.records.push(data.payload);
+ return data.payload;
+ } catch (error) {
+ this.error = error;
+ throw error;
+ } finally {
+ this.uiFlags.isCreating = false;
+ }
+ },
+
+ async update(id, payload) {
+ this.uiFlags.isUpdating = true;
+ this.error = null;
+
+ try {
+ const { data } = await api.put(id, payload);
+ const index = this.records.findIndex(record => record.id === id);
+ if (index !== -1) {
+ this.records[index] = data.payload;
+ }
+ return data.payload;
+ } catch (error) {
+ this.error = error;
+ throw error;
+ } finally {
+ this.uiFlags.isUpdating = false;
+ }
+ },
+
+ async delete(id) {
+ this.uiFlags.isDeleting = true;
+ this.error = null;
+
+ try {
+ await api.delete(id);
+ this.records = this.records.filter(record => record.id !== id);
+ } catch (error) {
+ this.error = error;
+ throw error;
+ } finally {
+ this.uiFlags.isDeleting = false;
+ }
+ },
+
+ reset() {
+ this.records = [];
+ this.error = null;
+ Object.keys(this.uiFlags).forEach(key => {
+ this.uiFlags[key] = false;
+ });
+ },
+
+ ...customActions,
+ },
+ });
+};
diff --git a/app/javascript/widget/views/Home.vue b/app/javascript/widget/views/Home.vue
index d02e3f8d3..ff0703a2a 100755
--- a/app/javascript/widget/views/Home.vue
+++ b/app/javascript/widget/views/Home.vue
@@ -1,62 +1,26 @@
@@ -95,21 +41,6 @@ export default {
@start-conversation="startConversation"
/>
-
+
diff --git a/package.json b/package.json
index 10ee2a88a..63dcb3423 100644
--- a/package.json
+++ b/package.json
@@ -78,6 +78,7 @@
"md5": "^2.3.0",
"mitt": "^3.0.1",
"opus-recorder": "^8.0.5",
+ "pinia": "^2.3.0",
"semver": "7.6.3",
"snakecase-keys": "^8.0.1",
"timezone-phone-codes": "^0.0.2",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 7d2059e42..6e868b601 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -157,6 +157,9 @@ importers:
opus-recorder:
specifier: ^8.0.5
version: 8.0.5
+ pinia:
+ specifier: ^2.3.0
+ version: 2.3.0(typescript@5.6.2)(vue@3.5.12(typescript@5.6.2))
semver:
specifier: 7.6.3
version: 7.6.3
@@ -3927,15 +3930,12 @@ packages:
resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==}
engines: {node: '>=0.10.0'}
- pinia@2.2.6:
- resolution: {integrity: sha512-vIsR8JkDN5Ga2vAxqOE2cJj4VtsHnzpR1Fz30kClxlh0yCHfec6uoMeM3e/ddqmwFUejK3NlrcQa/shnpyT4hA==}
+ pinia@2.3.0:
+ resolution: {integrity: sha512-ohZj3jla0LL0OH5PlLTDMzqKiVw2XARmC1XYLdLWIPBMdhDW/123ZWr4zVAhtJm+aoSkFa13pYXskAvAscIkhQ==}
peerDependencies:
- '@vue/composition-api': ^1.4.0
typescript: '>=4.4.4'
- vue: ^2.6.14 || ^3.5.11
+ vue: ^2.7.0 || ^3.5.11
peerDependenciesMeta:
- '@vue/composition-api':
- optional: true
typescript:
optional: true
@@ -5262,7 +5262,7 @@ snapshots:
clsx: 2.1.1
date-fns: 3.6.0
lucide-vue-next: 0.394.0(vue@3.5.12(typescript@5.6.2))
- pinia: 2.2.6(typescript@5.6.2)(vue@3.5.12(typescript@5.6.2))
+ pinia: 2.3.0(typescript@5.6.2)(vue@3.5.12(typescript@5.6.2))
radix-vue: 1.9.9(vue@3.5.12(typescript@5.6.2))
secure-ls: 2.0.0
tailwind-merge: 2.5.4
@@ -9317,13 +9317,15 @@ snapshots:
pify@2.3.0: {}
- pinia@2.2.6(typescript@5.6.2)(vue@3.5.12(typescript@5.6.2)):
+ pinia@2.3.0(typescript@5.6.2)(vue@3.5.12(typescript@5.6.2)):
dependencies:
'@vue/devtools-api': 6.6.4
vue: 3.5.12(typescript@5.6.2)
vue-demi: 0.14.10(vue@3.5.12(typescript@5.6.2))
optionalDependencies:
typescript: 5.6.2
+ transitivePeerDependencies:
+ - '@vue/composition-api'
pirates@4.0.6: {}