diff --git a/.circleci/config.yml b/.circleci/config.yml index 459faf1c8..99795db91 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -19,13 +19,19 @@ jobs: steps: - checkout - node/install: - node-version: '20.12' + node-version: '23.7' - node/install-pnpm - node/install-packages: pkg-manager: pnpm override-ci-command: pnpm i - run: node --version - run: pnpm --version + - run: + name: Add PostgreSQL repository and update + command: | + sudo sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt/ $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list' + wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add - + sudo apt-get update -y - run: name: Install System Dependencies @@ -34,7 +40,9 @@ jobs: DEBIAN_FRONTEND=noninteractive sudo apt-get install -y \ libpq-dev \ redis-server \ - postgresql \ + postgresql-common \ + postgresql-16 \ + postgresql-16-pgvector \ build-essential \ git \ curl \ @@ -65,16 +73,16 @@ jobs: libvips - run: - name: Install RVM and Ruby 3.3.3 + name: Install RVM and Ruby 3.4.4 command: | sudo apt-get install -y gpg gpg --keyserver hkp://keyserver.ubuntu.com --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB \curl -sSL https://get.rvm.io | bash -s stable echo 'source ~/.rvm/scripts/rvm' >> $BASH_ENV source ~/.rvm/scripts/rvm - rvm install "3.3.3" - rvm use 3.3.3 --default - gem install bundler + rvm install "3.4.4" + rvm use 3.4.4 --default + gem install bundler -v 2.5.16 - run: name: Install Application Dependencies diff --git a/.codeclimate.yml b/.codeclimate.yml index 5e16262da..c68251f32 100644 --- a/.codeclimate.yml +++ b/.codeclimate.yml @@ -51,9 +51,12 @@ exclude_patterns: - 'app/javascript/dashboard/routes/dashboard/settings/automation/constants.js' - 'app/javascript/dashboard/components/widgets/FilterInput/FilterOperatorTypes.js' - 'app/javascript/dashboard/routes/dashboard/settings/reports/constants.js' + - 'app/javascript/dashboard/store/captain/storeFactory.js' - 'app/javascript/dashboard/i18n/index.js' - 'app/javascript/widget/i18n/index.js' - 'app/javascript/survey/i18n/index.js' - 'app/javascript/shared/constants/locales.js' - 'app/javascript/dashboard/helper/specs/macrosFixtures.js' - 'app/javascript/dashboard/routes/dashboard/settings/macros/constants.js' + - '**/fixtures/**' + - '**/*/fixtures.js' diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 3fd4f1a31..9e8c36fdb 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -4,5 +4,15 @@ FROM ghcr.io/chatwoot/chatwoot_codespace:latest # Do the set up required for chatwoot app WORKDIR /workspace + +# Copy dependency files first for better caching +COPY package.json pnpm-lock.yaml ./ +COPY Gemfile Gemfile.lock ./ + +# Install dependencies (will be cached if files don't change) +RUN pnpm install --frozen-lockfile && \ + gem install bundler && \ + bundle install --jobs=$(nproc) + +# Copy source code after dependencies are installed COPY . /workspace -RUN yarn && gem install bundler && bundle install diff --git a/.devcontainer/Dockerfile.base b/.devcontainer/Dockerfile.base index fe31dc42e..2b74602b8 100644 --- a/.devcontainer/Dockerfile.base +++ b/.devcontainer/Dockerfile.base @@ -1,12 +1,16 @@ - -ARG VARIANT +ARG VARIANT="ubuntu-22.04" FROM mcr.microsoft.com/vscode/devcontainers/base:0-${VARIANT} +ENV DEBIAN_FRONTEND=noninteractive + ARG NODE_VERSION ARG RUBY_VERSION ARG USER_UID ARG USER_GID +ARG PNPM_VERSION="10.2.0" +ENV PNPM_VERSION ${PNPM_VERSION} +ENV RUBY_CONFIGURE_OPTS=--disable-install-doc # Update args in docker-compose.yaml to set the UID/GID of the "vscode" user. RUN if [ "$USER_GID" != "1000" ] || [ "$USER_UID" != "1000" ]; then \ @@ -15,61 +19,80 @@ RUN if [ "$USER_GID" != "1000" ] || [ "$USER_UID" != "1000" ]; then \ && chmod -R $USER_UID:$USER_GID /home/vscode; \ fi -RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ - && apt-get -y install --no-install-recommends \ - build-essential \ - libssl-dev \ - zlib1g-dev \ - gnupg2 \ - tar \ - tzdata \ - postgresql-client \ - libpq-dev \ - yarn \ - git \ - imagemagick \ - tmux \ - zsh \ - git-flow \ - npm \ - libyaml-dev +RUN NODE_MAJOR=$(echo $NODE_VERSION | cut -d. -f1) \ + && curl -fsSL https://deb.nodesource.com/setup_${NODE_MAJOR}.x | bash - \ + && apt-get update \ + && apt-get -y install --no-install-recommends \ + build-essential \ + libssl-dev \ + zlib1g-dev \ + gnupg \ + tar \ + tzdata \ + postgresql-client \ + libpq-dev \ + git \ + imagemagick \ + libyaml-dev \ + curl \ + ca-certificates \ + tmux \ + nodejs \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* -# Install rbenv and ruby -RUN git clone https://github.com/rbenv/rbenv.git ~/.rbenv \ +# Install rbenv and ruby for root user first +RUN git clone --depth 1 https://github.com/rbenv/rbenv.git ~/.rbenv \ && echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc \ && echo 'eval "$(rbenv init -)"' >> ~/.bashrc ENV PATH "/root/.rbenv/bin/:/root/.rbenv/shims/:$PATH" -RUN git clone https://github.com/rbenv/ruby-build.git && \ +RUN git clone --depth 1 https://github.com/rbenv/ruby-build.git && \ PREFIX=/usr/local ./ruby-build/install.sh RUN rbenv install $RUBY_VERSION && \ rbenv global $RUBY_VERSION && \ rbenv versions -# Install overmind +# Set up rbenv for vscode user +RUN su - vscode -c "git clone --depth 1 https://github.com/rbenv/rbenv.git ~/.rbenv" \ + && su - vscode -c "echo 'export PATH=\"\$HOME/.rbenv/bin:\$PATH\"' >> ~/.bashrc" \ + && su - vscode -c "echo 'eval \"\$(rbenv init -)\"' >> ~/.bashrc" \ + && su - vscode -c "PATH=\"/home/vscode/.rbenv/bin:\$PATH\" rbenv install $RUBY_VERSION" \ + && su - vscode -c "PATH=\"/home/vscode/.rbenv/bin:\$PATH\" rbenv global $RUBY_VERSION" + +# Install overmind and gh in single layer RUN curl -L https://github.com/DarthSim/overmind/releases/download/v2.1.0/overmind-v2.1.0-linux-amd64.gz > overmind.gz \ && gunzip overmind.gz \ - && sudo mv overmind /usr/local/bin \ - && chmod +x /usr/local/bin/overmind - - -# Install gh -RUN curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg \ - && echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null \ - && sudo apt update \ - && sudo apt install gh + && mv overmind /usr/local/bin \ + && chmod +x /usr/local/bin/overmind \ + && curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg \ + && echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | tee /etc/apt/sources.list.d/github-cli.list > /dev/null \ + && apt-get update \ + && apt-get install -y --no-install-recommends gh \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* # Do the set up required for chatwoot app WORKDIR /workspace -COPY . /workspace +RUN chown vscode:vscode /workspace -# set up ruby -COPY Gemfile Gemfile.lock ./ -RUN gem install bundler && bundle install +# set up node js and pnpm in single layer +RUN npm install -g pnpm@${PNPM_VERSION} \ + && npm cache clean --force -# set up node js -RUN npm install n -g && \ - n $NODE_VERSION -RUN npm install --global yarn -RUN yarn +# Switch to vscode user +USER vscode +ENV PATH="/home/vscode/.rbenv/bin:/home/vscode/.rbenv/shims:$PATH" + +# Copy dependency files first for better caching +COPY --chown=vscode:vscode Gemfile Gemfile.lock package.json pnpm-lock.yaml ./ + +# Install dependencies as vscode user +RUN eval "$(rbenv init -)" \ + && gem install bundler -N \ + && bundle install --jobs=$(nproc) \ + && pnpm install --frozen-lockfile + +# Copy source code after dependencies are installed +COPY --chown=vscode:vscode . /workspace diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index d2dac356b..c51338b51 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -23,15 +23,15 @@ // 5432 postgres // 6379 redis // 1025,8025 mailhog - "forwardPorts": [8025, 3000, 3035], + "forwardPorts": [8025, 3000, 3036], - "postCreateCommand": ".devcontainer/scripts/setup.sh && POSTGRES_STATEMENT_TIMEOUT=600s bundle exec rake db:chatwoot_prepare && yarn", + "postCreateCommand": ".devcontainer/scripts/setup.sh && POSTGRES_STATEMENT_TIMEOUT=600s bundle exec rake db:chatwoot_prepare && pnpm install", "portsAttributes": { "3000": { "label": "Rails Server" }, - "3035": { - "label": "Webpack Dev Server" + "3036": { + "label": "Vite Dev Server" }, "8025": { "label": "Mailhog UI" diff --git a/.devcontainer/docker-compose.base.yml b/.devcontainer/docker-compose.base.yml new file mode 100644 index 000000000..6932b5f10 --- /dev/null +++ b/.devcontainer/docker-compose.base.yml @@ -0,0 +1,18 @@ +# Docker Compose file for building the base image in GitHub Actions +# Usage: docker-compose -f .devcontainer/docker-compose.base.yml build base + +version: '3' + +services: + base: + build: + context: .. + dockerfile: .devcontainer/Dockerfile.base + args: + VARIANT: 'ubuntu-22.04' + NODE_VERSION: '23.7.0' + RUBY_VERSION: '3.4.4' + # On Linux, you may need to update USER_UID and USER_GID below if not your local UID is not 1000. + USER_UID: '1000' + USER_GID: '1000' + image: ghcr.io/chatwoot/chatwoot_codespace:latest diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml index a804bb15c..a9185ea09 100644 --- a/.devcontainer/docker-compose.yml +++ b/.devcontainer/docker-compose.yml @@ -5,30 +5,17 @@ version: '3' services: - base: - build: - context: .. - dockerfile: .devcontainer/Dockerfile.base - args: - VARIANT: "ubuntu-22.04" - NODE_VERSION: "20.9.0" - RUBY_VERSION: "3.3.3" - # On Linux, you may need to update USER_UID and USER_GID below if not your local UID is not 1000. - USER_UID: "1000" - USER_GID: "1000" - image: base:latest - app: build: context: .. dockerfile: .devcontainer/Dockerfile args: - VARIANT: "ubuntu-22.04" - NODE_VERSION: "20.9.0" - RUBY_VERSION: "3.3.3" + VARIANT: 'ubuntu-22.04' + NODE_VERSION: '23.7.0' + RUBY_VERSION: '3.4.4' # On Linux, you may need to update USER_UID and USER_GID below if not your local UID is not 1000. - USER_UID: "1000" - USER_GID: "1000" + USER_UID: '1000' + USER_GID: '1000' volumes: - ..:/workspace:cached @@ -40,7 +27,7 @@ services: network_mode: service:db db: - image: postgres:latest + image: pgvector/pgvector:pg16 restart: unless-stopped volumes: - postgres-data:/var/lib/postgresql/data diff --git a/.devcontainer/scripts/setup.sh b/.devcontainer/scripts/setup.sh index 4ffee2d3a..6beb2ff57 100755 --- a/.devcontainer/scripts/setup.sh +++ b/.devcontainer/scripts/setup.sh @@ -2,12 +2,7 @@ cp .env.example .env sed -i -e '/REDIS_URL/ s/=.*/=redis:\/\/localhost:6379/' .env sed -i -e '/POSTGRES_HOST/ s/=.*/=localhost/' .env sed -i -e '/SMTP_ADDRESS/ s/=.*/=localhost/' .env -sed -i -e "/FRONTEND_URL/ s/=.*/=https:\/\/$CODESPACE_NAME-3000.githubpreview.dev/" .env -sed -i -e "/WEBPACKER_DEV_SERVER_PUBLIC/ s/=.*/=https:\/\/$CODESPACE_NAME-3035.githubpreview.dev/" .env -# uncomment the webpacker env variable -sed -i -e '/WEBPACKER_DEV_SERVER_PUBLIC/s/^# //' .env -# fix the error with webpacker -echo 'export NODE_OPTIONS=--openssl-legacy-provider' >> ~/.zshrc +sed -i -e "/FRONTEND_URL/ s/=.*/=https:\/\/$CODESPACE_NAME-3000.app.github.dev/" .env # codespaces make the ports public -gh codespace ports visibility 3000:public 3035:public 8025:public -c $CODESPACE_NAME +gh codespace ports visibility 3000:public 3036:public 8025:public -c $CODESPACE_NAME diff --git a/.env.example b/.env.example index b7ba0920d..2ab2933dc 100644 --- a/.env.example +++ b/.env.example @@ -2,7 +2,7 @@ # https://www.chatwoot.com/docs/self-hosted/configuration/environment-variables/#rails-production-variables # Used to verify the integrity of signed cookies. so ensure a secure value is set -# SECRET_KEY_BASE should be alphanumeric. Avoid special characters or symbols. +# SECRET_KEY_BASE should be alphanumeric. Avoid special characters or symbols. # Use `rake secret` to generate this variable SECRET_KEY_BASE=replace_with_lengthy_secure_hex @@ -216,6 +216,8 @@ ANDROID_SHA256_CERT_FINGERPRINT=AC:73:8E:DE:EB:56:EA:CC:10:87:02:A7:65:37:7B:38: # ENABLE_RACK_ATTACK=true # RACK_ATTACK_LIMIT=300 # ENABLE_RACK_ATTACK_WIDGET_API=true +# Comma-separated list of trusted IPs that bypass Rack Attack throttling rules +# RACK_ATTACK_ALLOWED_IPS=127.0.0.1,::1,192.168.0.10 ## Running chatwoot as an API only server ## setting this value to true will disable the frontend dashboard endpoints @@ -257,4 +259,3 @@ AZURE_APP_SECRET= # Set to true if you want to remove stale contact inboxes # contact_inboxes with no conversation older than 90 days will be removed # REMOVE_STALE_CONTACT_INBOX_JOB_STATUS=false - diff --git a/.eslintrc.js b/.eslintrc.js index a2932b2a9..6c867f557 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -4,6 +4,8 @@ module.exports = { 'prettier', 'plugin:vue/vue3-recommended', 'plugin:vitest-globals/recommended', + // use recommended-legacy when upgrading the plugin to v4 + 'plugin:@intlify/vue-i18n/recommended', ], overrides: [ { @@ -229,6 +231,18 @@ module.exports = { 'vue/singleline-html-element-content-newline': 'off', 'import/extensions': ['off'], 'no-console': 'error', + '@intlify/vue-i18n/no-dynamic-keys': 'warn', + '@intlify/vue-i18n/no-unused-keys': [ + 'warn', + { + extensions: ['.js', '.vue'], + }, + ], + }, + settings: { + 'vue-i18n': { + localeDir: './app/javascript/*/i18n/**.json', + }, }, env: { browser: true, diff --git a/.github/dashboard-screen.png b/.github/dashboard-screen.png deleted file mode 100644 index 847ae1582..000000000 Binary files a/.github/dashboard-screen.png and /dev/null differ diff --git a/.github/screenshots/dashboard-dark.png b/.github/screenshots/dashboard-dark.png new file mode 100644 index 000000000..4d08b52b9 Binary files /dev/null and b/.github/screenshots/dashboard-dark.png differ diff --git a/.github/screenshots/dashboard.png b/.github/screenshots/dashboard.png new file mode 100644 index 000000000..b8b99be49 Binary files /dev/null and b/.github/screenshots/dashboard.png differ diff --git a/.github/screenshots/header-dark.png b/.github/screenshots/header-dark.png new file mode 100644 index 000000000..84931aee3 Binary files /dev/null and b/.github/screenshots/header-dark.png differ diff --git a/.github/screenshots/header.png b/.github/screenshots/header.png new file mode 100644 index 000000000..f10ca0faf Binary files /dev/null and b/.github/screenshots/header.png differ diff --git a/.github/workflows/frontend-fe.yml b/.github/workflows/frontend-fe.yml index 15bb6f5e9..45ff25203 100644 --- a/.github/workflows/frontend-fe.yml +++ b/.github/workflows/frontend-fe.yml @@ -10,7 +10,7 @@ on: jobs: test: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 @@ -23,12 +23,10 @@ jobs: bundler-cache: true - uses: pnpm/action-setup@v4 - with: - version: 9.3.0 - uses: actions/setup-node@v4 with: - node-version: 20 + node-version: 23 cache: 'pnpm' - name: Install pnpm dependencies diff --git a/.github/workflows/nightly_installer.yml b/.github/workflows/nightly_installer.yml index d11fe6401..beef5727c 100644 --- a/.github/workflows/nightly_installer.yml +++ b/.github/workflows/nightly_installer.yml @@ -2,7 +2,7 @@ # # # # Linux nightly installer action # # This action will try to install and setup -# # chatwoot on an Ubuntu 20.04 machine using +# # chatwoot on an Ubuntu 22.04 machine using # # the linux installer script. # # # # This is set to run daily at midnight. @@ -16,7 +16,7 @@ on: jobs: nightly: - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 steps: - name: get installer diff --git a/.github/workflows/publish_codespace_image.yml b/.github/workflows/publish_codespace_image.yml index 647608473..5da4fda05 100644 --- a/.github/workflows/publish_codespace_image.yml +++ b/.github/workflows/publish_codespace_image.yml @@ -19,6 +19,5 @@ jobs: - name: Build the Codespace Base Image run: | - docker-compose -f .devcontainer/docker-compose.yml build base - docker tag base:latest ghcr.io/chatwoot/chatwoot_codespace:latest + docker compose -f .devcontainer/docker-compose.base.yml build base docker push ghcr.io/chatwoot/chatwoot_codespace:latest diff --git a/.github/workflows/publish_ee_docker.yml b/.github/workflows/publish_ee_docker.yml new file mode 100644 index 000000000..8e2c22481 --- /dev/null +++ b/.github/workflows/publish_ee_docker.yml @@ -0,0 +1,140 @@ +# # +# # This action will publish Chatwoot EE docker image. +# # This is set to run against merges to develop, master +# # and when tags are created. +# # + +name: Publish Chatwoot EE docker images + +on: + push: + branches: + - develop + - master + tags: + - v* + workflow_dispatch: + +env: + DOCKER_REPO: chatwoot/chatwoot + +jobs: + build: + strategy: + fail-fast: false + matrix: + include: + - platform: linux/amd64 + runner: ubuntu-latest + - platform: linux/arm64 + runner: ubuntu-22.04-arm + runs-on: ${{ matrix.runner }} + env: + GIT_REF: ${{ github.head_ref || github.ref_name }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Prepare + run: | + platform=${{ matrix.platform }} + echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV + + - name: Set Chatwoot edition + run: | + echo -en '\nENV CW_EDITION="ee"' >> docker/Dockerfile + + - name: Set Docker Tags + run: | + SANITIZED_REF=$(echo "$GIT_REF" | sed 's/\//-/g') + if [ "${{ github.ref_name }}" = "master" ]; then + echo "DOCKER_TAG=${DOCKER_REPO}:latest" >> $GITHUB_ENV + else + echo "DOCKER_TAG=${DOCKER_REPO}:${SANITIZED_REF}" >> $GITHUB_ENV + fi + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to DockerHub + if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Build and push by digest + id: build + uses: docker/build-push-action@v6 + with: + context: . + file: docker/Dockerfile + platforms: ${{ matrix.platform }} + push: ${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' }} + outputs: type=image,name=${{ env.DOCKER_REPO }},push-by-digest=true,name-canonical=true,push=true + + - name: Export digest + run: | + mkdir -p ${{ runner.temp }}/digests + digest="${{ steps.build.outputs.digest }}" + touch "${{ runner.temp }}/digests/${digest#sha256:}" + + - name: Upload digest + uses: actions/upload-artifact@v4 + with: + name: digests-${{ env.PLATFORM_PAIR }} + path: ${{ runner.temp }}/digests/* + if-no-files-found: error + retention-days: 1 + + merge: + runs-on: ubuntu-latest + needs: + - build + steps: + - name: Download digests + uses: actions/download-artifact@v4 + with: + path: ${{ runner.temp }}/digests + pattern: digests-* + merge-multiple: true + + - name: Login to DockerHub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Create manifest list and push + working-directory: ${{ runner.temp }}/digests + env: + GIT_REF: ${{ github.head_ref || github.ref_name }} + run: | + SANITIZED_REF=$(echo "$GIT_REF" | sed 's/\//-/g') + if [ "${{ github.ref_name }}" = "master" ]; then + TAG="${DOCKER_REPO}:latest" + else + TAG="${DOCKER_REPO}:${SANITIZED_REF}" + fi + + docker buildx imagetools create -t $TAG \ + $(printf '${{ env.DOCKER_REPO }}@sha256:%s ' *) + + - name: Inspect image + env: + GIT_REF: ${{ github.head_ref || github.ref_name }} + run: | + SANITIZED_REF=$(echo "$GIT_REF" | sed 's/\//-/g') + if [ "${{ github.ref_name }}" = "master" ]; then + TAG="${DOCKER_REPO}:latest" + else + TAG="${DOCKER_REPO}:${SANITIZED_REF}" + fi + + docker buildx imagetools inspect $TAG diff --git a/.github/workflows/publish_foss_docker.yml b/.github/workflows/publish_foss_docker.yml index d48b82a58..3075a7f3d 100644 --- a/.github/workflows/publish_foss_docker.yml +++ b/.github/workflows/publish_foss_docker.yml @@ -5,6 +5,7 @@ # # name: Publish Chatwoot CE docker images + on: push: branches: @@ -14,20 +15,30 @@ on: - v* workflow_dispatch: +env: + DOCKER_REPO: chatwoot/chatwoot + jobs: build: - runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - platform: linux/amd64 + runner: ubuntu-latest + - platform: linux/arm64 + runner: ubuntu-22.04-arm + runs-on: ${{ matrix.runner }} env: - GIT_REF: ${{ github.head_ref || github.ref_name }} # ref_name to get tags/branches + GIT_REF: ${{ github.head_ref || github.ref_name }} steps: - name: Checkout uses: actions/checkout@v4 - - name: Set up QEMU - uses: docker/setup-qemu-action@v1 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 + - name: Prepare + run: | + platform=${{ matrix.platform }} + echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV - name: Strip enterprise code run: | @@ -38,26 +49,97 @@ jobs: run: | echo -en '\nENV CW_EDITION="ce"' >> docker/Dockerfile - - name: set docker tag + - name: Set Docker Tags run: | - echo "DOCKER_TAG=chatwoot/chatwoot:$GIT_REF-ce" >> $GITHUB_ENV + SANITIZED_REF=$(echo "$GIT_REF" | sed 's/\//-/g') + if [ "${{ github.ref_name }}" = "master" ]; then + echo "DOCKER_TAG=${DOCKER_REPO}:latest-ce" >> $GITHUB_ENV + else + echo "DOCKER_TAG=${DOCKER_REPO}:${SANITIZED_REF}-ce" >> $GITHUB_ENV + fi - - name: replace docker tag if master - if: github.ref_name == 'master' - run: | - echo "DOCKER_TAG=chatwoot/chatwoot:latest-ce" >> $GITHUB_ENV + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 - name: Login to DockerHub - uses: docker/login-action@v1 + if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Build and push - uses: docker/build-push-action@v2 + - name: Build and push by digest + id: build + uses: docker/build-push-action@v6 with: context: . file: docker/Dockerfile - platforms: linux/amd64 - push: true - tags: ${{ env.DOCKER_TAG }} + platforms: ${{ matrix.platform }} + push: ${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' }} + outputs: type=image,name=${{ env.DOCKER_REPO }},push-by-digest=true,name-canonical=true,push=true + + - name: Export digest + run: | + mkdir -p ${{ runner.temp }}/digests + digest="${{ steps.build.outputs.digest }}" + touch "${{ runner.temp }}/digests/${digest#sha256:}" + + - name: Upload digest + uses: actions/upload-artifact@v4 + with: + name: digests-${{ env.PLATFORM_PAIR }} + path: ${{ runner.temp }}/digests/* + if-no-files-found: error + retention-days: 1 + + merge: + runs-on: ubuntu-latest + needs: + - build + steps: + - name: Download digests + uses: actions/download-artifact@v4 + with: + path: ${{ runner.temp }}/digests + pattern: digests-* + merge-multiple: true + + - name: Login to DockerHub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Create manifest list and push + working-directory: ${{ runner.temp }}/digests + env: + GIT_REF: ${{ github.head_ref || github.ref_name }} + run: | + SANITIZED_REF=$(echo "$GIT_REF" | sed 's/\//-/g') + if [ "${{ github.ref_name }}" = "master" ]; then + TAG="${DOCKER_REPO}:latest-ce" + else + TAG="${DOCKER_REPO}:${SANITIZED_REF}-ce" + fi + + docker buildx imagetools create -t $TAG \ + $(printf '${{ env.DOCKER_REPO }}@sha256:%s ' *) + + - name: Inspect image + env: + GIT_REF: ${{ github.head_ref || github.ref_name }} + run: | + SANITIZED_REF=$(echo "$GIT_REF" | sed 's/\//-/g') + if [ "${{ github.ref_name }}" = "master" ]; then + TAG="${DOCKER_REPO}:latest-ce" + else + TAG="${DOCKER_REPO}:${SANITIZED_REF}-ce" + fi + + docker buildx imagetools inspect $TAG diff --git a/.github/workflows/run_foss_spec.yml b/.github/workflows/run_foss_spec.yml index 6a018b314..385feddfc 100644 --- a/.github/workflows/run_foss_spec.yml +++ b/.github/workflows/run_foss_spec.yml @@ -1,9 +1,3 @@ -# # -# # This action will strip the enterprise folder -# # and run the spec. -# # This is set to run against every PR. -# # - name: Run Chatwoot CE spec on: push: @@ -15,10 +9,10 @@ on: jobs: test: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 services: postgres: - image: postgres:15.3 + image: pgvector/pgvector:pg15 env: POSTGRES_USER: postgres POSTGRES_PASSWORD: '' @@ -44,7 +38,6 @@ jobs: - uses: actions/checkout@v4 - uses: pnpm/action-setup@v4 with: - version: 9 ref: ${{ github.event.pull_request.head.ref }} repository: ${{ github.event.pull_request.head.repo.full_name }} @@ -54,7 +47,7 @@ jobs: - uses: actions/setup-node@v4 with: - node-version: 20 + node-version: 23 cache: 'pnpm' - name: Install pnpm dependencies diff --git a/.github/workflows/run_response_bot_spec.yml b/.github/workflows/run_response_bot_spec.yml deleted file mode 100644 index c788a0400..000000000 --- a/.github/workflows/run_response_bot_spec.yml +++ /dev/null @@ -1,89 +0,0 @@ -# # -# # This workflow will run specs related to response bot -# # This can only be activated in installations Where vector extension is available. -# # - -name: Run Response Bot spec -on: - push: - branches: - - develop - - master - pull_request: - workflow_dispatch: - -jobs: - test: - runs-on: ubuntu-20.04 - services: - postgres: - image: ankane/pgvector - env: - POSTGRES_USER: postgres - POSTGRES_PASSWORD: "" - POSTGRES_DB: postgres - POSTGRES_HOST_AUTH_METHOD: trust - ports: - - 5432:5432 - # needed because the postgres container does not provide a healthcheck - # tmpfs makes DB faster by using RAM - options: >- - --mount type=tmpfs,destination=/var/lib/postgresql/data - --health-cmd pg_isready - --health-interval 10s - --health-timeout 5s - --health-retries 5 - redis: - image: redis - ports: - - 6379:6379 - options: --entrypoint redis-server - - steps: - - uses: actions/checkout@v4 - with: - ref: ${{ github.event.pull_request.head.ref }} - repository: ${{ github.event.pull_request.head.repo.full_name }} - - - uses: ruby/setup-ruby@v1 - with: - bundler-cache: true # runs 'bundle install' and caches installed gems automatically - - - uses: pnpm/action-setup@v2 - with: - version: 9.3.0 - - - uses: actions/setup-node@v4 - with: - node-version: 20 - cache: pnpm - - - name: pnpm - run: pnpm install - - - name: Create database - run: bundle exec rake db:create - - - name: Seed database - run: bundle exec rake db:schema:load - - - name: Enable ResponseBotService in installation - run: RAILS_ENV=test bundle exec rails runner "Features::ResponseBotService.new.enable_in_installation" - - # Run Response Bot specs - - name: Run backend tests - run: | - bundle exec rspec \ - spec/enterprise/controllers/api/v1/accounts/response_sources_controller_spec.rb \ - spec/enterprise/services/enterprise/message_templates/response_bot_service_spec.rb \ - spec/enterprise/controllers/enterprise/api/v1/accounts/inboxes_controller_spec.rb:47 \ - spec/enterprise/jobs/enterprise/account/conversations_resolution_scheduler_job_spec.rb \ - --profile=10 \ - --format documentation - - - name: Upload rails log folder - uses: actions/upload-artifact@v4 - if: always() - with: - name: rails-log-folder - path: log diff --git a/.github/workflows/size-limit.yml b/.github/workflows/size-limit.yml index 0758ca7d0..9be79da05 100644 --- a/.github/workflows/size-limit.yml +++ b/.github/workflows/size-limit.yml @@ -7,7 +7,7 @@ on: jobs: test: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 @@ -19,13 +19,11 @@ jobs: with: bundler-cache: true # runs 'bundle install' and caches installed gems automatically - - uses: pnpm/action-setup@v2 - with: - version: 9.3.0 + - uses: pnpm/action-setup@v4 - uses: actions/setup-node@v4 with: - node-version: 20 + node-version: 23 cache: 'pnpm' - name: pnpm @@ -39,7 +37,7 @@ jobs: - name: setup env run: | cp .env.example .env - + - name: Run asset compile run: bundle exec rake assets:precompile env: @@ -47,5 +45,3 @@ jobs: - name: Size Check run: pnpm run size - - diff --git a/.github/workflows/test_docker_build.yml b/.github/workflows/test_docker_build.yml new file mode 100644 index 000000000..460c4ba1f --- /dev/null +++ b/.github/workflows/test_docker_build.yml @@ -0,0 +1,40 @@ +name: Test Docker Build + +on: + pull_request: + branches: + - develop + - master + workflow_dispatch: + +jobs: + test-build: + strategy: + fail-fast: false + matrix: + include: + - platform: linux/amd64 + runner: ubuntu-latest + - platform: linux/arm64 + runner: ubuntu-22.04-arm + runs-on: ${{ matrix.runner }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build Docker image + uses: docker/build-push-action@v6 + with: + context: . + file: docker/Dockerfile + platforms: ${{ matrix.platform }} + push: false + load: false + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.gitignore b/.gitignore index 5eb883db0..c64fb5c1b 100644 --- a/.gitignore +++ b/.gitignore @@ -71,8 +71,6 @@ test/cypress/videos/* /config/master.key /config/*.enc -#ignore files under .vscode directory -.vscode # yalc for local testing .yalc @@ -90,3 +88,9 @@ yarn-debug.log* # Vite uses dotenv and suggests to ignore local-only env files. See # https://vitejs.dev/guide/env-and-mode.html#env-files *.local + + +# TextEditors & AI Agents config files +.vscode +.claude/settings.local.json +.cursor diff --git a/.husky/pre-commit b/.husky/pre-commit index 2525dde4e..b3aceacd6 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,11 +1,11 @@ -# #!/bin/sh -# . "$(dirname "$0")/_/husky.sh" +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" -# # lint js and vue files -# npx --no-install lint-staged +# lint js and vue files +npx --no-install lint-staged -# # lint only staged ruby files -# git diff --name-only --cached | xargs ls -1 2>/dev/null | grep '\.rb$' | xargs bundle exec rubocop --force-exclusion -a +# lint only staged ruby files that still exist (not deleted) +git diff --name-only --cached | xargs -I {} sh -c 'test -f "{}" && echo "{}"' | grep '\.rb$' | xargs -I {} bundle exec rubocop --force-exclusion -a "{}" || true -# # stage rubocop changes to files -# git diff --name-only --cached | xargs git add +# stage rubocop changes to files +git diff --name-only --cached | xargs -I {} sh -c 'test -f "{}" && git add "{}"' || true diff --git a/.rubocop.yml b/.rubocop.yml index 1cdfbc713..12e756af6 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,7 +1,10 @@ -require: +plugins: - rubocop-performance - rubocop-rails - rubocop-rspec + - rubocop-factory_bot + +require: - ./rubocop/use_from_email.rb - ./rubocop/custom_cop_location.rb @@ -13,44 +16,61 @@ Metrics/ClassLength: Exclude: - 'app/models/message.rb' - 'app/models/conversation.rb' + Metrics/MethodLength: Max: 19 + Exclude: + - 'enterprise/lib/captain/agent.rb' + RSpec/ExampleLength: Max: 25 + Style/Documentation: Enabled: false + Style/ExponentialNotation: Enabled: false + Style/FrozenStringLiteralComment: Enabled: false + Style/SymbolArray: Enabled: false + Style/OpenStructUse: Enabled: false + Style/OptionalBooleanParameter: Exclude: - 'app/services/email_templates/db_resolver_service.rb' - 'app/dispatchers/dispatcher.rb' + Style/GlobalVars: Exclude: - 'config/initializers/01_redis.rb' - 'config/initializers/rack_attack.rb' - 'lib/redis/alfred.rb' - 'lib/global_config.rb' + Style/ClassVars: Exclude: - 'app/services/email_templates/db_resolver_service.rb' + Lint/MissingSuper: Exclude: - 'app/drops/base_drop.rb' + Lint/SymbolConversion: Enabled: false + Lint/EmptyBlock: Exclude: - 'app/views/api/v1/accounts/conversations/toggle_status.json.jbuilder' + Lint/OrAssignmentToConstant: Exclude: - 'lib/redis/config.rb' + Metrics/BlockLength: Max: 30 Exclude: @@ -58,10 +78,16 @@ Metrics/BlockLength: - '**/routes.rb' - 'config/environments/*' - db/schema.rb + Metrics/ModuleLength: Exclude: - lib/seeders/message_seeder.rb - spec/support/slack_stubs.rb + +Rails/HelperInstanceVariable: + Exclude: + - enterprise/app/helpers/captain/chat_helper.rb + Rails/ApplicationController: Exclude: - 'app/controllers/api/v1/widget/messages_controller.rb' @@ -71,74 +97,101 @@ Rails/ApplicationController: - 'app/controllers/platform_controller.rb' - 'app/controllers/public_controller.rb' - 'app/controllers/survey/responses_controller.rb' + Rails/FindEach: Enabled: true Include: - 'app/**/*.rb' + Rails/CompactBlank: Enabled: false + Rails/EnvironmentVariableAccess: Enabled: false + Rails/TimeZoneAssignment: Enabled: false + Rails/RedundantPresenceValidationOnBelongsTo: Enabled: false + +Rails/InverseOf: + Exclude: + - enterprise/app/models/captain/assistant.rb + +Rails/UniqueValidationWithoutIndex: + Exclude: + - app/models/canned_response.rb + - app/models/telegram_bot.rb + - enterprise/app/models/captain_inbox.rb + - 'app/models/channel/twitter_profile.rb' + - 'app/models/webhook.rb' + - 'app/models/contact.rb' + Style/ClassAndModuleChildren: EnforcedStyle: compact Exclude: - 'config/application.rb' - 'config/initializers/monkey_patches/*' + Style/MapToHash: Enabled: false + Style/HashSyntax: Enabled: true EnforcedStyle: no_mixed_keys EnforcedShorthandSyntax: never + RSpec/NestedGroups: Enabled: true Max: 4 + RSpec/MessageSpies: Enabled: false + RSpec/StubbedMock: Enabled: false -RSpec/FactoryBot/SyntaxMethods: - Enabled: false + Naming/VariableNumber: Enabled: false + Naming/MemoizedInstanceVariableName: Exclude: - 'app/models/message.rb' + Style/GuardClause: Exclude: - 'app/builders/account_builder.rb' - 'app/models/attachment.rb' - 'app/models/message.rb' + Metrics/AbcSize: Max: 26 Exclude: - 'app/controllers/concerns/auth_helper.rb' -Rails/UniqueValidationWithoutIndex: - Exclude: - - 'app/models/channel/twitter_profile.rb' - - 'app/models/webhook.rb' - - 'app/models/contact.rb' + - 'app/models/integrations/hook.rb' - 'app/models/canned_response.rb' - 'app/models/telegram_bot.rb' + Rails/RenderInline: Exclude: - 'app/controllers/swagger_controller.rb' + Rails/ThreeStateBooleanColumn: Exclude: - 'db/migrate/20230503101201_create_sla_policies.rb' + RSpec/IndexedLet: Enabled: false + RSpec/NamedSubject: Enabled: false # we should bring this down RSpec/MultipleExpectations: Max: 7 + RSpec/MultipleMemoizedHelpers: Max: 14 @@ -166,3 +219,121 @@ AllCops: - 'tmp/**/*' - 'storage/**/*' - 'db/migrate/20230426130150_init_schema.rb' + +FactoryBot/SyntaxMethods: + Enabled: false + +# Disable new rules causing errors +Layout/LeadingCommentSpace: + Enabled: false + +Style/ReturnNilInPredicateMethodDefinition: + Enabled: false + +Style/RedundantParentheses: + Enabled: false + +Performance/StringIdentifierArgument: + Enabled: false + +Layout/EmptyLinesAroundExceptionHandlingKeywords: + Enabled: false + +Lint/LiteralAsCondition: + Enabled: false + +Style/RedundantReturn: + Enabled: false + +Layout/SpaceAroundOperators: + Enabled: false + +Rails/EnvLocal: + Enabled: false + +Rails/WhereRange: + Enabled: false + +Lint/UselessConstantScoping: + Enabled: false + +Style/MultipleComparison: + Enabled: false + +Bundler/OrderedGems: + Enabled: false + +RSpec/ExampleWording: + Enabled: false + +RSpec/ReceiveMessages: + Enabled: false + +FactoryBot/AssociationStyle: + Enabled: false + +Rails/EnumSyntax: + Enabled: false + +Lint/RedundantTypeConversion: + Enabled: false + +# Additional rules to disable +Rails/RedundantActiveRecordAllMethod: + Enabled: false + +Layout/TrailingEmptyLines: + Enabled: false + +Style/SafeNavigationChainLength: + Enabled: false + +Lint/SafeNavigationConsistency: + Enabled: false + +Lint/CopDirectiveSyntax: + Enabled: false + +# Final set of rules to disable +FactoryBot/ExcessiveCreateList: + Enabled: false + +RSpec/MissingExpectationTargetMethod: + Enabled: false + +Performance/InefficientHashSearch: + Enabled: false + +Style/RedundantSelfAssignmentBranch: + Enabled: false + +Style/YAMLFileRead: + Enabled: false + +Layout/ExtraSpacing: + Enabled: false + +Style/RedundantFilterChain: + Enabled: false + +Performance/MapMethodChain: + Enabled: false + +Rails/RootPathnameMethods: + Enabled: false + +Style/SuperArguments: + Enabled: false + +# Final remaining rules to disable +Rails/Delegate: + Enabled: false + +Style/CaseLikeIf: + Enabled: false + +FactoryBot/RedundantFactoryOption: + Enabled: false + +FactoryBot/FactoryAssociationWithStrategy: + Enabled: false \ No newline at end of file diff --git a/.ruby-version b/.ruby-version index 619b53766..f9892605c 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -3.3.3 +3.4.4 diff --git a/.scss-lint.yml b/.scss-lint.yml index 1cc029441..2477dfffb 100644 --- a/.scss-lint.yml +++ b/.scss-lint.yml @@ -283,3 +283,4 @@ exclude: - 'app/javascript/widget/assets/scss/sdk.css' - 'app/assets/stylesheets/administrate/reset/_normalize.scss' - 'app/javascript/shared/assets/stylesheets/*.scss' + - 'app/javascript/dashboard/assets/scss/_woot.scss' diff --git a/.windsurf/rules/chatwoot.md b/.windsurf/rules/chatwoot.md new file mode 120000 index 000000000..b7e6491d3 --- /dev/null +++ b/.windsurf/rules/chatwoot.md @@ -0,0 +1 @@ +../../AGENTS.md \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000..ad374799c --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,58 @@ +# Chatwoot Development Guidelines + +## Build / Test / Lint + +- **Setup**: `bundle install && pnpm install` +- **Run Dev**: `pnpm dev` or `overmind start -f ./Procfile.dev` +- **Lint JS/Vue**: `pnpm eslint` / `pnpm eslint:fix` +- **Lint Ruby**: `bundle exec rubocop -a` +- **Test JS**: `pnpm test` or `pnpm test:watch` +- **Test Ruby**: `bundle exec rspec spec/path/to/file_spec.rb` +- **Single Test**: `bundle exec rspec spec/path/to/file_spec.rb:LINE_NUMBER` +- **Run Project**: `overmind start -f Procfile.dev` + +## Code Style + +- **Ruby**: Follow RuboCop rules (150 character max line length) +- **Vue/JS**: Use ESLint (Airbnb base + Vue 3 recommended) +- **Vue Components**: Use PascalCase +- **Events**: Use camelCase +- **I18n**: No bare strings in templates; use i18n +- **Error Handling**: Use custom exceptions (`lib/custom_exceptions/`) +- **Models**: Validate presence/uniqueness, add proper indexes +- **Type Safety**: Use PropTypes in Vue, strong params in Rails +- **Naming**: Use clear, descriptive names with consistent casing +- **Vue API**: Always use Composition API with ` + + diff --git a/app/javascript/dashboard/components-next/Campaigns/CampaignCard/CampaignCard.vue b/app/javascript/dashboard/components-next/Campaigns/CampaignCard/CampaignCard.vue index dbdbb5b45..304e55347 100644 --- a/app/javascript/dashboard/components-next/Campaigns/CampaignCard/CampaignCard.vue +++ b/app/javascript/dashboard/components-next/Campaigns/CampaignCard/CampaignCard.vue @@ -82,60 +82,56 @@ const inboxIcon = computed(() => { diff --git a/app/javascript/dashboard/components-next/Campaigns/CampaignCard/LiveChatCampaignDetails.vue b/app/javascript/dashboard/components-next/Campaigns/CampaignCard/LiveChatCampaignDetails.vue index 6f8c548d4..6a883ce6b 100644 --- a/app/javascript/dashboard/components-next/Campaigns/CampaignCard/LiveChatCampaignDetails.vue +++ b/app/javascript/dashboard/components-next/Campaigns/CampaignCard/LiveChatCampaignDetails.vue @@ -1,7 +1,8 @@ + + diff --git a/app/javascript/dashboard/components-next/Contacts/ContactsCard/ContactDeleteSection.vue b/app/javascript/dashboard/components-next/Contacts/ContactsCard/ContactDeleteSection.vue new file mode 100644 index 000000000..47b779b61 --- /dev/null +++ b/app/javascript/dashboard/components-next/Contacts/ContactsCard/ContactDeleteSection.vue @@ -0,0 +1,65 @@ + + + diff --git a/app/javascript/dashboard/components-next/Contacts/ContactsCard/ContactsCard.vue b/app/javascript/dashboard/components-next/Contacts/ContactsCard/ContactsCard.vue new file mode 100644 index 000000000..b2b0dbfa0 --- /dev/null +++ b/app/javascript/dashboard/components-next/Contacts/ContactsCard/ContactsCard.vue @@ -0,0 +1,190 @@ + + + diff --git a/app/javascript/dashboard/components-next/Contacts/ContactsCard/story/ContactsCard.story.vue b/app/javascript/dashboard/components-next/Contacts/ContactsCard/story/ContactsCard.story.vue new file mode 100644 index 000000000..3f8d5a411 --- /dev/null +++ b/app/javascript/dashboard/components-next/Contacts/ContactsCard/story/ContactsCard.story.vue @@ -0,0 +1,67 @@ + + + diff --git a/app/javascript/dashboard/components-next/Contacts/ContactsCard/story/fixtures.js b/app/javascript/dashboard/components-next/Contacts/ContactsCard/story/fixtures.js new file mode 100644 index 000000000..67bdba9b4 --- /dev/null +++ b/app/javascript/dashboard/components-next/Contacts/ContactsCard/story/fixtures.js @@ -0,0 +1,149 @@ +export default [ + { + additionalAttributes: { + socialProfiles: {}, + }, + availabilityStatus: null, + email: 'johndoe@chatwoot.com', + id: 370, + name: 'John Doe', + phoneNumber: '+918634322418', + identifier: null, + thumbnail: 'https://api.dicebear.com/9.x/thumbs/svg?seed=Felix', + customAttributes: {}, + lastActivityAt: 1731608270, + createdAt: 1731586271, + }, + { + additionalAttributes: { + city: 'kerala', + country: 'India', + description: 'Curious about the web. ', + companyName: 'Chatwoot', + countryCode: '', + socialProfiles: { + github: 'abozler', + twitter: 'ozler', + facebook: 'abozler', + linkedin: 'abozler', + instagram: 'ozler', + }, + }, + availabilityStatus: null, + email: 'ozler@chatwoot.com', + id: 29, + name: 'Abraham Ozlers', + phoneNumber: '+246232222222', + identifier: null, + thumbnail: 'https://api.dicebear.com/9.x/thumbs/svg?seed=Upload', + customAttributes: { + dateContact: '2024-02-01T00:00:00.000Z', + linkContact: 'https://staging.chatwoot.com/app/accounts/3/contacts-new', + listContact: 'Not spam', + numberContact: '12', + }, + lastActivityAt: 1712127410, + createdAt: 1712127389, + }, + { + additionalAttributes: { + city: 'Kerala', + country: 'India', + description: + "I'm Candice developer focusing on building things for the web 🌍. Currently, I’m working as a Product Developer here at @chatwootapp ⚡️🔥", + companyName: 'Chatwoot', + countryCode: 'IN', + socialProfiles: { + github: 'cmathersonj', + twitter: 'cmather', + facebook: 'cmathersonj', + linkedin: 'cmathersonj', + instagram: 'cmathersonjs', + }, + }, + availabilityStatus: null, + email: 'cmathersonj@va.test', + id: 22, + name: 'Candice Matherson', + phoneNumber: '+917474774742', + identifier: null, + thumbnail: 'https://api.dicebear.com/9.x/thumbs/svg?seed=Emery', + customAttributes: { + dateContact: '2024-11-12T03:23:06.963Z', + linkContact: 'https://sd.sd', + textContact: 'hey', + numberContact: '12', + checkboxContact: true, + }, + lastActivityAt: 1712123233, + createdAt: 1712123233, + }, + { + additionalAttributes: { + city: '', + country: '', + description: '', + companyName: '', + countryCode: '', + socialProfiles: { + github: '', + twitter: '', + facebook: '', + linkedin: '', + instagram: '', + }, + }, + availabilityStatus: null, + email: 'ofolkardi@taobao.test', + id: 21, + name: 'Ophelia Folkard', + phoneNumber: '', + identifier: null, + thumbnail: + 'https://sivin-tunnel.chatwoot.dev/rails/active_storage/representations/redirect/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBPZz09IiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--08dcac8eb72ef12b2cad92d58dddd04cd8a5f513/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaDdCem9MWm05eWJXRjBTU0lJYW5CbkJqb0dSVlE2RTNKbGMybDZaVjkwYjE5bWFXeHNXd2RwQWZvdyIsImV4cCI6bnVsbCwicHVyIjoidmFyaWF0aW9uIn19--df796c2af3c0153e55236c2f3cf3a199ac2cb6f7/32.jpg', + customAttributes: {}, + lastActivityAt: 1712123233, + createdAt: 1712123233, + }, + { + additionalAttributes: { + socialProfiles: {}, + }, + availabilityStatus: null, + email: 'wcasteloth@exblog.jp', + id: 20, + name: 'Willy Castelot', + phoneNumber: '+919384', + identifier: null, + thumbnail: 'https://api.dicebear.com/9.x/thumbs/svg?seed=Jade', + customAttributes: {}, + lastActivityAt: 1712123233, + createdAt: 1712123233, + }, + { + additionalAttributes: { + city: '', + country: '', + description: '', + companyName: '', + countryCode: '', + socialProfiles: { + github: '', + twitter: '', + facebook: '', + linkedin: '', + instagram: '', + }, + }, + availabilityStatus: null, + email: 'ederingtong@printfriendly.test', + id: 19, + name: 'Elisabeth Derington', + phoneNumber: '', + identifier: null, + thumbnail: 'https://api.dicebear.com/9.x/avataaars/svg?seed=Jade', + customAttributes: {}, + lastActivityAt: 1712123232, + createdAt: 1712123232, + }, +]; diff --git a/app/javascript/dashboard/components-next/Contacts/ContactsDetailsLayout.vue b/app/javascript/dashboard/components-next/Contacts/ContactsDetailsLayout.vue new file mode 100644 index 000000000..bb4327816 --- /dev/null +++ b/app/javascript/dashboard/components-next/Contacts/ContactsDetailsLayout.vue @@ -0,0 +1,115 @@ + + + diff --git a/app/javascript/dashboard/components-next/Contacts/ContactsForm/ConfirmContactDeleteDialog.vue b/app/javascript/dashboard/components-next/Contacts/ContactsForm/ConfirmContactDeleteDialog.vue new file mode 100644 index 000000000..f43a50883 --- /dev/null +++ b/app/javascript/dashboard/components-next/Contacts/ContactsForm/ConfirmContactDeleteDialog.vue @@ -0,0 +1,54 @@ + + + diff --git a/app/javascript/dashboard/components-next/Contacts/ContactsForm/ContactExportDialog.vue b/app/javascript/dashboard/components-next/Contacts/ContactsForm/ContactExportDialog.vue new file mode 100644 index 000000000..d2b57cbb8 --- /dev/null +++ b/app/javascript/dashboard/components-next/Contacts/ContactsForm/ContactExportDialog.vue @@ -0,0 +1,66 @@ + + + diff --git a/app/javascript/dashboard/components-next/Contacts/ContactsForm/ContactImportDialog.vue b/app/javascript/dashboard/components-next/Contacts/ContactsForm/ContactImportDialog.vue new file mode 100644 index 000000000..acb8f0f1c --- /dev/null +++ b/app/javascript/dashboard/components-next/Contacts/ContactsForm/ContactImportDialog.vue @@ -0,0 +1,134 @@ + + + diff --git a/app/javascript/dashboard/components-next/Contacts/ContactsForm/ContactMergeForm.vue b/app/javascript/dashboard/components-next/Contacts/ContactsForm/ContactMergeForm.vue new file mode 100644 index 000000000..2cee66d12 --- /dev/null +++ b/app/javascript/dashboard/components-next/Contacts/ContactsForm/ContactMergeForm.vue @@ -0,0 +1,113 @@ + + + diff --git a/app/javascript/dashboard/components-next/Contacts/ContactsForm/ContactsForm.vue b/app/javascript/dashboard/components-next/Contacts/ContactsForm/ContactsForm.vue new file mode 100644 index 000000000..cd8c767b8 --- /dev/null +++ b/app/javascript/dashboard/components-next/Contacts/ContactsForm/ContactsForm.vue @@ -0,0 +1,316 @@ + + + diff --git a/app/javascript/dashboard/components-next/Contacts/ContactsForm/CreateNewContactDialog.vue b/app/javascript/dashboard/components-next/Contacts/ContactsForm/CreateNewContactDialog.vue new file mode 100644 index 000000000..75400692e --- /dev/null +++ b/app/javascript/dashboard/components-next/Contacts/ContactsForm/CreateNewContactDialog.vue @@ -0,0 +1,70 @@ + + + diff --git a/app/javascript/dashboard/components-next/Contacts/ContactsForm/CreateSegmentDialog.vue b/app/javascript/dashboard/components-next/Contacts/ContactsForm/CreateSegmentDialog.vue new file mode 100644 index 000000000..a246c742c --- /dev/null +++ b/app/javascript/dashboard/components-next/Contacts/ContactsForm/CreateSegmentDialog.vue @@ -0,0 +1,71 @@ + + + diff --git a/app/javascript/dashboard/components-next/Contacts/ContactsForm/DeleteSegmentDialog.vue b/app/javascript/dashboard/components-next/Contacts/ContactsForm/DeleteSegmentDialog.vue new file mode 100644 index 000000000..60aa0e55e --- /dev/null +++ b/app/javascript/dashboard/components-next/Contacts/ContactsForm/DeleteSegmentDialog.vue @@ -0,0 +1,43 @@ + + + diff --git a/app/javascript/dashboard/components-next/Contacts/ContactsForm/story/ContactMergeForm.story.vue b/app/javascript/dashboard/components-next/Contacts/ContactsForm/story/ContactMergeForm.story.vue new file mode 100644 index 000000000..dbf22dadd --- /dev/null +++ b/app/javascript/dashboard/components-next/Contacts/ContactsForm/story/ContactMergeForm.story.vue @@ -0,0 +1,73 @@ + + + diff --git a/app/javascript/dashboard/components-next/Contacts/ContactsForm/story/ContactsForm.story.vue b/app/javascript/dashboard/components-next/Contacts/ContactsForm/story/ContactsForm.story.vue new file mode 100644 index 000000000..306df1df0 --- /dev/null +++ b/app/javascript/dashboard/components-next/Contacts/ContactsForm/story/ContactsForm.story.vue @@ -0,0 +1,80 @@ + + + diff --git a/app/javascript/dashboard/components-next/Contacts/ContactsForm/story/fixtures.js b/app/javascript/dashboard/components-next/Contacts/ContactsForm/story/fixtures.js new file mode 100644 index 000000000..9d9590d63 --- /dev/null +++ b/app/javascript/dashboard/components-next/Contacts/ContactsForm/story/fixtures.js @@ -0,0 +1,47 @@ +export const contactData = { + id: 370, + name: 'John Doe', + email: 'johndoe@chatwoot.com', + phoneNumber: '+918634322418', + additionalAttributes: { + city: 'Kerala', + country: 'India', + description: 'Curious about the web.', + companyName: 'Chatwoot', + countryCode: 'IN', + socialProfiles: { + github: 'johndoe', + twitter: 'johndoe', + facebook: 'johndoe', + linkedin: 'johndoe', + instagram: 'johndoe', + }, + }, +}; + +export const primaryContactList = [ + { + id: 1, + name: 'Jane Smith', + email: 'jane@chatwoot.com', + thumbnail: '', + label: '(ID: 1) Jane Smith', + value: 1, + }, + { + id: 2, + name: 'Mike Johnson', + email: 'mike@chatwoot.com', + thumbnail: '', + label: '(ID: 2) Mike Johnson', + value: 2, + }, + { + id: 3, + name: 'Sarah Wilson', + email: 'sarah@chatwoot.com', + thumbnail: '', + label: '(ID: 3) Sarah Wilson', + value: 3, + }, +]; diff --git a/app/javascript/dashboard/components-next/Contacts/ContactsHeader/ContactHeader.vue b/app/javascript/dashboard/components-next/Contacts/ContactsHeader/ContactHeader.vue new file mode 100644 index 000000000..42f526f95 --- /dev/null +++ b/app/javascript/dashboard/components-next/Contacts/ContactsHeader/ContactHeader.vue @@ -0,0 +1,143 @@ + + + diff --git a/app/javascript/dashboard/components-next/Contacts/ContactsHeader/ContactListHeaderWrapper.vue b/app/javascript/dashboard/components-next/Contacts/ContactsHeader/ContactListHeaderWrapper.vue new file mode 100644 index 000000000..627addff8 --- /dev/null +++ b/app/javascript/dashboard/components-next/Contacts/ContactsHeader/ContactListHeaderWrapper.vue @@ -0,0 +1,311 @@ + + + diff --git a/app/javascript/dashboard/components-next/Contacts/ContactsHeader/components/ContactMoreActions.vue b/app/javascript/dashboard/components-next/Contacts/ContactsHeader/components/ContactMoreActions.vue new file mode 100644 index 000000000..d5932535c --- /dev/null +++ b/app/javascript/dashboard/components-next/Contacts/ContactsHeader/components/ContactMoreActions.vue @@ -0,0 +1,62 @@ + + + diff --git a/app/javascript/dashboard/components-next/Contacts/ContactsHeader/components/ContactSortMenu.vue b/app/javascript/dashboard/components-next/Contacts/ContactsHeader/components/ContactSortMenu.vue new file mode 100644 index 000000000..7125d8385 --- /dev/null +++ b/app/javascript/dashboard/components-next/Contacts/ContactsHeader/components/ContactSortMenu.vue @@ -0,0 +1,134 @@ + + + diff --git a/app/javascript/dashboard/components-next/Contacts/ContactsHeader/components/ContactsActiveFiltersPreview.vue b/app/javascript/dashboard/components-next/Contacts/ContactsHeader/components/ContactsActiveFiltersPreview.vue new file mode 100644 index 000000000..88ee94604 --- /dev/null +++ b/app/javascript/dashboard/components-next/Contacts/ContactsHeader/components/ContactsActiveFiltersPreview.vue @@ -0,0 +1,69 @@ + + + diff --git a/app/javascript/dashboard/components-next/Contacts/ContactsHeader/story/ContactHeader.story.vue b/app/javascript/dashboard/components-next/Contacts/ContactsHeader/story/ContactHeader.story.vue new file mode 100644 index 000000000..a89aa724c --- /dev/null +++ b/app/javascript/dashboard/components-next/Contacts/ContactsHeader/story/ContactHeader.story.vue @@ -0,0 +1,106 @@ + + + diff --git a/app/javascript/dashboard/components-next/Contacts/ContactsListLayout.vue b/app/javascript/dashboard/components-next/Contacts/ContactsListLayout.vue new file mode 100644 index 000000000..25aac5df8 --- /dev/null +++ b/app/javascript/dashboard/components-next/Contacts/ContactsListLayout.vue @@ -0,0 +1,103 @@ + + + diff --git a/app/javascript/dashboard/components-next/Contacts/ContactsSidebar/ContactCustomAttributeItem.vue b/app/javascript/dashboard/components-next/Contacts/ContactsSidebar/ContactCustomAttributeItem.vue new file mode 100644 index 000000000..dc52d8912 --- /dev/null +++ b/app/javascript/dashboard/components-next/Contacts/ContactsSidebar/ContactCustomAttributeItem.vue @@ -0,0 +1,95 @@ + + + diff --git a/app/javascript/dashboard/components-next/Contacts/ContactsSidebar/ContactCustomAttributes.vue b/app/javascript/dashboard/components-next/Contacts/ContactsSidebar/ContactCustomAttributes.vue new file mode 100644 index 000000000..039d2c709 --- /dev/null +++ b/app/javascript/dashboard/components-next/Contacts/ContactsSidebar/ContactCustomAttributes.vue @@ -0,0 +1,161 @@ + + + diff --git a/app/javascript/dashboard/components-next/Contacts/ContactsSidebar/ContactHistory.vue b/app/javascript/dashboard/components-next/Contacts/ContactsSidebar/ContactHistory.vue new file mode 100644 index 000000000..3763d198a --- /dev/null +++ b/app/javascript/dashboard/components-next/Contacts/ContactsSidebar/ContactHistory.vue @@ -0,0 +1,54 @@ + + + diff --git a/app/javascript/dashboard/components-next/Contacts/ContactsSidebar/ContactMerge.vue b/app/javascript/dashboard/components-next/Contacts/ContactsSidebar/ContactMerge.vue new file mode 100644 index 000000000..2a0e79931 --- /dev/null +++ b/app/javascript/dashboard/components-next/Contacts/ContactsSidebar/ContactMerge.vue @@ -0,0 +1,145 @@ + + + diff --git a/app/javascript/dashboard/components-next/Contacts/ContactsSidebar/ContactNotes.vue b/app/javascript/dashboard/components-next/Contacts/ContactsSidebar/ContactNotes.vue new file mode 100644 index 000000000..789aba1c8 --- /dev/null +++ b/app/javascript/dashboard/components-next/Contacts/ContactsSidebar/ContactNotes.vue @@ -0,0 +1,101 @@ + + + diff --git a/app/javascript/dashboard/components-next/Contacts/ContactsSidebar/components/ContactNoteItem.vue b/app/javascript/dashboard/components-next/Contacts/ContactsSidebar/components/ContactNoteItem.vue new file mode 100644 index 000000000..c11fa2d9d --- /dev/null +++ b/app/javascript/dashboard/components-next/Contacts/ContactsSidebar/components/ContactNoteItem.vue @@ -0,0 +1,109 @@ + + + diff --git a/app/javascript/dashboard/components-next/Contacts/ContactsSidebar/components/story/ContactNoteItem.story.vue b/app/javascript/dashboard/components-next/Contacts/ContactsSidebar/components/story/ContactNoteItem.story.vue new file mode 100644 index 000000000..0dbf8bf01 --- /dev/null +++ b/app/javascript/dashboard/components-next/Contacts/ContactsSidebar/components/story/ContactNoteItem.story.vue @@ -0,0 +1,39 @@ + + + diff --git a/app/javascript/dashboard/components-next/Contacts/ContactsSidebar/components/story/fixtures.js b/app/javascript/dashboard/components-next/Contacts/ContactsSidebar/components/story/fixtures.js new file mode 100644 index 000000000..440ad95df --- /dev/null +++ b/app/javascript/dashboard/components-next/Contacts/ContactsSidebar/components/story/fixtures.js @@ -0,0 +1,69 @@ +export default [ + { + id: 12, + content: + 'This tutorial will show you how to use Chatwoot and, hence, ensure you practice effective customer communication. We will explain in detail the following:\n\n* Step-by-step setup of your account, with illustrative screenshots.\n\n* An in-depth explanation of all the core features of Chatwoot.\n\n* Get your account up and running by the end of this tutorial.\n\n* Basic concepts of customer communication.', + accountId: null, + contactId: null, + user: { + id: 30, + account_id: 2, + availability_status: 'offline', + auto_offline: true, + confirmed: true, + email: 'bruce@paperlayer.test', + available_name: 'Bruce', + name: 'Bruce', + role: 'administrator', + thumbnail: + 'https://sivin-tunnel.chatwoot.dev/rails/active_storage/representations/redirect/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBJZz09IiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--515dbb35e9ba3c36d14f4c4b77220a675513c1fb/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaDdCem9MWm05eWJXRjBTU0lJYW5CbkJqb0dSVlE2RTNKbGMybDZaVjkwYjE5bWFXeHNXd2RwQWZvdyIsImV4cCI6bnVsbCwicHVyIjoidmFyaWF0aW9uIn19--df796c2af3c0153e55236c2f3cf3a199ac2cb6f7/2.jpg', + custom_role_id: null, + }, + createdAt: 1730786556, + updatedAt: 1730786556, + }, + { + id: 10, + content: + 'We discussed a couple of things:\n\n* Product offering and how it can be useful to talk with people.\n\n* They’ll reach out to us after an internal review.', + accountId: null, + contactId: null, + user: { + id: 1, + account_id: 2, + availability_status: 'online', + auto_offline: false, + confirmed: true, + email: 'hillary@chatwoot.com', + available_name: 'Hillary', + name: 'Hillary', + role: 'administrator', + thumbnail: '', + custom_role_id: null, + }, + createdAt: 1730782566, + updatedAt: 1730782566, + }, + { + id: 9, + content: + 'We discussed a couple of things:\n\n* Product offering and how it can be useful to talk with people.\n\n* They’ll reach out to us after an internal review.', + accountId: null, + contactId: null, + user: { + id: 1, + account_id: 2, + availability_status: 'online', + auto_offline: false, + confirmed: true, + email: 'john@chatwoot.com', + available_name: 'John', + name: 'John', + role: 'administrator', + thumbnail: '', + custom_role_id: null, + }, + createdAt: 1730782564, + updatedAt: 1730782564, + }, +]; diff --git a/app/javascript/dashboard/components-next/Contacts/EmptyState/ContactEmptyState.story.vue b/app/javascript/dashboard/components-next/Contacts/EmptyState/ContactEmptyState.story.vue new file mode 100644 index 000000000..0c1998a84 --- /dev/null +++ b/app/javascript/dashboard/components-next/Contacts/EmptyState/ContactEmptyState.story.vue @@ -0,0 +1,28 @@ + + + diff --git a/app/javascript/dashboard/components-next/Contacts/EmptyState/ContactEmptyState.vue b/app/javascript/dashboard/components-next/Contacts/EmptyState/ContactEmptyState.vue new file mode 100644 index 000000000..f6f5ddaf6 --- /dev/null +++ b/app/javascript/dashboard/components-next/Contacts/EmptyState/ContactEmptyState.vue @@ -0,0 +1,66 @@ + + + diff --git a/app/javascript/dashboard/components-next/Contacts/EmptyState/contactEmptyStateContent.js b/app/javascript/dashboard/components-next/Contacts/EmptyState/contactEmptyStateContent.js new file mode 100644 index 000000000..6d80c9151 --- /dev/null +++ b/app/javascript/dashboard/components-next/Contacts/EmptyState/contactEmptyStateContent.js @@ -0,0 +1,228 @@ +export default [ + { + additionalAttributes: { + city: 'Los Angeles', + country: 'United States', + description: + "I'm Candice, a developer focusing on building web solutions. Currently, I’m working as a Product Developer at Chatwoot.", + companyName: 'Chatwoot', + countryCode: 'US', + socialProfiles: { + github: 'candice-dev', + twitter: 'candice_w_dev', + facebook: 'candice.dev', + linkedin: 'candice-matherson', + instagram: 'candice.codes', + }, + }, + availabilityStatus: 'offline', + email: 'candice.matherson@chatwoot.com', + id: 22, + name: 'Candice Matherson', + phoneNumber: '+14155552671', + identifier: null, + thumbnail: '', + customAttributes: { + dateContact: '2024-11-11T11:53:09.299Z', + linkContact: 'https://example.com', + listContact: 'Follow-Up', + textContact: 'Hi there!', + numberContact: '42', + checkboxContact: false, + }, + lastActivityAt: 1712123233, + createdAt: 1712123233, + }, + { + additionalAttributes: { + city: 'San Francisco', + country: 'United States', + description: 'Passionate about design and user experience.', + companyName: 'Designify', + countryCode: 'US', + socialProfiles: { + github: 'ophelia-folkard', + twitter: 'oph_designs', + facebook: 'ophelia.folkard', + linkedin: 'ophelia-folkard', + instagram: 'ophelia.design', + }, + }, + availabilityStatus: 'offline', + email: 'ophelia.folkard@designify.com', + id: 21, + name: 'Ophelia Folkard', + phoneNumber: '+14155552672', + identifier: null, + thumbnail: '', + customAttributes: { + dateContact: '2024-10-05T10:12:34.567Z', + linkContact: 'https://designify.com', + listContact: 'Prospects', + textContact: 'Looking forward to connecting!', + }, + lastActivityAt: 1712123233, + createdAt: 1712123233, + }, + { + additionalAttributes: { + city: 'Austin', + country: 'United States', + description: 'Avid coder and tech enthusiast.', + companyName: 'CodeHub', + countryCode: 'US', + socialProfiles: { + github: 'willy_castelot', + twitter: 'willy_code', + facebook: 'willy.castelot', + linkedin: 'willy-castelot', + instagram: 'willy.coder', + }, + }, + availabilityStatus: 'offline', + email: 'willy.castelot@codehub.io', + id: 20, + name: 'Willy Castelot', + phoneNumber: '+14155552673', + identifier: null, + thumbnail: '', + customAttributes: { + textContact: 'Let’s collaborate!', + checkboxContact: true, + }, + lastActivityAt: 1712123233, + createdAt: 1712123233, + }, + { + additionalAttributes: { + city: 'Seattle', + country: 'United States', + description: 'Product manager with a love for innovation.', + companyName: 'InnovaTech', + countryCode: 'US', + socialProfiles: { + github: 'elisabeth-d', + twitter: 'elisabeth_innova', + facebook: 'elisabeth.derington', + linkedin: 'elisabeth-derington', + instagram: 'elisabeth.innovates', + }, + }, + availabilityStatus: 'offline', + email: 'elisabeth.derington@innova.com', + id: 19, + name: 'Elisabeth Derington', + phoneNumber: '+14155552674', + identifier: null, + thumbnail: '', + customAttributes: { + textContact: 'Let’s schedule a call.', + }, + lastActivityAt: 1712123232, + createdAt: 1712123232, + }, + { + additionalAttributes: { + city: 'Chicago', + country: 'United States', + description: 'Marketing specialist and content creator.', + companyName: 'Contently', + countryCode: 'US', + socialProfiles: { + github: 'olia-olenchenko', + twitter: 'olia_content', + facebook: 'olia.olenchenko', + linkedin: 'olia-olenchenko', + instagram: 'olia.creates', + }, + }, + availabilityStatus: 'offline', + email: 'olia.olenchenko@contently.com', + id: 18, + name: 'Olia Olenchenko', + phoneNumber: '+14155552675', + identifier: null, + thumbnail: '', + customAttributes: {}, + lastActivityAt: 1712123232, + createdAt: 1712123232, + }, + { + additionalAttributes: { + city: 'Boston', + country: 'United States', + description: 'SEO expert and analytics enthusiast.', + companyName: 'OptiSearch', + countryCode: 'US', + socialProfiles: { + github: 'nate-vannuchi', + twitter: 'nate_seo', + facebook: 'nathaniel.vannuchi', + linkedin: 'nathaniel-vannuchi', + instagram: 'nate.optimizes', + }, + }, + availabilityStatus: 'offline', + email: 'nathaniel.vannuchi@optisearch.com', + id: 17, + name: 'Nathaniel Vannuchi', + phoneNumber: '+14155552676', + identifier: null, + thumbnail: '', + customAttributes: {}, + lastActivityAt: 1712123232, + createdAt: 1712123232, + }, + { + additionalAttributes: { + city: 'Denver', + country: 'United States', + description: 'UI/UX designer with a flair for minimalist designs.', + companyName: 'Minimal Designs', + countryCode: 'US', + socialProfiles: { + github: 'merrile-petruk', + twitter: 'merrile_ux', + facebook: 'merrile.petruk', + linkedin: 'merrile-petruk', + instagram: 'merrile.designs', + }, + }, + availabilityStatus: 'offline', + email: 'merrile.petruk@minimal.com', + id: 16, + name: 'Merrile Petruk', + phoneNumber: '+14155552677', + identifier: null, + thumbnail: '', + customAttributes: {}, + lastActivityAt: 1712123232, + createdAt: 1712123232, + }, + { + additionalAttributes: { + city: 'Miami', + country: 'United States', + description: 'Entrepreneur with a background in e-commerce.', + companyName: 'Ecom Solutions', + countryCode: 'US', + socialProfiles: { + github: 'cordell-d', + twitter: 'cordell_entrepreneur', + facebook: 'cordell.dalinder', + linkedin: 'cordell-dalinder', + instagram: 'cordell.ecom', + }, + }, + availabilityStatus: 'offline', + email: 'cordell.dalinder@ecomsolutions.com', + id: 15, + name: 'Cordell Dalinder', + phoneNumber: '+14155552678', + identifier: null, + thumbnail: '', + customAttributes: {}, + lastActivityAt: 1712123232, + createdAt: 1712123232, + }, +]; diff --git a/app/javascript/dashboard/components-next/Contacts/Pages/ContactDetails.vue b/app/javascript/dashboard/components-next/Contacts/Pages/ContactDetails.vue new file mode 100644 index 000000000..aec21a976 --- /dev/null +++ b/app/javascript/dashboard/components-next/Contacts/Pages/ContactDetails.vue @@ -0,0 +1,200 @@ + + + diff --git a/app/javascript/dashboard/components-next/Contacts/Pages/ContactsList.vue b/app/javascript/dashboard/components-next/Contacts/Pages/ContactsList.vue new file mode 100644 index 000000000..33532edcf --- /dev/null +++ b/app/javascript/dashboard/components-next/Contacts/Pages/ContactsList.vue @@ -0,0 +1,81 @@ + + + diff --git a/app/javascript/dashboard/components-next/Conversation/ConversationCard/CardMessagePreview.vue b/app/javascript/dashboard/components-next/Conversation/ConversationCard/CardMessagePreview.vue index dfa31e100..a2b3ad7fb 100644 --- a/app/javascript/dashboard/components-next/Conversation/ConversationCard/CardMessagePreview.vue +++ b/app/javascript/dashboard/components-next/Conversation/ConversationCard/CardMessagePreview.vue @@ -1,6 +1,7 @@ diff --git a/app/javascript/dashboard/components-next/HelpCenter/PortalSwitcher/PortalSwitcher.vue b/app/javascript/dashboard/components-next/HelpCenter/PortalSwitcher/PortalSwitcher.vue index f88bd99e3..5a835f8cb 100644 --- a/app/javascript/dashboard/components-next/HelpCenter/PortalSwitcher/PortalSwitcher.vue +++ b/app/javascript/dashboard/components-next/HelpCenter/PortalSwitcher/PortalSwitcher.vue @@ -6,7 +6,7 @@ import { useMapGetter, useStore } from 'dashboard/composables/store.js'; import { buildPortalURL } from 'dashboard/helper/portalHelper'; import Button from 'dashboard/components-next/button/Button.vue'; -import Thumbnail from 'dashboard/components-next/thumbnail/Thumbnail.vue'; +import Avatar from 'dashboard/components-next/avatar/Avatar.vue'; const emit = defineEmits(['close', 'createPortal']); @@ -92,7 +92,7 @@ const redirectToPortalHomePage = () => {