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:
@@ -0,0 +1,181 @@
|
||||
import { MESSAGE_TYPE } from 'shared/constants/messages';
|
||||
import { applyPageFilters, applyRoleFilter, sortComparator } from './helpers';
|
||||
import filterQueryGenerator from 'dashboard/helper/filterQueryGenerator';
|
||||
import { matchesFilters } from './helpers/filterHelpers';
|
||||
import {
|
||||
getUserPermissions,
|
||||
getUserRole,
|
||||
} from '../../../helper/permissionsHelper';
|
||||
import camelcaseKeys from 'camelcase-keys';
|
||||
|
||||
export const getSelectedChatConversation = ({
|
||||
allConversations,
|
||||
selectedChatId,
|
||||
}) =>
|
||||
allConversations.filter(conversation => conversation.id === selectedChatId);
|
||||
|
||||
const getters = {
|
||||
getAllConversations: ({ allConversations, chatSortFilter: sortKey }) => {
|
||||
return allConversations.sort((a, b) => sortComparator(a, b, sortKey));
|
||||
},
|
||||
getFilteredConversations: (
|
||||
{ allConversations, chatSortFilter, appliedFilters },
|
||||
_,
|
||||
__,
|
||||
rootGetters
|
||||
) => {
|
||||
const currentUser = rootGetters.getCurrentUser;
|
||||
const currentUserId = rootGetters.getCurrentUser.id;
|
||||
const currentAccountId = rootGetters.getCurrentAccountId;
|
||||
|
||||
const permissions = getUserPermissions(currentUser, currentAccountId);
|
||||
const userRole = getUserRole(currentUser, currentAccountId);
|
||||
|
||||
return allConversations
|
||||
.filter(conversation => {
|
||||
const matchesFilterResult = matchesFilters(
|
||||
conversation,
|
||||
appliedFilters
|
||||
);
|
||||
const allowedForRole = applyRoleFilter(
|
||||
conversation,
|
||||
userRole,
|
||||
permissions,
|
||||
currentUserId
|
||||
);
|
||||
|
||||
return matchesFilterResult && allowedForRole;
|
||||
})
|
||||
.sort((a, b) => sortComparator(a, b, chatSortFilter));
|
||||
},
|
||||
getSelectedChat: ({ selectedChatId, allConversations }) => {
|
||||
const selectedChat = allConversations.find(
|
||||
conversation => conversation.id === selectedChatId
|
||||
);
|
||||
return selectedChat || {};
|
||||
},
|
||||
getSelectedChatAttachments: ({ selectedChatId, attachments }) => {
|
||||
return attachments[selectedChatId] || [];
|
||||
},
|
||||
getSelectedChatAttachmentsLoaded: ({ selectedChatId, attachments }) =>
|
||||
selectedChatId !== null && attachments[selectedChatId] !== undefined,
|
||||
getChatListFilters: ({ conversationFilters }) => conversationFilters,
|
||||
getLastEmailInSelectedChat: (stage, _getters) => {
|
||||
const selectedChat = _getters.getSelectedChat;
|
||||
const { messages = [] } = selectedChat;
|
||||
const lastEmail = [...messages].reverse().find(message => {
|
||||
const { message_type: messageType } = message;
|
||||
if (message.private) return false;
|
||||
|
||||
return [MESSAGE_TYPE.OUTGOING, MESSAGE_TYPE.INCOMING].includes(
|
||||
messageType
|
||||
);
|
||||
});
|
||||
|
||||
return lastEmail;
|
||||
},
|
||||
getMineChats: (_state, _, __, rootGetters) => activeFilters => {
|
||||
const currentUserID = rootGetters.getCurrentUser?.id;
|
||||
|
||||
return _state.allConversations.filter(conversation => {
|
||||
const { assignee } = conversation.meta;
|
||||
const isAssignedToMe = assignee && assignee.id === currentUserID;
|
||||
const shouldFilter = applyPageFilters(conversation, activeFilters);
|
||||
const isChatMine = isAssignedToMe && shouldFilter;
|
||||
|
||||
return isChatMine;
|
||||
});
|
||||
},
|
||||
getAppliedConversationFiltersV2: _state => {
|
||||
// TODO: Replace existing one with V2 after migrating the filters to use camelcase
|
||||
return _state.appliedFilters.map(camelcaseKeys);
|
||||
},
|
||||
getAppliedConversationFilters: _state => {
|
||||
return _state.appliedFilters;
|
||||
},
|
||||
getAppliedConversationFiltersQuery: _state => {
|
||||
const hasAppliedFilters = _state.appliedFilters.length !== 0;
|
||||
return hasAppliedFilters ? filterQueryGenerator(_state.appliedFilters) : [];
|
||||
},
|
||||
getUnAssignedChats: _state => activeFilters => {
|
||||
return _state.allConversations.filter(conversation => {
|
||||
const isUnAssigned = !conversation.meta.assignee;
|
||||
const shouldFilter = applyPageFilters(conversation, activeFilters);
|
||||
return isUnAssigned && shouldFilter;
|
||||
});
|
||||
},
|
||||
getParticipatingChats: (_state, _, __, rootGetters) => activeFilters => {
|
||||
const currentUserId = rootGetters.getCurrentUser?.id;
|
||||
const getWatchers = rootGetters['conversationWatchers/getByConversationId'];
|
||||
return _state.allConversations.filter(conversation => {
|
||||
const watchers = getWatchers(conversation.id);
|
||||
// Watchers are only loaded for the conversation open in the detail
|
||||
// panel. If loaded and current user is not in them, filter it out.
|
||||
if (watchers && !watchers.some(w => w.id === currentUserId)) {
|
||||
return false;
|
||||
}
|
||||
return applyPageFilters(conversation, activeFilters);
|
||||
});
|
||||
},
|
||||
getAllStatusChats: (_state, _, __, rootGetters) => activeFilters => {
|
||||
const currentUser = rootGetters.getCurrentUser;
|
||||
const currentUserId = rootGetters.getCurrentUser.id;
|
||||
const currentAccountId = rootGetters.getCurrentAccountId;
|
||||
|
||||
const permissions = getUserPermissions(currentUser, currentAccountId);
|
||||
const userRole = getUserRole(currentUser, currentAccountId);
|
||||
|
||||
return _state.allConversations.filter(conversation => {
|
||||
const shouldFilter = applyPageFilters(conversation, activeFilters);
|
||||
const allowedForRole = applyRoleFilter(
|
||||
conversation,
|
||||
userRole,
|
||||
permissions,
|
||||
currentUserId
|
||||
);
|
||||
|
||||
return shouldFilter && allowedForRole;
|
||||
});
|
||||
},
|
||||
getChatListLoadingStatus: ({ listLoadingStatus }) => listLoadingStatus,
|
||||
getAllMessagesLoaded(_state) {
|
||||
const [chat] = getSelectedChatConversation(_state);
|
||||
return !chat || chat.allMessagesLoaded === undefined
|
||||
? false
|
||||
: chat.allMessagesLoaded;
|
||||
},
|
||||
getUnreadCount(_state) {
|
||||
const [chat] = getSelectedChatConversation(_state);
|
||||
if (!chat) return [];
|
||||
return chat.messages.filter(
|
||||
chatMessage =>
|
||||
chatMessage.created_at * 1000 > chat.agent_last_seen_at * 1000 &&
|
||||
chatMessage.message_type === 0 &&
|
||||
chatMessage.private !== true
|
||||
).length;
|
||||
},
|
||||
getChatStatusFilter: ({ chatStatusFilter }) => chatStatusFilter,
|
||||
getChatSortFilter: ({ chatSortFilter }) => chatSortFilter,
|
||||
getSelectedInbox: ({ currentInbox }) => currentInbox,
|
||||
getConversationById: _state => conversationId => {
|
||||
return _state.allConversations.find(
|
||||
value => value.id === Number(conversationId)
|
||||
);
|
||||
},
|
||||
getConversationParticipants: _state => {
|
||||
return _state.conversationParticipants;
|
||||
},
|
||||
getConversationLastSeen: _state => {
|
||||
return _state.conversationLastSeen;
|
||||
},
|
||||
|
||||
getContextMenuChatId: _state => {
|
||||
return _state.contextMenuChatId;
|
||||
},
|
||||
|
||||
getCopilotAssistant: _state => {
|
||||
return _state.copilotAssistant;
|
||||
},
|
||||
};
|
||||
|
||||
export default getters;
|
||||
Reference in New Issue
Block a user