diff --git a/.github/workflows/bats-test.yml b/.github/workflows/bats-test.yml new file mode 100644 index 0000000000..49e26b6e6b --- /dev/null +++ b/.github/workflows/bats-test.yml @@ -0,0 +1,55 @@ +name: Run BATS Tests + +on: + push: + paths: + - 'docker-compose/setup-script/noco.sh' + workflow_dispatch: + +jobs: + prepare: + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.set-matrix.outputs.matrix }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install jq + run: | + sudo apt-get update + sudo apt-get install -y jq + + - name: Prepare matrix for test files + id: set-matrix + run: | + BATS_FILES=$(find docker-compose/setup-script/tests -name '*.bats') + MATRIX_JSON=$(echo $BATS_FILES | jq -Rsc 'split("\n") | map(select(. != ""))') + echo "matrix=$MATRIX_JSON" >> $GITHUB_ENV + + test: + needs: prepare + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + test: ${{fromJson(env.matrix)}} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install BATS + run: | + sudo apt-get update + sudo apt-get install -y bats expect + + - name: Get working directory + run: | + WORKING_DIR="$(pwd)/docker-compose/setup-script/tests" + echo "WORKING_DIR=$WORKING_DIR" >> $GITHUB_ENV + + - name: Run BATS test + run: bats ${{ matrix.test }} + env: + WORKING_DIR: ${{ env.WORKING_DIR }} + SKIP_TARE_DOWN: true diff --git a/.github/workflows/sync-to-develop.yml b/.github/workflows/sync-to-develop.yml index 2f95378921..ca67059ba7 100644 --- a/.github/workflows/sync-to-develop.yml +++ b/.github/workflows/sync-to-develop.yml @@ -13,6 +13,10 @@ jobs: uses: actions/setup-node@v3 with: node-version: 18.19.1 + - name: Setup pnpm + uses: pnpm/action-setup@v2 + with: + version: 8 - name: Checkout uses: actions/checkout@v3 with: @@ -39,6 +43,7 @@ jobs: git config user.name 'github-actions[bot]' git config user.email 'github-actions[bot]@users.noreply.github.com' revertSDK=true node scripts/upgradeNocodbSdk.js + pnpm bootstrap git add . git diff-index --quiet HEAD || git commit -m "chore: update sdk path" git push origin $BRANCH_NAME diff --git a/docker-compose/setup-script/noco.sh b/docker-compose/setup-script/noco.sh index 45e070ff74..2c5c809d10 100755 --- a/docker-compose/setup-script/noco.sh +++ b/docker-compose/setup-script/noco.sh @@ -1,6 +1,24 @@ #!/bin/bash # set -x +# ****************************************************************************** +# ***************** GLOBAL VARIABLES START ********************************* + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +MAGENTA='\033[0;35m' +CYAN='\033[0;36m' +ORANGE='\033[0;33m' +BOLD='\033[1m' +NC='\033[0m' + +NOCO_HOME="${HOME}/.nocodb" + +# ***************** GLOBAL VARIABLES END *********************************** +# ****************************************************************************** + # ****************************************************************************** # ***************** HELPER FUNCTIONS START ********************************* @@ -53,46 +71,323 @@ install_package() { fi } -# Function to check if sudo is required for Docker Compose command -check_for_docker_compose_sudo() { - if docker-compose ps >/dev/null 2>&1; then +# Function to check if sudo is required for Docker command +check_for_docker_sudo() { + if docker ps >/dev/null 2>&1; then echo "n" else echo "y" fi } +# Function to read a number from the user +read_number() { + local number + read -rp "$1" number + + # Ensure the input is a number or empty + while ! [[ $number =~ ^[0-9]+$ ]] && [ -n "$number" ] ; do + read -rp "Please enter a valid number: " number + done + + echo "$number" +} + +# Function to read a number within a range from the user +read_number_range() { + local number + local min + local max + + # Check if there are 3 arguments + if [ "$#" -ne 3 ]; then + number=$(read_number) + min=$1 + max=$2 + else + number=$(read_number "$1") + min=$2 + max=$3 + fi + + # Ensure the input is in the specified range + while [[ -n "$number" && ($number -lt $min || $number -gt $max) ]]; do + number=$(read_number "Please enter a number between $min and $max: ") + done + + echo "$number" +} + +check_if_docker_is_running() { + if ! $DOCKER_COMMAND ps >/dev/null 2>&1; then + echo "+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-+" + echo -e "| ${BOLD}${YELLOW}Warning ! ${NC} |" + echo "| Docker is not running. Most of the commands will not work without Docker. |" + echo "| Use the following command to start Docker: |" + echo -e "| ${BLUE} sudo systemctl start docker ${NC} |" + echo "+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-+" + fi +} + # ***************** HELPER FUNCTIONS END *********************************** # ****************************************************************************** +# ***************************************************************************** +# *************************** Management ************************************* + +# Function to display the menu +show_menu() { + clear + check_if_docker_is_running + echo "" + echo "$MSG" + echo -e "\t\t${BOLD}Service Management Menu${NC}" + echo -e " ${GREEN}1. Start Service" + echo -e " ${ORANGE}2. Stop Service" + echo -e " ${CYAN}3. Logs" + echo -e " ${MAGENTA}4. Restart" + echo -e " ${BLUE}5. Upgrade" + echo -e " 6. Scale" + echo -e " 7. Monitoring" + echo -e " ${RED}0. Exit${NC}" +} + +# Function to start the service +start_service() { + echo -e "\nStarting nocodb..." + $DOCKER_COMMAND compose up -d +} + +# Function to stop the service +stop_service() { + echo -e "\nStopping nocodb..." + $DOCKER_COMMAND compose stop +} + +show_logs_sub_menu() { + clear + echo "Select a replica for $1:" + for i in $(seq 1 $2); do + echo "$i. \"$1\" replica $i" + done + echo "A. All" + echo "0. Back to Logs Menu" + echo "Enter replica number: " + read -r replica_choice + + if [[ "$replica_choice" =~ ^[0-9]+$ ]] && [ "$replica_choice" -gt 0 ] && [ "$replica_choice" -le "$2" ]; then + container_id=$($DOCKER_COMMAND compose ps | grep "$1-$replica_choice" | cut -d " " -f 1) + $DOCKER_COMMAND logs -f "$container_id" + elif [ "$replica_choice" == "A" ] || [ "$replica_choice" == "a" ]; then + $DOCKER_COMMAND compose logs -f "$1" + elif [ "$replica_choice" == "0" ]; then + show_logs + else + show_logs_sub_menu "$1" "$2" + fi +} + + +# Function to show logs +show_logs() { + clear + echo "Select a container for logs:" + + # Fetch the list of services + services=() + while IFS= read -r service; do + services+=("$service") + done < <($DOCKER_COMMAND compose ps --services) + + service_replicas=() + count=0 + + # For each service, count the number of running instances + for service in "${services[@]}"; do + # Count the number of lines that have the service name, which corresponds to the number of replicas + replicas=$($DOCKER_COMMAND compose ps "$service" | grep -c "$service") + service_replicas["$count"]=$replicas + count=$((count + 1)) + done + + count=1 + + for service in "${services[@]}"; do + echo "$count. $service (${service_replicas[(($count - 1))]} replicas)" + count=$((count + 1)) + done + + echo "A. All" + echo "0. Back to main menu" + echo "Enter your choice: " + read -r log_choice + echo + + if [[ "$log_choice" =~ ^[0-9]+$ ]] && [ "$log_choice" -gt 0 ] && [ "$log_choice" -lt "$count" ]; then + service_index=$((log_choice-1)) + service="${services[$service_index]}" + num_replicas="${service_replicas[$service_index]}" + + if [ "$num_replicas" -gt 1 ]; then + trap 'show_logs_sub_menu "$service" "$num_replicas"' INT + show_logs_sub_menu "$service" "$num_replicas" + trap - INT + else + trap 'show_logs' INT + $DOCKER_COMMAND compose logs -f "$service" + fi + elif [ "$log_choice" == "A" ] || [ "$log_choice" == "a" ]; then + trap 'show_logs' INT + $DOCKER_COMMAND compose logs -f + elif [ "$log_choice" == "0" ]; then + return + else + show_logs + fi + + trap - INT +} + +# Function to restart the service +restart_service() { + echo -e "\nRestarting nocodb..." + $DOCKER_COMMAND compose restart +} + +# Function to upgrade the service +upgrade_service() { + echo -e "\nUpgrading nocodb..." + $DOCKER_COMMAND compose pull + $DOCKER_COMMAND compose up -d --force-recreate + $DOCKER_COMMAND image prune -a -f +} + +# Function to scale the service +scale_service() { + num_cores=$(nproc || sysctl -n hw.ncpu || echo 1) + current_scale=$($DOCKER_COMMAND compose ps -q nocodb | wc -l) + echo -e "\nCurrent number of instances: $current_scale" + echo "How many instances of NocoDB do you want to run (Maximum: ${num_cores}) ? (default: 1): " + scale_num=$(read_number_range 1 "$num_cores") + + if [ "$scale_num" -eq "$current_scale" ]; then + echo "Number of instances is already set to $scale_num. Returning to main menu." + return + fi + + $DOCKER_COMMAND compose up -d --scale nocodb="$scale_num" +} + +# Function for basic monitoring +monitoring_service() { + echo -e '\nLoading stats...' + trap ' ' INT + $DOCKER_COMMAND stats +} + +management_menu() { + # Main program loop + while true; do + trap - INT + show_menu + echo "Enter your choice: " + + read -r choice + case $choice in + 1) start_service && MSG="NocoDB Started" ;; + 2) stop_service && MSG="NocoDB Stopped" ;; + 3) show_logs ;; + 4) restart_service && MSG="NocoDB Restarted" ;; + 5) upgrade_service && MSG="NocoDB has been upgraded to latest version" ;; + 6) scale_service && MSG="NocoDB has been scaled" ;; + 7) monitoring_service ;; + 0) exit 0 ;; + *) MSG="\nInvalid choice. Please select a correct option." ;; + esac + done +} + +# ****************************************************************************** +# *************************** Management END ********************************** + + +# ****************************************************************************** +# ***************** Existing Install Test ************************************ + +IS_DOCKER_REQUIRE_SUDO=$(check_for_docker_sudo) +DOCKER_COMMAND=$([ "$IS_DOCKER_REQUIRE_SUDO" = "y" ] && echo "sudo docker" || echo "docker") + +NOCO_FOUND=false + +# Check if $NOCO_HOME exists as directory +if [ -d "$NOCO_HOME" ]; then + NOCO_FOUND=true +elif $DOCKER_COMMAND ps --format '{{.Names}}' | grep -q "nocodb"; then + NOCO_ID=$(docker ps | grep "nocodb/nocodb" | cut -d ' ' -f 1) + CUSTOM_HOME=$(docker inspect --format='{{index .Mounts 0}}' "$NOCO_ID" | cut -d ' ' -f 3) + PARENT_DIR=$(dirname "$CUSTOM_HOME") + + ln -s "$PARENT_DIR" "$NOCO_HOME" + basename "$PARENT_DIR" > "$NOCO_HOME/.COMPOSE_PROJECT_NAME" + + NOCO_FOUND=true +else + mkdir -p "$NOCO_HOME" +fi + +cd "$NOCO_HOME" || exit 1 + +# Check if nocodb is already installed +if [ "$NOCO_FOUND" = true ]; then + echo "NocoDB is already installed. And running." + echo "Do you want to reinstall NocoDB? [Y/N] (default: N): " + read -r REINSTALL + + if [ -f "$NOCO_HOME/.COMPOSE_PROJECT_NAME" ]; then + COMPOSE_PROJECT_NAME=$(cat "$NOCO_HOME/.COMPOSE_PROJECT_NAME") + export COMPOSE_PROJECT_NAME + fi + + if [ "$REINSTALL" != "Y" ] && [ "$REINSTALL" != "y" ]; then + management_menu + exit 0 + else + echo "Reinstalling NocoDB..." + $DOCKER_COMMAND compose down + + unset COMPOSE_PROJECT_NAME + cd /tmp || exit 1 + rm -rf "$NOCO_HOME" + + mkdir -p "$NOCO_HOME" + cd "$NOCO_HOME" || exit 1 + fi +fi # ****************************************************************************** # ******************** SYSTEM REQUIREMENTS CHECK START ************************* # Check if the following requirements are met: -# a. docker, docker-compose, jq installed +# a. docker, jq installed # b. port mapping check : 80,443 are free or being used by nginx container REQUIRED_PORTS=(80 443) echo "** Performing nocodb system check and setup. This step may require sudo permissions" -# pre install wget if not found +# pre-install wget if not found if ! command_exists wget; then echo "wget is not installed. Setting up for installation..." install_package wget fi # d. Check if required tools are installed -echo " | Checking if required tools (docker, docker-compose, lsof) are installed..." -for tool in docker docker-compose lsof openssl; do +echo " | Checking if required tools (docker, lsof) are installed..." +for tool in docker lsof openssl; do if ! command_exists "$tool"; then echo "$tool is not installed. Setting up for installation..." - if [ "$tool" = "docker-compose" ]; then - sudo -E curl -L https://github.com/docker/compose/releases/download/1.29.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose - sudo chmod +x /usr/local/bin/docker-compose - elif [ "$tool" = "docker" ]; then + if [ "$tool" = "docker" ]; then wget -qO- https://get.docker.com/ | sh elif [ "$tool" = "lsof" ]; then install_package lsof @@ -100,16 +395,11 @@ for tool in docker docker-compose lsof openssl; do fi done -# e. Check if NocoDB is already installed and its expected version -# echo "Checking if NocoDB is already installed and its expected version..." -# Replace the following command with the actual command to check NocoDB installation and version -# Example: nocodb_version=$(command_to_get_nocodb_version) -# echo "NocoDB version: $nocodb_install_version" # f. Port mapping check echo " | Checking port accessibility..." for port in "${REQUIRED_PORTS[@]}"; do - if lsof -Pi :$port -sTCP:LISTEN -t >/dev/null; then + if lsof -Pi :"$port" -sTCP:LISTEN -t >/dev/null; then echo " | WARNING: Port $port is in use. Please make sure it is free." >&2 else echo " | Port $port is free." @@ -130,26 +420,7 @@ if [ -z "$PUBLIC_IP" ]; then PUBLIC_IP="localhost" fi -# generate a folder for the docker-compose file which is not existing and do the setup within the folder -# Define the folder name -FOLDER_NAME="nocodb_$(date +"%Y%m%d_%H%M%S")" - -# prompt for custom folder name and if left empty skip -#echo "Enter a custom folder name or press Enter to use the default folder name ($FOLDER_NAME): " -#read CUSTOM_FOLDER_NAME - -message_arr+=("Setup folder: $FOLDER_NAME") - -if [ -n "$CUSTOM_FOLDER_NAME" ]; then - FOLDER_NAME="$CUSTOM_FOLDER_NAME" -fi - - -# Create the folder -mkdir -p "$FOLDER_NAME" - -# Navigate into the folder -cd "$FOLDER_NAME" || exit +message_arr+=("Setup folder: $NOCO_HOME") # ******************** SYSTEM REQUIREMENTS CHECK END ************************** # ****************************************************************************** @@ -159,34 +430,36 @@ cd "$FOLDER_NAME" || exit # ******************** INPUTS FROM USER START ******************************** # ****************************************************************************** -echo "Choose Community or Enterprise Edition [CE/EE] (default: CE): " -read EDITION +echo "Enter the IP address or domain name for the NocoDB instance (default: $PUBLIC_IP): " +read -r DOMAIN_NAME -echo "Do you want to configure SSL [Y/N] (default: N): " -read SSL_ENABLED +echo "Show Advanced Options [Y/N] (default: N): " +read -r ADVANCED_OPTIONS +if [ "$ADVANCED_OPTIONS" == "Y" ]; then + ADVANCED_OPTIONS="y" +fi -if [ -n "$SSL_ENABLED" ] && { [ "$SSL_ENABLED" = "Y" ] || [ "$SSL_ENABLED" = "y" ]; }; then - SSL_ENABLED='y' - echo "Enter the domain name for the SSL certificate: " - read DOMAIN_NAME - if [ -z "$DOMAIN_NAME" ]; then - echo "Domain name is required for SSL configuration" - exit 1 - fi - message_arr+=("Domain: $DOMAIN_NAME") +if [ -n "$DOMAIN_NAME" ]; then + if [ "$ADVANCED_OPTIONS" == "y" ]; then + echo "Do you want to configure SSL [Y/N] (default: N): " + read -r SSL_ENABLED + message_arr+=("SSL: ${SSL_ENABLED}") + fi else - # prompt for ip address and if left empty use extracted public ip - echo "Enter the IP address or domain name for the NocoDB instance (default: $PUBLIC_IP): " - read DOMAIN_NAME - if [ -z "$DOMAIN_NAME" ]; then - DOMAIN_NAME="$PUBLIC_IP" - fi + DOMAIN_NAME="$PUBLIC_IP" +fi + +message_arr+=("Domain: $PUBLIC_IP") + +if [ "$ADVANCED_OPTIONS" == "y" ]; then + echo "Choose Community or Enterprise Edition [CE/EE] (default: CE): " + read -r EDITION fi if [ -n "$EDITION" ] && { [ "$EDITION" = "EE" ] || [ "$EDITION" = "ee" ]; }; then echo "Enter the NocoDB license key: " - read LICENSE_KEY + read -r LICENSE_KEY if [ -z "$LICENSE_KEY" ]; then echo "License key is required for Enterprise Edition installation" exit 1 @@ -194,8 +467,10 @@ if [ -n "$EDITION" ] && { [ "$EDITION" = "EE" ] || [ "$EDITION" = "ee" ]; }; the fi -echo "Do you want to enabled Redis for caching [Y/N] (default: Y): " -read REDIS_ENABLED +if [ "$ADVANCED_OPTIONS" == "y" ]; then + echo "Do you want to enabled Redis for caching [Y/N] (default: Y): " + read -r REDIS_ENABLED +fi if [ -z "$REDIS_ENABLED" ] || { [ "$REDIS_ENABLED" != "N" ] && [ "$REDIS_ENABLED" != "n" ]; }; then message_arr+=("Redis: Enabled") @@ -204,8 +479,10 @@ else fi -echo "Do you want to enabled Watchtower for automatic updates [Y/N] (default: Y): " -read WATCHTOWER_ENABLED +if [ "$ADVANCED_OPTIONS" == "y" ]; then + echo "Do you want to enabled Watchtower for automatic updates [Y/N] (default: Y): " + read -r WATCHTOWER_ENABLED +fi if [ -z "$WATCHTOWER_ENABLED" ] || { [ "$WATCHTOWER_ENABLED" != "N" ] && [ "$WATCHTOWER_ENABLED" != "n" ]; }; then message_arr+=("Watchtower: Enabled") @@ -213,7 +490,17 @@ else message_arr+=("Watchtower: Disabled") fi +if [ "$ADVANCED_OPTIONS" = "y" ] ; then + NUM_CORES=$(nproc || sysctl -n hw.ncpu || echo 1) + echo "How many instances of NocoDB do you want to run (Maximum: ${NUM_CORES}) ? (default: 1): " + NUM_INSTANCES=$(read_number_range 1 "$NUM_CORES") +fi + +if [ -z "$NUM_INSTANCES" ]; then + NUM_INSTANCES=1 +fi +message_arr+=("Number of instances: $NUM_INSTANCES") # ****************************************************************************** # *********************** INPUTS FROM USER END ******************************** @@ -253,12 +540,13 @@ fi # Write the Docker Compose file with the updated password cat < docker-compose.yml -version: '3' - services: nocodb: image: ${IMAGE} env_file: docker.env + deploy: + mode: replicated + replicas: ${NUM_INSTANCES} depends_on: - db ${DEPENDS_ON} @@ -444,6 +732,12 @@ mkdir -p ./nginx-post-config # Create nginx config with the provided domain name cat > ./nginx-post-config/default.conf < ./update.sh < ./update.sh < ./update.sh < /dev/null +} diff --git a/docker-compose/setup-script/tests/install/watchtower.bats b/docker-compose/setup-script/tests/install/watchtower.bats new file mode 100755 index 0000000000..32c988809c --- /dev/null +++ b/docker-compose/setup-script/tests/install/watchtower.bats @@ -0,0 +1,30 @@ +#!/usr/bin/env bats + +NOCO_HOME="${HOME}/.nocodb" +export NOCO_HOME + +setup() { + cd "${WORKING_DIR}/install" || exit 1 + ./setup.sh +} + +teardown() { + if [ -n "$SKIP_TEARDOWN" ]; then + return + fi + + cd "${WORKING_DIR}/install" || exit 1 + ./setup.sh +} + +@test "Check WatchTower is enabled when specified" { + ../expects/install/watchtower.sh + + cd "${NOCO_HOME}" + + # Check Docker Compose file to verify WatchTower configuration + grep -q 'watchtower' docker-compose.yml + + # Verify WatchTower container is running + docker compose ps | grep -q 'watchtower' +} diff --git a/docker-compose/setup-script/tests/mocks/clear b/docker-compose/setup-script/tests/mocks/clear new file mode 100755 index 0000000000..e08f792e8f --- /dev/null +++ b/docker-compose/setup-script/tests/mocks/clear @@ -0,0 +1,3 @@ +#!/bin/bash + +echo "--- Clear Mock ---" \ No newline at end of file diff --git a/docker-compose/setup-script/tests/mocks/nproc b/docker-compose/setup-script/tests/mocks/nproc new file mode 100755 index 0000000000..1d83c4a9b0 --- /dev/null +++ b/docker-compose/setup-script/tests/mocks/nproc @@ -0,0 +1,3 @@ +#!/bin/bash + +echo 4 \ No newline at end of file diff --git a/markdown/readme/languages/japanese.md b/markdown/readme/languages/japanese.md index 0d2d933bd2..f0ba8fd5e8 100644 --- a/markdown/readme/languages/japanese.md +++ b/markdown/readme/languages/japanese.md @@ -6,7 +6,7 @@

