Vendor Chatwoot Vue 3 frontend into frontend/

Copy the Chatwoot (v4.14.0) frontend runnable subset into frontend/ for
customization:
- app/javascript/ (Vue SPA: dashboard, widget, sdk, portal, superadmin)
- app/views/ (ERB templates for vite-plugin-ruby entrypoint resolution)
- app/helpers/, app/assets/ (Rails view helpers, static assets)
- enterprise/ (Enterprise edition frontend overlay)
- config/vite.json, vite.config.ts, bin/vite (Vite-Rails toolchain)
- package.json, pnpm-lock.yaml, tailwind/postcss/eslint configs
- Gemfile, Gemfile.lock (vite_rails gem for bin/vite binstub)

Excluded Rails backend: controllers, models, services, jobs, mailers,
policies, db, lib, spec, public, node_modules.

Update references to the new frontend location:
- .gitignore: exclude frontend build artifacts (node_modules, tmp, packs),
  keep frontend/bin/ and frontend/vendor/ via negation
- backend/scripts/parity_frontend_smoke.sh: CHATWOOT_DIR default
  reference/chatwoot -> ../frontend
- backend/scripts/parity_frontend_browser_smoke.mjs: same default update
- AGENTS.md: add frontend section with Rails/Vite coupling notes
- README: architecture tree includes frontend/
This commit is contained in:
2026-07-07 14:56:01 +08:00
parent aeddedf2a3
commit 321c61aaae
5737 changed files with 945861 additions and 3 deletions
@@ -0,0 +1,41 @@
import { actions } from '../../appConfig';
const commit = vi.fn();
describe('#actions', () => {
describe('#setReferrerHost', () => {
it('creates actions properly', () => {
actions.setReferrerHost({ commit }, 'www.chatwoot.com');
expect(commit.mock.calls).toEqual([
['SET_REFERRER_HOST', 'www.chatwoot.com'],
]);
});
});
describe('#setBubbleVisibility', () => {
it('creates actions properly', () => {
actions.setBubbleVisibility({ commit }, false);
expect(commit.mock.calls).toEqual([['SET_BUBBLE_VISIBILITY', false]]);
});
});
describe('#setWidgetColor', () => {
it('creates actions properly', () => {
actions.setWidgetColor({ commit }, '#eaeaea');
expect(commit.mock.calls).toEqual([['SET_WIDGET_COLOR', '#eaeaea']]);
});
});
describe('#setColorScheme', () => {
it('creates actions for dark mode properly', () => {
actions.setColorScheme({ commit }, 'dark');
expect(commit.mock.calls).toEqual([['SET_COLOR_SCHEME', 'dark']]);
});
});
describe('#setRouteTransitionState', () => {
it('creates actions properly', () => {
actions.setRouteTransitionState({ commit }, false);
expect(commit.mock.calls).toEqual([['SET_ROUTE_UPDATE_STATE', false]]);
});
});
});
@@ -0,0 +1,70 @@
import { getters } from '../../appConfig';
describe('#getters', () => {
describe('#getWidgetColor', () => {
it('returns correct value', () => {
const state = { widgetColor: '#00bcd4' };
expect(getters.getWidgetColor(state)).toEqual('#00bcd4');
});
});
describe('#getReferrerHost', () => {
it('returns correct value', () => {
const state = { referrerHost: 'www.chatwoot.com' };
expect(getters.getReferrerHost(state)).toEqual('www.chatwoot.com');
});
});
describe('#getShowUnreadMessagesDialog', () => {
it('returns correct value', () => {
const state = { showUnreadMessagesDialog: true };
expect(getters.getShowUnreadMessagesDialog(state)).toEqual(true);
});
});
describe('#getAvailableMessage', () => {
it('returns correct value', () => {
const state = { availableMessage: 'We reply quickly' };
expect(getters.getAvailableMessage(state)).toEqual('We reply quickly');
});
});
describe('#getWelcomeHeading', () => {
it('returns correct value', () => {
const state = { welcomeTitle: 'Hello!' };
expect(getters.getWelcomeHeading(state)).toEqual('Hello!');
});
});
describe('#getWelcomeTagline', () => {
it('returns correct value', () => {
const state = { welcomeDescription: 'Welcome to our site' };
expect(getters.getWelcomeTagline(state)).toEqual('Welcome to our site');
});
});
describe('#getShouldShowFilePicker', () => {
it('returns correct value', () => {
const state = { enableFileUpload: true };
expect(getters.getShouldShowFilePicker(state)).toEqual(true);
});
});
describe('#getShouldShowEmojiPicker', () => {
it('returns correct value', () => {
const state = { enableEmojiPicker: true };
expect(getters.getShouldShowEmojiPicker(state)).toEqual(true);
});
});
describe('#getCanUserEndConversation', () => {
it('returns correct value', () => {
const state = { enableEndConversation: true };
expect(getters.getCanUserEndConversation(state)).toEqual(true);
});
});
describe('#getUnavailableMessage', () => {
it('returns correct value', () => {
const state = { unavailableMessage: 'We are offline' };
expect(getters.getUnavailableMessage(state)).toEqual('We are offline');
});
});
describe('#getIsUpdatingRoute', () => {
it('returns correct value', () => {
const state = { isUpdatingRoute: true };
expect(getters.getIsUpdatingRoute(state)).toEqual(true);
});
});
});
@@ -0,0 +1,43 @@
import { mutations } from '../../appConfig';
describe('#mutations', () => {
describe('#SET_REFERRER_HOST', () => {
it('sets referrer host properly', () => {
const state = { referrerHost: '' };
mutations.SET_REFERRER_HOST(state, 'www.chatwoot.com');
expect(state.referrerHost).toEqual('www.chatwoot.com');
});
});
describe('#SET_BUBBLE_VISIBILITY', () => {
it('sets bubble visibility properly', () => {
const state = { hideMessageBubble: false };
mutations.SET_BUBBLE_VISIBILITY(state, true);
expect(state.hideMessageBubble).toEqual(true);
});
});
describe('#SET_WIDGET_COLOR', () => {
it('sets widget color properly', () => {
const state = { widgetColor: '' };
mutations.SET_WIDGET_COLOR(state, '#00bcd4');
expect(state.widgetColor).toEqual('#00bcd4');
});
});
describe('#SET_COLOR_SCHEME', () => {
it('sets dark mode properly', () => {
const state = { darkMode: 'light' };
mutations.SET_COLOR_SCHEME(state, 'dark');
expect(state.darkMode).toEqual('dark');
});
});
describe('#SET_ROUTE_UPDATE_STATE', () => {
it('sets dark mode properly', () => {
const state = { isUpdatingRoute: false };
mutations.SET_ROUTE_UPDATE_STATE(state, true);
expect(state.isUpdatingRoute).toEqual(true);
});
});
});