-MySQL、PostgreSQL、SQL Server、SQLite&Mariadbをスマートスプレッドシートに変えます。 +MySQL、PostgreSQL、SQL Server、SQLite&Mariadbをスマートスプレッドシートに変換します。

@@ -41,7 +41,7 @@ docker run -d --name nocodb -p 8080:8080 nocodb/nocodb:latest ``` - NocoDBは入力としてデータベースが必要です:[本番環境設定](https://github.com/nocodb/nocodb/blob/master/README.md#production-setup)を参照してください。 -- この入力がない場合、SQLiteにフォールバックする。SQLiteを持続させるために、`/usr/app/data/`をマウントします。 +- この入力がない場合、SQLiteにフォールバックします。SQLiteでデータを保持するために、`/usr/app/data/`をマウントします。 例: @@ -49,7 +49,7 @@ docker run -d --name nocodb -p 8080:8080 nocodb/nocodb:latest docker run -d -p 8080:8080 --name nocodb -v "$(pwd)"/nocodb:/usr/app/data/ nocodb/nocodb:latest ``` -### NPM を使用して +### NPM を使用して初期化を行う ``` npx create-nocodb-app @@ -66,7 +66,7 @@ npm start ### GUI -アクセスダッシュボードを使用して : [http://localhost:8080/dashboard](http://localhost:8080/dashboard) +アクセスダッシュボードを使用する : [http://localhost:8080/dashboard](http://localhost:8080/dashboard) # 私たちのコミュニティに参加する @@ -115,7 +115,7 @@ npm start ### リッチスプレッドシートインターフェース -検索、並べ替え、フィルタリング、列を隠す +検索、並べ替え、フィルタリング、列の非表示 - ⚡ ビューを作成する:グリッド、ギャラリー、カンバン、ガント、フォーム - ⚡ シェアビュー:Public&Password Protected. @@ -194,10 +194,10 @@ docker-compose up -d [コントリビューションガイド](https://github.com/nocodb/nocodb/blob/master/.github/CONTRIBUTING.md)をご参照ください。 -# なぜこれを構築しているのですか? +# 開発の目的 -ほとんどのインターネットビジネスは、ビジネスニーズを解決するためにスプレッドシートかデータベースのどちらかを装備しています。表計算ソフトは、毎日10億人以上の人が共同作業で使っています。しかし、コンピューティングに関しては、より強力なツールであるデータベースで同様のスピードで作業するのは、かなり遅れています。SaaSでこれを解決しようとすると、ひどいアクセスコントロール、ベンダーの囲い込み、データの囲い込み、突然の価格変更、そして最も重要なことは、将来的に何が可能かというガラスの天井を意味することになるのです。 +ほとんどのインターネットビジネスは、ビジネスニーズを解決するためにスプレッドシートかデータベースのどちらかを用いています。表計算ソフトは、毎日10億人以上の人が共同作業で使っています。しかし、コンピューティングに関しては、より強力なツールであるデータベースで同様のスピードで作業するのは、かなり遅れています。SaaSでこれを解決しようとすると、ひどいアクセスコントロール、ベンダーの囲い込み、データの囲い込み、突然の価格変更、そして最も重要なこととしては、将来における可能性に対する隠れた制限が存在することです # 私たちの使命 -私たちの使命は、データベース用の最も強力なノーコードインターフェイスを、世界中のすべてのインターネットビジネスにオープンソースで提供することです。これは、強力なコンピューティングツールへのアクセスを民主化するだけでなく、インターネット上で根本的な改造と構築の能力を持つ10億人以上の人々を生み出すでしょう。 +私たちの使命は、データベース用の最も強力なノーコードインターフェイスを、世界中のすべてのインターネットビジネスにオープンソースで提供することです。これは、強力なコンピューティングツールへのアクセスを民主化するだけでなく、インターネット上で根本的な改修と構築の能力を持つ10億人以上の人々を生み出すでしょう。 diff --git a/packages/nc-gui/assets/img/placeholder/no-search-result-found.png b/packages/nc-gui/assets/img/placeholder/no-search-result-found.png new file mode 100644 index 0000000000..26d270c3cf Binary files /dev/null and b/packages/nc-gui/assets/img/placeholder/no-search-result-found.png differ diff --git a/packages/nc-gui/assets/nc-icons/arrow-up-right.svg b/packages/nc-gui/assets/nc-icons/arrow-up-right.svg new file mode 100644 index 0000000000..b0b9d3026b --- /dev/null +++ b/packages/nc-gui/assets/nc-icons/arrow-up-right.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/nc-gui/assets/nc-icons/control-panel.svg b/packages/nc-gui/assets/nc-icons/control-panel.svg new file mode 100644 index 0000000000..c566e17b96 --- /dev/null +++ b/packages/nc-gui/assets/nc-icons/control-panel.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/nc-gui/assets/nc-icons/discord.svg b/packages/nc-gui/assets/nc-icons/discord.svg new file mode 100644 index 0000000000..4e47c6e830 --- /dev/null +++ b/packages/nc-gui/assets/nc-icons/discord.svg @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/packages/nc-gui/assets/nc-icons/help.svg b/packages/nc-gui/assets/nc-icons/help.svg new file mode 100644 index 0000000000..febcdaf329 --- /dev/null +++ b/packages/nc-gui/assets/nc-icons/help.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/nc-gui/assets/nc-icons/home.svg b/packages/nc-gui/assets/nc-icons/home.svg new file mode 100644 index 0000000000..fe8bdeb742 --- /dev/null +++ b/packages/nc-gui/assets/nc-icons/home.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/nc-gui/assets/nc-icons/office.svg b/packages/nc-gui/assets/nc-icons/office.svg new file mode 100644 index 0000000000..b2b313eaf4 --- /dev/null +++ b/packages/nc-gui/assets/nc-icons/office.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/packages/nc-gui/assets/nc-icons/record.svg b/packages/nc-gui/assets/nc-icons/record.svg index 7515f62243..d4180461e4 100644 --- a/packages/nc-gui/assets/nc-icons/record.svg +++ b/packages/nc-gui/assets/nc-icons/record.svg @@ -1,12 +1,13 @@ - - - - - - - - - - + + + + + \ No newline at end of file diff --git a/packages/nc-gui/assets/nc-icons/reddit.svg b/packages/nc-gui/assets/nc-icons/reddit.svg new file mode 100644 index 0000000000..daaf9cdbe0 --- /dev/null +++ b/packages/nc-gui/assets/nc-icons/reddit.svg @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/packages/nc-gui/assets/nc-icons/settings.svg b/packages/nc-gui/assets/nc-icons/settings.svg new file mode 100644 index 0000000000..9c25d28f35 --- /dev/null +++ b/packages/nc-gui/assets/nc-icons/settings.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/packages/nc-gui/assets/nc-icons/slash.svg b/packages/nc-gui/assets/nc-icons/slash.svg new file mode 100644 index 0000000000..8e7e27899e --- /dev/null +++ b/packages/nc-gui/assets/nc-icons/slash.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/packages/nc-gui/assets/nc-icons/twitter.svg b/packages/nc-gui/assets/nc-icons/twitter.svg new file mode 100644 index 0000000000..5b065fc05a --- /dev/null +++ b/packages/nc-gui/assets/nc-icons/twitter.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/nc-gui/assets/nc-icons/workspace.svg b/packages/nc-gui/assets/nc-icons/workspace.svg new file mode 100644 index 0000000000..52c0757116 --- /dev/null +++ b/packages/nc-gui/assets/nc-icons/workspace.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/nc-gui/assets/style.scss b/packages/nc-gui/assets/style.scss index 0687d9ea7a..d2d59b0c02 100644 --- a/packages/nc-gui/assets/style.scss +++ b/packages/nc-gui/assets/style.scss @@ -37,7 +37,7 @@ body { } .rc-virtual-list-holder-inner { - @apply !px-1.5 + @apply !px-1.5; } .ant-layout-header { height: var(--topbar-height) !important; @@ -51,13 +51,17 @@ main { @apply m-0 h-full w-full bg-white; } - .nc-input-md { @apply !rounded-lg !py-2 !px-3 mb-1; } .mobile { - .nc-scrollbar-md, .nc-scrollbar-lg, .nc-scrollbar-x-md, .nc-scrollbar-dark-md, .nc-scrollbar-x-md-dark, .nc-scrollbar-x-lg { + .nc-scrollbar-md, + .nc-scrollbar-lg, + .nc-scrollbar-x-md, + .nc-scrollbar-dark-md, + .nc-scrollbar-x-md-dark, + .nc-scrollbar-x-lg { &::-webkit-scrollbar { width: 0px; } @@ -116,7 +120,6 @@ main { overflow-x: auto !important; scrollbar-width: thin !important; - &::-webkit-scrollbar { width: 4px; height: 4px; @@ -131,7 +134,6 @@ main { -webkit-border-radius: 10px; border-radius: 10px; - width: 4px; @apply bg-gray-200; } @@ -145,7 +147,6 @@ main { overflow-x: hidden; scrollbar-width: thin !important; - &::-webkit-scrollbar { width: 4px; height: 4px; @@ -177,7 +178,6 @@ main { overflow-x: auto !important; scrollbar-width: thin !important; - &::-webkit-scrollbar { width: 4px; height: 4px; @@ -192,14 +192,11 @@ main { -webkit-border-radius: 10px; border-radius: 10px; - width: 4px; - background-color: rgba(0, 0, 0, 0.3) - + background-color: rgba(0, 0, 0, 0.3); } &::-webkit-scrollbar-thumb:hover { - background-color: rgba(0, 0, 0, 0.4) - + background-color: rgba(0, 0, 0, 0.4); } } @@ -220,7 +217,6 @@ main { -webkit-border-radius: 10px; border-radius: 10px; - width: 8px; @apply bg-gray-200; } @@ -255,11 +251,11 @@ a { .rc-virtual-list-scrollbar { @apply !w-1; } - - .rc-virtual-list-scrollbar-thumb{ + + .rc-virtual-list-scrollbar-thumb { @apply !bg-gray-200; - &:hover{ + &:hover { @apply !bg-gray-300; } } @@ -465,9 +461,9 @@ a { .ant-dropdown-menu-submenu { @apply !py-0; - - &.ant-dropdown-menu-submenu-popup{ - @apply border-1 border-gray-200 + + &.ant-dropdown-menu-submenu-popup { + @apply border-1 border-gray-200; } .ant-dropdown-menu, .ant-menu { @@ -545,11 +541,11 @@ a { @apply bg-gray-300 bg-opacity-20; } -.ant-select-item-option:hover{ +.ant-select-item-option:hover { @apply !bg-gray-100; } -.ant-select-item-option-selected{ +.ant-select-item-option-selected { @apply !bg-white; } /* Hide the element with id nc-selected-item-icon */ @@ -658,7 +654,7 @@ a { } .nc-toolbar-dropdown { - @apply !rounded-2xl; + @apply !rounded-lg; } input[type='number'] { @@ -712,7 +708,8 @@ input[type='number'] { .nc-emoji { @apply xs:(text-lg); } - .material-symbols, .nc-icon { + .material-symbols, + .nc-icon { @apply !xs:(text-xl -mt-0.25); } @@ -723,12 +720,19 @@ input[type='number'] { .nc-sidebar-node-btn:not(.nc-sidebar-expand) { @apply !xs:(hidden); } + + .nc-sidebar-node-btn.nc-sidebar-expand { + @apply !xs:(flex-none border-1 border-gray-200 w-6.5 h-6.5 mr-1); + } } .nc-button.ant-btn.nc-sidebar-node-btn { @apply opacity-0 group-hover:(opacity-100) text-gray-600 hover:(bg-gray-400 bg-opacity-20 text-gray-900) duration-100; } +.nc-button.ant-btn.nc-sidebar-node-btn:not(.nc-sidebar-expand):not(.nc-sidebar-view-node-context-btn) { + @apply hidden group-hover:(inline-block); +} .nc-button.ant-btn.nc-sidebar-node-btn.nc-sidebar-expand { @apply xs:(opacity-100 hover:bg-gray-50); @@ -740,18 +744,18 @@ input[type='number'] { .ant-message-notice-content { @apply !rounded-md; - .ant-message-custom-content{ + .ant-message-custom-content { @apply flex items-center; } } -svg.nc-cell-icon, svg.nc-virtual-cell-icon { +svg.nc-cell-icon, +svg.nc-virtual-cell-icon { @apply w-1em h-1em flex-none; font-size: 1rem; } - -// For select type field list layout +// For select type field list layout .nc-field-layout-list { @apply !flex !flex-col !items-start w-full !space-y-0.5 !max-w-full; @@ -786,3 +790,25 @@ svg.nc-cell-icon, svg.nc-virtual-cell-icon { } } +.nc-toolbar-dropdown-search-field-input { + @apply !rounded-lg; + + .nc-search-icon { + @apply text-gray-400; + } + + &:hover .nc-search-icon, + &.ant-input-affix-wrapper-focused .nc-search-icon { + @apply text-gray-800; + } +} + +// switch - on tab focus show outline +.ant-switch:focus-visible, +.ant-switch-checked:focus-visible { + box-shadow: 0 0 0 2px #fff, 0 0 0 4px #3366ff; +} + +.text-nowrap{ + text-wrap: nowrap; +} \ No newline at end of file diff --git a/packages/nc-gui/components.d.ts b/packages/nc-gui/components.d.ts index 0b40fd5fd3..9f38e62348 100644 --- a/packages/nc-gui/components.d.ts +++ b/packages/nc-gui/components.d.ts @@ -52,6 +52,7 @@ declare module 'vue' { ARadio: typeof import('ant-design-vue/es')['Radio'] ARadioGroup: typeof import('ant-design-vue/es')['RadioGroup'] ARate: typeof import('ant-design-vue/es')['Rate'] + AResult: typeof import('ant-design-vue/es')['Result'] ARow: typeof import('ant-design-vue/es')['Row'] ASelect: typeof import('ant-design-vue/es')['Select'] ASelectOption: typeof import('ant-design-vue/es')['SelectOption'] diff --git a/packages/nc-gui/components/account/HeaderWithSorter.vue b/packages/nc-gui/components/account/HeaderWithSorter.vue new file mode 100644 index 0000000000..c3660259ca --- /dev/null +++ b/packages/nc-gui/components/account/HeaderWithSorter.vue @@ -0,0 +1,32 @@ + + + diff --git a/packages/nc-gui/components/account/UserList.vue b/packages/nc-gui/components/account/UserList.vue index 87458a8467..bf94e14ab2 100644 --- a/packages/nc-gui/components/account/UserList.vue +++ b/packages/nc-gui/components/account/UserList.vue @@ -28,7 +28,7 @@ const { user: loggedInUser } = useGlobal() const { copy } = useCopy() -const { sorts, sortDirection, loadSorts, saveOrUpdate, handleGetSortedData } = useUserSorts('Org') +const { sorts, loadSorts, handleGetSortedData, toggleSort } = useUserSorts('Org') const users = ref([]) @@ -198,21 +198,22 @@ const openDeleteModal = (user: UserType) => {
-
- - {{ $t('objects.users') }} - - -
-
- - {{ $t('general.access') }} - - -
+ + + +
{{ $t('labels.action') }}
diff --git a/packages/nc-gui/components/account/UserMenu.vue b/packages/nc-gui/components/account/UserMenu.vue deleted file mode 100644 index 7fb5e180aa..0000000000 --- a/packages/nc-gui/components/account/UserMenu.vue +++ /dev/null @@ -1,79 +0,0 @@ - - - - - diff --git a/packages/nc-gui/components/cell/Checkbox.vue b/packages/nc-gui/components/cell/Checkbox.vue index 155bd454d6..99f003f047 100644 --- a/packages/nc-gui/components/cell/Checkbox.vue +++ b/packages/nc-gui/components/cell/Checkbox.vue @@ -9,6 +9,7 @@ import { getMdiIcon, inject, parseProp, + rowHeightInPx, useBase, useSelectedCellKeyupListener, } from '#imports' @@ -47,6 +48,8 @@ const rowHeight = inject(RowHeightInj, ref()) const isSurveyForm = inject(IsSurveyFormInj, ref(false)) +const isGrid = inject(IsGridInj, ref(false)) + const checkboxMeta = computed(() => { return { icon: { @@ -110,7 +113,9 @@ useSelectedCellKeyupListener(active, (e) => { }" :style="{ height: - isForm || isExpandedFormOpen || isGallery || isEditColumnMenu ? undefined : `max(${(rowHeight || 1) * 1.8}rem, 41px)`, + isGrid && !isForm && !isExpandedFormOpen && !isEditColumnMenu + ? `${!rowHeight || rowHeight === 1 ? rowHeightInPx['1'] - 4 : rowHeightInPx[`${rowHeight}`] - 20}px` + : undefined, }" :tabindex="readOnly ? -1 : 0" @click="onClick(false, $event)" diff --git a/packages/nc-gui/components/cell/ClampedText.vue b/packages/nc-gui/components/cell/ClampedText.vue index d01c6fb0f4..4fe2b84733 100644 --- a/packages/nc-gui/components/cell/ClampedText.vue +++ b/packages/nc-gui/components/cell/ClampedText.vue @@ -6,7 +6,12 @@ const props = defineProps<{