mirror of
https://github.com/umami-software/umami.git
synced 2026-02-19 20:15:41 +01:00
commit
16cf709bc9
191 changed files with 17482 additions and 3307 deletions
28
.github/workflows/cd-cloud.yml
vendored
Normal file
28
.github/workflows/cd-cloud.yml
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
name: Create docker images
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- analytics
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Build, push, and deploy
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Generate random hash
|
||||||
|
id: random_hash
|
||||||
|
run: echo "hash=$(openssl rand -hex 4)" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- uses: mr-smithers-excellent/docker-build-push@v6
|
||||||
|
name: Build & push Docker image to docker.io
|
||||||
|
with:
|
||||||
|
image: umamisoftware/umami
|
||||||
|
tags: cloud-${{ steps.random_hash.outputs.hash }}, cloud-latest
|
||||||
|
buildArgs: DATABASE_TYPE=postgresql
|
||||||
|
registry: docker.io
|
||||||
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
19
.github/workflows/cd-manual.yml
vendored
19
.github/workflows/cd-manual.yml
vendored
|
|
@ -20,11 +20,26 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Extract version parts from input
|
||||||
|
id: extract_version
|
||||||
|
run: |
|
||||||
|
echo "version=$(echo ${{ github.event.inputs.version }})" >> $GITHUB_ENV
|
||||||
|
echo "major=$(echo ${{ github.event.inputs.version }} | cut -d. -f1)" >> $GITHUB_ENV
|
||||||
|
echo "minor=$(echo ${{ github.event.inputs.version }} | cut -d. -f2)" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Generate tags
|
||||||
|
id: generate_tags
|
||||||
|
run: |
|
||||||
|
echo "tag_major=$(echo ${{ matrix.db-type }}-${{ env.major }})" >> $GITHUB_ENV
|
||||||
|
echo "tag_minor=$(echo ${{ matrix.db-type }}-${{ env.major }}.${{ env.minor }})" >> $GITHUB_ENV
|
||||||
|
echo "tag_patch=$(echo ${{ matrix.db-type }}-${{ env.version }})" >> $GITHUB_ENV
|
||||||
|
echo "tag_latest=$(echo ${{ matrix.db-type }}-latest)" >> $GITHUB_ENV
|
||||||
|
|
||||||
- uses: mr-smithers-excellent/docker-build-push@v6
|
- uses: mr-smithers-excellent/docker-build-push@v6
|
||||||
name: Build & push Docker image to ghcr.io for ${{ matrix.db-type }}
|
name: Build & push Docker image to ghcr.io for ${{ matrix.db-type }}
|
||||||
with:
|
with:
|
||||||
image: umami
|
image: umami
|
||||||
tags: ${{ matrix.db-type }}-${{ inputs.version }}, ${{ matrix.db-type }}-latest
|
tags: ${{ env.tag_major }}, ${{ env.tag_minor }}, ${{ env.tag_patch }}, ${{ env.tag_latest }}
|
||||||
buildArgs: DATABASE_TYPE=${{ matrix.db-type }}
|
buildArgs: DATABASE_TYPE=${{ matrix.db-type }}
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
multiPlatform: true
|
multiPlatform: true
|
||||||
|
|
@ -36,7 +51,7 @@ jobs:
|
||||||
name: Build & push Docker image to docker.io for ${{ matrix.db-type }}
|
name: Build & push Docker image to docker.io for ${{ matrix.db-type }}
|
||||||
with:
|
with:
|
||||||
image: umamisoftware/umami
|
image: umamisoftware/umami
|
||||||
tags: ${{ matrix.db-type }}-${{ inputs.version }}, ${{ matrix.db-type }}-latest
|
tags: ${{ env.tag_major }}, ${{ env.tag_minor }}, ${{ env.tag_patch }}, ${{ env.tag_latest }}
|
||||||
buildArgs: DATABASE_TYPE=${{ matrix.db-type }}
|
buildArgs: DATABASE_TYPE=${{ matrix.db-type }}
|
||||||
registry: docker.io
|
registry: docker.io
|
||||||
username: ${{ secrets.DOCKER_USERNAME }}
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
|
|
|
||||||
14
.github/workflows/cd.yml
vendored
14
.github/workflows/cd.yml
vendored
|
|
@ -17,14 +17,21 @@ jobs:
|
||||||
|
|
||||||
- name: Set env
|
- name: Set env
|
||||||
run: |
|
run: |
|
||||||
echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
|
|
||||||
echo "NOW=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_ENV
|
echo "NOW=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Generate tags
|
||||||
|
id: generate_tags
|
||||||
|
run: |
|
||||||
|
echo "tag_patch=$(echo ${{ matrix.db-type }})-${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
|
||||||
|
echo "tag_minor=$(echo ${{ matrix.db-type }})-$(echo ${GITHUB_REF#refs/tags/} | cut -d. -f1,2)" >> $GITHUB_ENV
|
||||||
|
echo "tag_major=$(echo ${{ matrix.db-type }})-$(echo ${GITHUB_REF#refs/tags/} | cut -d. -f1)" >> $GITHUB_ENV
|
||||||
|
echo "tag_latest=$(echo ${{ matrix.db-type }})-latest" >> $GITHUB_ENV
|
||||||
|
|
||||||
- uses: mr-smithers-excellent/docker-build-push@v6
|
- uses: mr-smithers-excellent/docker-build-push@v6
|
||||||
name: Build & push Docker image to ghcr.io for ${{ matrix.db-type }}
|
name: Build & push Docker image to ghcr.io for ${{ matrix.db-type }}
|
||||||
with:
|
with:
|
||||||
image: umami
|
image: umami
|
||||||
tags: ${{ matrix.db-type }}-${{ env.RELEASE_VERSION }}, ${{ matrix.db-type }}-latest
|
tags: ${{ env.tag_major }}, ${{ env.tag_minor }}, ${{ env.tag_patch }}, ${{ env.tag_latest }}
|
||||||
buildArgs: DATABASE_TYPE=${{ matrix.db-type }}
|
buildArgs: DATABASE_TYPE=${{ matrix.db-type }}
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
multiPlatform: true
|
multiPlatform: true
|
||||||
|
|
@ -32,12 +39,11 @@ jobs:
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
|
||||||
- uses: mr-smithers-excellent/docker-build-push@v6
|
- uses: mr-smithers-excellent/docker-build-push@v6
|
||||||
name: Build & push Docker image to docker.io for ${{ matrix.db-type }}
|
name: Build & push Docker image to docker.io for ${{ matrix.db-type }}
|
||||||
with:
|
with:
|
||||||
image: umamisoftware/umami
|
image: umamisoftware/umami
|
||||||
tags: ${{ matrix.db-type }}-${{ env.RELEASE_VERSION }}, ${{ matrix.db-type }}-latest
|
tags: ${{ env.tag_major }}, ${{ env.tag_minor }}, ${{ env.tag_patch }}, ${{ env.tag_latest }}
|
||||||
buildArgs: DATABASE_TYPE=${{ matrix.db-type }}
|
buildArgs: DATABASE_TYPE=${{ matrix.db-type }}
|
||||||
registry: docker.io
|
registry: docker.io
|
||||||
username: ${{ secrets.DOCKER_USERNAME }}
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
|
|
|
||||||
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
|
|
@ -16,9 +16,9 @@ jobs:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- node-version: 18.17
|
- node-version: 18.18
|
||||||
db-type: postgresql
|
db-type: postgresql
|
||||||
- node-version: 18.17
|
- node-version: 18.18
|
||||||
db-type: mysql
|
db-type: mysql
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ RUN addgroup --system --gid 1001 nodejs
|
||||||
RUN adduser --system --uid 1001 nextjs
|
RUN adduser --system --uid 1001 nextjs
|
||||||
|
|
||||||
RUN set -x \
|
RUN set -x \
|
||||||
&& apk add --no-cache curl \
|
&& apk add --no-cache curl openssl \
|
||||||
&& yarn add npm-run-all dotenv semver prisma@5.17.0
|
&& yarn add npm-run-all dotenv semver prisma@5.17.0
|
||||||
|
|
||||||
# You only need to copy next.config.js if you are NOT using the default configuration
|
# You only need to copy next.config.js if you are NOT using the default configuration
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="https://umami.is/images/umami-logo.png" alt="Umami Logo" width="100">
|
<img src="https://content.umami.is/website/images/umami-logo.png" alt="Umami Logo" width="100">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h1 align="center">Umami</h1>
|
<h1 align="center">Umami</h1>
|
||||||
|
|
@ -35,8 +35,8 @@ A detailed getting started guide can be found at [umami.is/docs](https://umami.i
|
||||||
|
|
||||||
### Requirements
|
### Requirements
|
||||||
|
|
||||||
- A server with Node.js version 16.13 or newer
|
- A server with Node.js version 18.18 or newer
|
||||||
- A database. Umami supports [MySQL](https://www.mysql.com/) (minimum v8.0) and [PostgreSQL](https://www.postgresql.org/) (minimum v12.14) databases.
|
- A database. Umami supports [MariaDB](https://www.mariadb.org/) (minimum v10.5), [MySQL](https://www.mysql.com/) (minimum v8.0) and [PostgreSQL](https://www.postgresql.org/) (minimum v12.14) databases.
|
||||||
|
|
||||||
### Install Yarn
|
### Install Yarn
|
||||||
|
|
||||||
|
|
|
||||||
77
db/clickhouse/migrations/04_add_tag.sql
Normal file
77
db/clickhouse/migrations/04_add_tag.sql
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
-- add tag column
|
||||||
|
ALTER TABLE umami.website_event ADD COLUMN "tag" String AFTER "event_name";
|
||||||
|
ALTER TABLE umami.website_event_stats_hourly ADD COLUMN "tag" SimpleAggregateFunction(groupArrayArray, Array(String)) AFTER "max_time";
|
||||||
|
|
||||||
|
-- update materialized view
|
||||||
|
DROP TABLE umami.website_event_stats_hourly_mv;
|
||||||
|
|
||||||
|
CREATE MATERIALIZED VIEW umami.website_event_stats_hourly_mv
|
||||||
|
TO umami.website_event_stats_hourly
|
||||||
|
AS
|
||||||
|
SELECT
|
||||||
|
website_id,
|
||||||
|
session_id,
|
||||||
|
visit_id,
|
||||||
|
hostname,
|
||||||
|
browser,
|
||||||
|
os,
|
||||||
|
device,
|
||||||
|
screen,
|
||||||
|
language,
|
||||||
|
country,
|
||||||
|
subdivision1,
|
||||||
|
city,
|
||||||
|
entry_url,
|
||||||
|
exit_url,
|
||||||
|
url_paths as url_path,
|
||||||
|
url_query,
|
||||||
|
referrer_domain,
|
||||||
|
page_title,
|
||||||
|
event_type,
|
||||||
|
event_name,
|
||||||
|
views,
|
||||||
|
min_time,
|
||||||
|
max_time,
|
||||||
|
tag,
|
||||||
|
timestamp as created_at
|
||||||
|
FROM (SELECT
|
||||||
|
website_id,
|
||||||
|
session_id,
|
||||||
|
visit_id,
|
||||||
|
hostname,
|
||||||
|
browser,
|
||||||
|
os,
|
||||||
|
device,
|
||||||
|
screen,
|
||||||
|
language,
|
||||||
|
country,
|
||||||
|
subdivision1,
|
||||||
|
city,
|
||||||
|
argMinState(url_path, created_at) entry_url,
|
||||||
|
argMaxState(url_path, created_at) exit_url,
|
||||||
|
arrayFilter(x -> x != '', groupArray(url_path)) as url_paths,
|
||||||
|
arrayFilter(x -> x != '', groupArray(url_query)) url_query,
|
||||||
|
arrayFilter(x -> x != '', groupArray(referrer_domain)) referrer_domain,
|
||||||
|
arrayFilter(x -> x != '', groupArray(page_title)) page_title,
|
||||||
|
event_type,
|
||||||
|
if(event_type = 2, groupArray(event_name), []) event_name,
|
||||||
|
sumIf(1, event_type = 1) views,
|
||||||
|
min(created_at) min_time,
|
||||||
|
max(created_at) max_time,
|
||||||
|
arrayFilter(x -> x != '', groupArray(tag)) tag,
|
||||||
|
toStartOfHour(created_at) timestamp
|
||||||
|
FROM umami.website_event
|
||||||
|
GROUP BY website_id,
|
||||||
|
session_id,
|
||||||
|
visit_id,
|
||||||
|
hostname,
|
||||||
|
browser,
|
||||||
|
os,
|
||||||
|
device,
|
||||||
|
screen,
|
||||||
|
language,
|
||||||
|
country,
|
||||||
|
subdivision1,
|
||||||
|
city,
|
||||||
|
event_type,
|
||||||
|
timestamp);
|
||||||
|
|
@ -26,6 +26,7 @@ CREATE TABLE umami.website_event
|
||||||
--events
|
--events
|
||||||
event_type UInt32,
|
event_type UInt32,
|
||||||
event_name String,
|
event_name String,
|
||||||
|
tag String,
|
||||||
created_at DateTime('UTC'),
|
created_at DateTime('UTC'),
|
||||||
job_id Nullable(UUID)
|
job_id Nullable(UUID)
|
||||||
)
|
)
|
||||||
|
|
@ -96,6 +97,7 @@ CREATE TABLE umami.website_event_stats_hourly
|
||||||
views SimpleAggregateFunction(sum, UInt64),
|
views SimpleAggregateFunction(sum, UInt64),
|
||||||
min_time SimpleAggregateFunction(min, DateTime('UTC')),
|
min_time SimpleAggregateFunction(min, DateTime('UTC')),
|
||||||
max_time SimpleAggregateFunction(max, DateTime('UTC')),
|
max_time SimpleAggregateFunction(max, DateTime('UTC')),
|
||||||
|
tag SimpleAggregateFunction(groupArrayArray, Array(String)),
|
||||||
created_at Datetime('UTC')
|
created_at Datetime('UTC')
|
||||||
)
|
)
|
||||||
ENGINE = AggregatingMergeTree
|
ENGINE = AggregatingMergeTree
|
||||||
|
|
@ -136,6 +138,7 @@ SELECT
|
||||||
views,
|
views,
|
||||||
min_time,
|
min_time,
|
||||||
max_time,
|
max_time,
|
||||||
|
tag,
|
||||||
timestamp as created_at
|
timestamp as created_at
|
||||||
FROM (SELECT
|
FROM (SELECT
|
||||||
website_id,
|
website_id,
|
||||||
|
|
@ -161,6 +164,7 @@ FROM (SELECT
|
||||||
sumIf(1, event_type = 1) views,
|
sumIf(1, event_type = 1) views,
|
||||||
min(created_at) min_time,
|
min(created_at) min_time,
|
||||||
max(created_at) max_time,
|
max(created_at) max_time,
|
||||||
|
arrayFilter(x -> x != '', groupArray(tag)) tag,
|
||||||
toStartOfHour(created_at) timestamp
|
toStartOfHour(created_at) timestamp
|
||||||
FROM umami.website_event
|
FROM umami.website_event
|
||||||
GROUP BY website_id,
|
GROUP BY website_id,
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ CREATE TABLE `user` (
|
||||||
UNIQUE INDEX `user_user_id_key`(`user_id`),
|
UNIQUE INDEX `user_user_id_key`(`user_id`),
|
||||||
UNIQUE INDEX `user_username_key`(`username`),
|
UNIQUE INDEX `user_username_key`(`username`),
|
||||||
PRIMARY KEY (`user_id`)
|
PRIMARY KEY (`user_id`)
|
||||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
) ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
-- CreateTable
|
-- CreateTable
|
||||||
CREATE TABLE `session` (
|
CREATE TABLE `session` (
|
||||||
|
|
@ -33,7 +33,7 @@ CREATE TABLE `session` (
|
||||||
INDEX `session_created_at_idx`(`created_at`),
|
INDEX `session_created_at_idx`(`created_at`),
|
||||||
INDEX `session_website_id_idx`(`website_id`),
|
INDEX `session_website_id_idx`(`website_id`),
|
||||||
PRIMARY KEY (`session_id`)
|
PRIMARY KEY (`session_id`)
|
||||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
) ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
-- CreateTable
|
-- CreateTable
|
||||||
CREATE TABLE `website` (
|
CREATE TABLE `website` (
|
||||||
|
|
@ -53,7 +53,7 @@ CREATE TABLE `website` (
|
||||||
INDEX `website_created_at_idx`(`created_at`),
|
INDEX `website_created_at_idx`(`created_at`),
|
||||||
INDEX `website_share_id_idx`(`share_id`),
|
INDEX `website_share_id_idx`(`share_id`),
|
||||||
PRIMARY KEY (`website_id`)
|
PRIMARY KEY (`website_id`)
|
||||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
) ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
-- CreateTable
|
-- CreateTable
|
||||||
CREATE TABLE `website_event` (
|
CREATE TABLE `website_event` (
|
||||||
|
|
@ -76,7 +76,7 @@ CREATE TABLE `website_event` (
|
||||||
INDEX `website_event_website_id_created_at_idx`(`website_id`, `created_at`),
|
INDEX `website_event_website_id_created_at_idx`(`website_id`, `created_at`),
|
||||||
INDEX `website_event_website_id_session_id_created_at_idx`(`website_id`, `session_id`, `created_at`),
|
INDEX `website_event_website_id_session_id_created_at_idx`(`website_id`, `session_id`, `created_at`),
|
||||||
PRIMARY KEY (`event_id`)
|
PRIMARY KEY (`event_id`)
|
||||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
) ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
-- CreateTable
|
-- CreateTable
|
||||||
CREATE TABLE `event_data` (
|
CREATE TABLE `event_data` (
|
||||||
|
|
@ -95,7 +95,7 @@ CREATE TABLE `event_data` (
|
||||||
INDEX `event_data_website_event_id_idx`(`website_event_id`),
|
INDEX `event_data_website_event_id_idx`(`website_event_id`),
|
||||||
INDEX `event_data_website_id_website_event_id_created_at_idx`(`website_id`, `website_event_id`, `created_at`),
|
INDEX `event_data_website_id_website_event_id_created_at_idx`(`website_id`, `website_event_id`, `created_at`),
|
||||||
PRIMARY KEY (`event_id`)
|
PRIMARY KEY (`event_id`)
|
||||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
) ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
-- CreateTable
|
-- CreateTable
|
||||||
CREATE TABLE `team` (
|
CREATE TABLE `team` (
|
||||||
|
|
@ -109,7 +109,7 @@ CREATE TABLE `team` (
|
||||||
UNIQUE INDEX `team_access_code_key`(`access_code`),
|
UNIQUE INDEX `team_access_code_key`(`access_code`),
|
||||||
INDEX `team_access_code_idx`(`access_code`),
|
INDEX `team_access_code_idx`(`access_code`),
|
||||||
PRIMARY KEY (`team_id`)
|
PRIMARY KEY (`team_id`)
|
||||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
) ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
-- CreateTable
|
-- CreateTable
|
||||||
CREATE TABLE `team_user` (
|
CREATE TABLE `team_user` (
|
||||||
|
|
@ -124,7 +124,7 @@ CREATE TABLE `team_user` (
|
||||||
INDEX `team_user_team_id_idx`(`team_id`),
|
INDEX `team_user_team_id_idx`(`team_id`),
|
||||||
INDEX `team_user_user_id_idx`(`user_id`),
|
INDEX `team_user_user_id_idx`(`user_id`),
|
||||||
PRIMARY KEY (`team_user_id`)
|
PRIMARY KEY (`team_user_id`)
|
||||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
) ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
-- CreateTable
|
-- CreateTable
|
||||||
CREATE TABLE `team_website` (
|
CREATE TABLE `team_website` (
|
||||||
|
|
@ -137,7 +137,7 @@ CREATE TABLE `team_website` (
|
||||||
INDEX `team_website_team_id_idx`(`team_id`),
|
INDEX `team_website_team_id_idx`(`team_id`),
|
||||||
INDEX `team_website_website_id_idx`(`website_id`),
|
INDEX `team_website_website_id_idx`(`website_id`),
|
||||||
PRIMARY KEY (`team_website_id`)
|
PRIMARY KEY (`team_website_id`)
|
||||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
) ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
-- AddSystemUser
|
-- AddSystemUser
|
||||||
INSERT INTO user (user_id, username, role, password) VALUES ('41e2b680-648e-4b09-bcd7-3e2b10c06264' , 'admin', 'admin', '$2b$10$BUli0c.muyCW1ErNJc3jL.vFRFtFJWrT8/GcR4A.sUdCznaXiqFXa');
|
INSERT INTO user (user_id, username, role, password) VALUES ('41e2b680-648e-4b09-bcd7-3e2b10c06264' , 'admin', 'admin', '$2b$10$BUli0c.muyCW1ErNJc3jL.vFRFtFJWrT8/GcR4A.sUdCznaXiqFXa');
|
||||||
|
|
@ -21,7 +21,7 @@ CREATE TABLE `session_data` (
|
||||||
INDEX `session_data_website_id_idx`(`website_id`),
|
INDEX `session_data_website_id_idx`(`website_id`),
|
||||||
INDEX `session_data_session_id_idx`(`session_id`),
|
INDEX `session_data_session_id_idx`(`session_id`),
|
||||||
PRIMARY KEY (`session_data_id`)
|
PRIMARY KEY (`session_data_id`)
|
||||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
) ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
-- CreateTable
|
-- CreateTable
|
||||||
CREATE TABLE `report` (
|
CREATE TABLE `report` (
|
||||||
|
|
@ -41,7 +41,7 @@ CREATE TABLE `report` (
|
||||||
INDEX `report_type_idx`(`type`),
|
INDEX `report_type_idx`(`type`),
|
||||||
INDEX `report_name_idx`(`name`),
|
INDEX `report_name_idx`(`name`),
|
||||||
PRIMARY KEY (`report_id`)
|
PRIMARY KEY (`report_id`)
|
||||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
) ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
-- EventData migration
|
-- EventData migration
|
||||||
UPDATE event_data
|
UPDATE event_data
|
||||||
|
|
|
||||||
5
db/mysql/migrations/07_add_tag/migration.sql
Normal file
5
db/mysql/migrations/07_add_tag/migration.sql
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `website_event` ADD COLUMN `tag` VARCHAR(50) NULL;
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX `website_event_website_id_created_at_tag_idx` ON `website_event`(`website_id`, `created_at`, `tag`);
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
generator client {
|
generator client {
|
||||||
provider = "prisma-client-js"
|
provider = "prisma-client-js"
|
||||||
|
binaryTargets = ["native", "linux-musl-openssl-3.0.x", "linux-musl-arm64-openssl-3.0.x"]
|
||||||
}
|
}
|
||||||
|
|
||||||
datasource db {
|
datasource db {
|
||||||
|
|
@ -19,10 +20,10 @@ model User {
|
||||||
updatedAt DateTime? @updatedAt @map("updated_at") @db.Timestamp(0)
|
updatedAt DateTime? @updatedAt @map("updated_at") @db.Timestamp(0)
|
||||||
deletedAt DateTime? @map("deleted_at") @db.Timestamp(0)
|
deletedAt DateTime? @map("deleted_at") @db.Timestamp(0)
|
||||||
|
|
||||||
websiteUser Website[] @relation("user")
|
websiteUser Website[] @relation("user")
|
||||||
websiteCreateUser Website[] @relation("createUser")
|
websiteCreateUser Website[] @relation("createUser")
|
||||||
teamUser TeamUser[]
|
teamUser TeamUser[]
|
||||||
report Report[]
|
report Report[]
|
||||||
|
|
||||||
@@map("user")
|
@@map("user")
|
||||||
}
|
}
|
||||||
|
|
@ -102,6 +103,7 @@ model WebsiteEvent {
|
||||||
pageTitle String? @map("page_title") @db.VarChar(500)
|
pageTitle String? @map("page_title") @db.VarChar(500)
|
||||||
eventType Int @default(1) @map("event_type") @db.UnsignedInt
|
eventType Int @default(1) @map("event_type") @db.UnsignedInt
|
||||||
eventName String? @map("event_name") @db.VarChar(50)
|
eventName String? @map("event_name") @db.VarChar(50)
|
||||||
|
tag String? @db.VarChar(50)
|
||||||
|
|
||||||
eventData EventData[]
|
eventData EventData[]
|
||||||
session Session @relation(fields: [sessionId], references: [id])
|
session Session @relation(fields: [sessionId], references: [id])
|
||||||
|
|
@ -116,6 +118,7 @@ model WebsiteEvent {
|
||||||
@@index([websiteId, createdAt, referrerDomain])
|
@@index([websiteId, createdAt, referrerDomain])
|
||||||
@@index([websiteId, createdAt, pageTitle])
|
@@index([websiteId, createdAt, pageTitle])
|
||||||
@@index([websiteId, createdAt, eventName])
|
@@index([websiteId, createdAt, eventName])
|
||||||
|
@@index([websiteId, createdAt, tag])
|
||||||
@@index([websiteId, sessionId, createdAt])
|
@@index([websiteId, sessionId, createdAt])
|
||||||
@@index([websiteId, visitId, createdAt])
|
@@index([websiteId, visitId, createdAt])
|
||||||
@@map("website_event")
|
@@map("website_event")
|
||||||
|
|
@ -174,8 +177,8 @@ model Team {
|
||||||
updatedAt DateTime? @updatedAt @map("updated_at") @db.Timestamp(0)
|
updatedAt DateTime? @updatedAt @map("updated_at") @db.Timestamp(0)
|
||||||
deletedAt DateTime? @map("deleted_at") @db.Timestamp(0)
|
deletedAt DateTime? @map("deleted_at") @db.Timestamp(0)
|
||||||
|
|
||||||
website Website[]
|
website Website[]
|
||||||
teamUser TeamUser[]
|
teamUser TeamUser[]
|
||||||
|
|
||||||
@@index([accessCode])
|
@@index([accessCode])
|
||||||
@@map("team")
|
@@map("team")
|
||||||
|
|
|
||||||
5
db/postgresql/migrations/07_add_tag/migration.sql
Normal file
5
db/postgresql/migrations/07_add_tag/migration.sql
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "website_event" ADD COLUMN "tag" VARCHAR(50);
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "website_event_website_id_created_at_tag_idx" ON "website_event"("website_id", "created_at", "tag");
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
generator client {
|
generator client {
|
||||||
provider = "prisma-client-js"
|
provider = "prisma-client-js"
|
||||||
|
binaryTargets = ["native", "linux-musl-openssl-3.0.x", "linux-musl-arm64-openssl-3.0.x"]
|
||||||
}
|
}
|
||||||
|
|
||||||
datasource db {
|
datasource db {
|
||||||
|
|
@ -19,8 +20,8 @@ model User {
|
||||||
updatedAt DateTime? @updatedAt @map("updated_at") @db.Timestamptz(6)
|
updatedAt DateTime? @updatedAt @map("updated_at") @db.Timestamptz(6)
|
||||||
deletedAt DateTime? @map("deleted_at") @db.Timestamptz(6)
|
deletedAt DateTime? @map("deleted_at") @db.Timestamptz(6)
|
||||||
|
|
||||||
websiteUser Website[] @relation("user")
|
websiteUser Website[] @relation("user")
|
||||||
websiteCreateUser Website[] @relation("createUser")
|
websiteCreateUser Website[] @relation("createUser")
|
||||||
teamUser TeamUser[]
|
teamUser TeamUser[]
|
||||||
report Report[]
|
report Report[]
|
||||||
|
|
||||||
|
|
@ -102,6 +103,7 @@ model WebsiteEvent {
|
||||||
pageTitle String? @map("page_title") @db.VarChar(500)
|
pageTitle String? @map("page_title") @db.VarChar(500)
|
||||||
eventType Int @default(1) @map("event_type") @db.Integer
|
eventType Int @default(1) @map("event_type") @db.Integer
|
||||||
eventName String? @map("event_name") @db.VarChar(50)
|
eventName String? @map("event_name") @db.VarChar(50)
|
||||||
|
tag String? @db.VarChar(50)
|
||||||
|
|
||||||
eventData EventData[]
|
eventData EventData[]
|
||||||
session Session @relation(fields: [sessionId], references: [id])
|
session Session @relation(fields: [sessionId], references: [id])
|
||||||
|
|
@ -116,6 +118,7 @@ model WebsiteEvent {
|
||||||
@@index([websiteId, createdAt, referrerDomain])
|
@@index([websiteId, createdAt, referrerDomain])
|
||||||
@@index([websiteId, createdAt, pageTitle])
|
@@index([websiteId, createdAt, pageTitle])
|
||||||
@@index([websiteId, createdAt, eventName])
|
@@index([websiteId, createdAt, eventName])
|
||||||
|
@@index([websiteId, createdAt, tag])
|
||||||
@@index([websiteId, sessionId, createdAt])
|
@@index([websiteId, sessionId, createdAt])
|
||||||
@@index([websiteId, visitId, createdAt])
|
@@index([websiteId, visitId, createdAt])
|
||||||
@@map("website_event")
|
@@map("website_event")
|
||||||
|
|
@ -147,7 +150,7 @@ model SessionData {
|
||||||
id String @id() @map("session_data_id") @db.Uuid
|
id String @id() @map("session_data_id") @db.Uuid
|
||||||
websiteId String @map("website_id") @db.Uuid
|
websiteId String @map("website_id") @db.Uuid
|
||||||
sessionId String @map("session_id") @db.Uuid
|
sessionId String @map("session_id") @db.Uuid
|
||||||
dataKey String @map("data_key") @db.VarChar(500)
|
dataKey String @map("data_key") @db.VarChar(500)
|
||||||
stringValue String? @map("string_value") @db.VarChar(500)
|
stringValue String? @map("string_value") @db.VarChar(500)
|
||||||
numberValue Decimal? @map("number_value") @db.Decimal(19, 4)
|
numberValue Decimal? @map("number_value") @db.Decimal(19, 4)
|
||||||
dateValue DateTime? @map("date_value") @db.Timestamptz(6)
|
dateValue DateTime? @map("date_value") @db.Timestamptz(6)
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ services:
|
||||||
depends_on:
|
depends_on:
|
||||||
db:
|
db:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
init: true
|
||||||
restart: always
|
restart: always
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD-SHELL", "curl http://localhost:3000/api/heartbeat"]
|
test: ["CMD-SHELL", "curl http://localhost:3000/api/heartbeat"]
|
||||||
|
|
|
||||||
2
next-env.d.ts
vendored
2
next-env.d.ts
vendored
|
|
@ -3,4 +3,4 @@
|
||||||
/// <reference types="next/navigation-types/compat/navigation" />
|
/// <reference types="next/navigation-types/compat/navigation" />
|
||||||
|
|
||||||
// NOTE: This file should not be edited
|
// NOTE: This file should not be edited
|
||||||
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information.
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@ require('dotenv').config();
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const pkg = require('./package.json');
|
const pkg = require('./package.json');
|
||||||
|
|
||||||
|
const TRACKER_SCRIPT = '/script.js';
|
||||||
|
|
||||||
const basePath = process.env.BASE_PATH;
|
const basePath = process.env.BASE_PATH;
|
||||||
const collectApiEndpoint = process.env.COLLECT_API_ENDPOINT;
|
const collectApiEndpoint = process.env.COLLECT_API_ENDPOINT;
|
||||||
const cloudMode = process.env.CLOUD_MODE;
|
const cloudMode = process.env.CLOUD_MODE;
|
||||||
|
|
@ -14,6 +16,7 @@ const forceSSL = process.env.FORCE_SSL;
|
||||||
const frameAncestors = process.env.ALLOWED_FRAME_URLS;
|
const frameAncestors = process.env.ALLOWED_FRAME_URLS;
|
||||||
const privateMode = process.env.PRIVATE_MODE;
|
const privateMode = process.env.PRIVATE_MODE;
|
||||||
const trackerScriptName = process.env.TRACKER_SCRIPT_NAME;
|
const trackerScriptName = process.env.TRACKER_SCRIPT_NAME;
|
||||||
|
const trackerScriptURL = process.env.TRACKER_SCRIPT_URL;
|
||||||
|
|
||||||
const contentSecurityPolicy = [
|
const contentSecurityPolicy = [
|
||||||
`default-src 'self'`,
|
`default-src 'self'`,
|
||||||
|
|
@ -24,7 +27,7 @@ const contentSecurityPolicy = [
|
||||||
`frame-ancestors 'self' ${frameAncestors}`,
|
`frame-ancestors 'self' ${frameAncestors}`,
|
||||||
];
|
];
|
||||||
|
|
||||||
const headers = [
|
const defaultHeaders = [
|
||||||
{
|
{
|
||||||
key: 'X-DNS-Prefetch-Control',
|
key: 'X-DNS-Prefetch-Control',
|
||||||
value: 'on',
|
value: 'on',
|
||||||
|
|
@ -39,14 +42,43 @@ const headers = [
|
||||||
];
|
];
|
||||||
|
|
||||||
if (forceSSL) {
|
if (forceSSL) {
|
||||||
headers.push({
|
defaultHeaders.push({
|
||||||
key: 'Strict-Transport-Security',
|
key: 'Strict-Transport-Security',
|
||||||
value: 'max-age=63072000; includeSubDomains; preload',
|
value: 'max-age=63072000; includeSubDomains; preload',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const trackerHeaders = [
|
||||||
|
{
|
||||||
|
key: 'Access-Control-Allow-Origin',
|
||||||
|
value: '*',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'Cache-Control',
|
||||||
|
value: 'public, max-age=86400, must-revalidate',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const headers = [
|
||||||
|
{
|
||||||
|
source: '/:path*',
|
||||||
|
headers: defaultHeaders,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
source: TRACKER_SCRIPT,
|
||||||
|
headers: trackerHeaders,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
const rewrites = [];
|
const rewrites = [];
|
||||||
|
|
||||||
|
if (trackerScriptURL) {
|
||||||
|
rewrites.push({
|
||||||
|
source: TRACKER_SCRIPT,
|
||||||
|
destination: trackerScriptURL,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (collectApiEndpoint) {
|
if (collectApiEndpoint) {
|
||||||
rewrites.push({
|
rewrites.push({
|
||||||
source: collectApiEndpoint,
|
source: collectApiEndpoint,
|
||||||
|
|
@ -54,19 +86,6 @@ if (collectApiEndpoint) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (trackerScriptName) {
|
|
||||||
const names = trackerScriptName?.split(',').map(name => name.trim());
|
|
||||||
|
|
||||||
if (names) {
|
|
||||||
names.forEach(name => {
|
|
||||||
rewrites.push({
|
|
||||||
source: `/${name.replace(/^\/+/, '')}`,
|
|
||||||
destination: '/script.js',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const redirects = [
|
const redirects = [
|
||||||
{
|
{
|
||||||
source: '/settings',
|
source: '/settings',
|
||||||
|
|
@ -85,6 +104,27 @@ const redirects = [
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// Adding rewrites + headers for all alternative tracker script names.
|
||||||
|
if (trackerScriptName) {
|
||||||
|
const names = trackerScriptName?.split(',').map(name => name.trim());
|
||||||
|
|
||||||
|
if (names) {
|
||||||
|
names.forEach(name => {
|
||||||
|
const normalizedSource = `/${name.replace(/^\/+/, '')}`;
|
||||||
|
|
||||||
|
rewrites.push({
|
||||||
|
source: normalizedSource,
|
||||||
|
destination: TRACKER_SCRIPT,
|
||||||
|
});
|
||||||
|
|
||||||
|
headers.push({
|
||||||
|
source: normalizedSource,
|
||||||
|
headers: trackerHeaders,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (cloudMode && cloudUrl) {
|
if (cloudMode && cloudUrl) {
|
||||||
redirects.push({
|
redirects.push({
|
||||||
source: '/settings/:path*',
|
source: '/settings/:path*',
|
||||||
|
|
@ -153,12 +193,7 @@ const config = {
|
||||||
return config;
|
return config;
|
||||||
},
|
},
|
||||||
async headers() {
|
async headers() {
|
||||||
return [
|
return headers;
|
||||||
{
|
|
||||||
source: '/:path*',
|
|
||||||
headers,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
},
|
},
|
||||||
async rewrites() {
|
async rewrites() {
|
||||||
return [
|
return [
|
||||||
|
|
|
||||||
22
package.json
22
package.json
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "umami",
|
"name": "umami",
|
||||||
"version": "2.13.2",
|
"version": "2.15.1",
|
||||||
"description": "A simple, fast, privacy-focused alternative to Google Analytics.",
|
"description": "A simple, fast, privacy-focused alternative to Google Analytics.",
|
||||||
"author": "Umami Software, Inc. <hello@umami.is>",
|
"author": "Umami Software, Inc. <hello@umami.is>",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|
@ -63,18 +63,21 @@
|
||||||
"cacheDirectories": [
|
"cacheDirectories": [
|
||||||
".next/cache"
|
".next/cache"
|
||||||
],
|
],
|
||||||
|
"resolutions": {
|
||||||
|
"jackspeak": "2.1.1"
|
||||||
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@clickhouse/client": "^1.4.1",
|
"@clickhouse/client": "^1.4.1",
|
||||||
"@date-fns/utc": "^1.2.0",
|
"@date-fns/utc": "^1.2.0",
|
||||||
"@dicebear/collection": "^9.2.1",
|
"@dicebear/collection": "^9.2.1",
|
||||||
"@dicebear/core": "^9.2.1",
|
"@dicebear/core": "^9.2.1",
|
||||||
"@fontsource/inter": "^4.5.15",
|
"@fontsource/inter": "^4.5.15",
|
||||||
"@prisma/client": "5.17",
|
"@prisma/client": "5.22.0",
|
||||||
"@prisma/extension-read-replicas": "^0.3.0",
|
"@prisma/extension-read-replicas": "^0.3.0",
|
||||||
"@react-spring/web": "^9.7.3",
|
"@react-spring/web": "^9.7.3",
|
||||||
"@tanstack/react-query": "^5.28.6",
|
"@tanstack/react-query": "^5.28.6",
|
||||||
"@umami/prisma-client": "^0.14.0",
|
"@umami/prisma-client": "^0.14.0",
|
||||||
"@umami/redis-client": "^0.21.0",
|
"@umami/redis-client": "^0.24.0",
|
||||||
"chalk": "^4.1.1",
|
"chalk": "^4.1.1",
|
||||||
"chart.js": "^4.4.2",
|
"chart.js": "^4.4.2",
|
||||||
"chartjs-adapter-date-fns": "^3.0.0",
|
"chartjs-adapter-date-fns": "^3.0.0",
|
||||||
|
|
@ -99,16 +102,15 @@
|
||||||
"kafkajs": "^2.1.0",
|
"kafkajs": "^2.1.0",
|
||||||
"maxmind": "^4.3.6",
|
"maxmind": "^4.3.6",
|
||||||
"md5": "^2.3.0",
|
"md5": "^2.3.0",
|
||||||
"moment-timezone": "^0.5.35",
|
"next": "15.0.4",
|
||||||
"next": "14.2.10",
|
|
||||||
"next-basics": "^0.39.0",
|
"next-basics": "^0.39.0",
|
||||||
"node-fetch": "^3.2.8",
|
"node-fetch": "^3.2.8",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"prisma": "5.17",
|
"prisma": "5.22.0",
|
||||||
"react": "^18.2.0",
|
"react": "^19.0.0",
|
||||||
"react-basics": "^0.125.0",
|
"react-basics": "^0.125.0",
|
||||||
"react-beautiful-dnd": "^13.1.0",
|
"react-beautiful-dnd": "^13.1.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^19.0.0",
|
||||||
"react-error-boundary": "^4.0.4",
|
"react-error-boundary": "^4.0.4",
|
||||||
"react-intl": "^6.5.5",
|
"react-intl": "^6.5.5",
|
||||||
"react-simple-maps": "^2.3.0",
|
"react-simple-maps": "^2.3.0",
|
||||||
|
|
@ -123,7 +125,7 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@formatjs/cli": "^4.2.29",
|
"@formatjs/cli": "^4.2.29",
|
||||||
"@netlify/plugin-nextjs": "^5.1.0",
|
"@netlify/plugin-nextjs": "^5.8.1",
|
||||||
"@rollup/plugin-alias": "^5.0.0",
|
"@rollup/plugin-alias": "^5.0.0",
|
||||||
"@rollup/plugin-commonjs": "^25.0.4",
|
"@rollup/plugin-commonjs": "^25.0.4",
|
||||||
"@rollup/plugin-json": "^6.0.0",
|
"@rollup/plugin-json": "^6.0.0",
|
||||||
|
|
@ -132,7 +134,7 @@
|
||||||
"@svgr/rollup": "^8.1.0",
|
"@svgr/rollup": "^8.1.0",
|
||||||
"@svgr/webpack": "^8.1.0",
|
"@svgr/webpack": "^8.1.0",
|
||||||
"@types/cypress": "^1.1.3",
|
"@types/cypress": "^1.1.3",
|
||||||
"@types/jest": "^29.5.12",
|
"@types/jest": "^29.5.14",
|
||||||
"@types/node": "^20.9.0",
|
"@types/node": "^20.9.0",
|
||||||
"@types/react": "^18.2.41",
|
"@types/react": "^18.2.41",
|
||||||
"@types/react-dom": "^18.2.17",
|
"@types/react-dom": "^18.2.17",
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -2,7 +2,7 @@
|
||||||
"label.access-code": [
|
"label.access-code": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Access code"
|
"value": "Přístupový kód"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.actions": [
|
"label.actions": [
|
||||||
|
|
@ -14,31 +14,31 @@
|
||||||
"label.activity": [
|
"label.activity": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Activity log"
|
"value": "Log aktivity"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.add": [
|
"label.add": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Add"
|
"value": "Přidat"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.add-description": [
|
"label.add-description": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Add description"
|
"value": "Přidat popis"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.add-member": [
|
"label.add-member": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Add member"
|
"value": "Přidat člena"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.add-step": [
|
"label.add-step": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Add step"
|
"value": "Přidat krok"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.add-website": [
|
"label.add-website": [
|
||||||
|
|
@ -56,7 +56,7 @@
|
||||||
"label.after": [
|
"label.after": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "After"
|
"value": "Po"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.all": [
|
"label.all": [
|
||||||
|
|
@ -68,7 +68,7 @@
|
||||||
"label.all-time": [
|
"label.all-time": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "All time"
|
"value": "Celá doba"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.analytics": [
|
"label.analytics": [
|
||||||
|
|
@ -80,7 +80,7 @@
|
||||||
"label.average": [
|
"label.average": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Average"
|
"value": "Průměr"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.back": [
|
"label.back": [
|
||||||
|
|
@ -92,7 +92,7 @@
|
||||||
"label.before": [
|
"label.before": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Before"
|
"value": "Před"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.bounce-rate": [
|
"label.bounce-rate": [
|
||||||
|
|
@ -110,13 +110,13 @@
|
||||||
"label.browser": [
|
"label.browser": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Browser"
|
"value": "Prohlížeč"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.browsers": [
|
"label.browsers": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Prohlížeč"
|
"value": "Prohlížeče"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.cancel": [
|
"label.cancel": [
|
||||||
|
|
@ -134,31 +134,31 @@
|
||||||
"label.cities": [
|
"label.cities": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Cities"
|
"value": "Města"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.city": [
|
"label.city": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "City"
|
"value": "Město"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.clear-all": [
|
"label.clear-all": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Clear all"
|
"value": "Vyčistit vše"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.compare": [
|
"label.compare": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Compare"
|
"value": "Porovnat"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.confirm": [
|
"label.confirm": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Confirm"
|
"value": "Potvrdit"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.confirm-password": [
|
"label.confirm-password": [
|
||||||
|
|
@ -170,61 +170,61 @@
|
||||||
"label.contains": [
|
"label.contains": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Contains"
|
"value": "Obsahuje"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.continue": [
|
"label.continue": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Continue"
|
"value": "Pokračovat"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.count": [
|
"label.count": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Count"
|
"value": "Počet"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.countries": [
|
"label.countries": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Země"
|
"value": "Státy"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.country": [
|
"label.country": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Country"
|
"value": "Stát"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.create": [
|
"label.create": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Create"
|
"value": "Vytvořit"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.create-report": [
|
"label.create-report": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Create report"
|
"value": "Vytvořit hlášení"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.create-team": [
|
"label.create-team": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Create team"
|
"value": "Vytvořit tým"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.create-user": [
|
"label.create-user": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Create user"
|
"value": "Vytvořit uživatele"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.created": [
|
"label.created": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Created"
|
"value": "Vytvořeno"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.created-by": [
|
"label.created-by": [
|
||||||
|
|
@ -236,7 +236,7 @@
|
||||||
"label.current": [
|
"label.current": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Current"
|
"value": "Aktuální"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.current-password": [
|
"label.current-password": [
|
||||||
|
|
@ -266,7 +266,7 @@
|
||||||
"label.date": [
|
"label.date": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Date"
|
"value": "Datum"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.date-range": [
|
"label.date-range": [
|
||||||
|
|
@ -278,7 +278,7 @@
|
||||||
"label.day": [
|
"label.day": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Day"
|
"value": "Den"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.default-date-range": [
|
"label.default-date-range": [
|
||||||
|
|
@ -296,19 +296,19 @@
|
||||||
"label.delete-report": [
|
"label.delete-report": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Delete report"
|
"value": "Smazat hlášení"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.delete-team": [
|
"label.delete-team": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Delete team"
|
"value": "Smazat tým"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.delete-user": [
|
"label.delete-user": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Delete user"
|
"value": "Smazat uživatele"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.delete-website": [
|
"label.delete-website": [
|
||||||
|
|
@ -320,7 +320,7 @@
|
||||||
"label.description": [
|
"label.description": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Description"
|
"value": "Popis"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.desktop": [
|
"label.desktop": [
|
||||||
|
|
@ -332,13 +332,13 @@
|
||||||
"label.details": [
|
"label.details": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Details"
|
"value": "Detaily"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.device": [
|
"label.device": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Device"
|
"value": "Zařízení"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.devices": [
|
"label.devices": [
|
||||||
|
|
@ -356,7 +356,7 @@
|
||||||
"label.does-not-contain": [
|
"label.does-not-contain": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Does not contain"
|
"value": "Neobsahuje"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.domain": [
|
"label.domain": [
|
||||||
|
|
@ -380,13 +380,13 @@
|
||||||
"label.edit-dashboard": [
|
"label.edit-dashboard": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Edit dashboard"
|
"value": "Upravit dashboard"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.edit-member": [
|
"label.edit-member": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Edit member"
|
"value": "Upravit člena"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.enable-share-url": [
|
"label.enable-share-url": [
|
||||||
|
|
@ -404,13 +404,13 @@
|
||||||
"label.entry": [
|
"label.entry": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Entry URL"
|
"value": "Vstupní URL"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.event": [
|
"label.event": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Event"
|
"value": "Událost"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.event-data": [
|
"label.event-data": [
|
||||||
|
|
@ -440,7 +440,7 @@
|
||||||
"label.field": [
|
"label.field": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Field"
|
"value": "Pole"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.fields": [
|
"label.fields": [
|
||||||
|
|
@ -452,7 +452,7 @@
|
||||||
"label.filter": [
|
"label.filter": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Filter"
|
"value": "Filtr"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.filter-combined": [
|
"label.filter-combined": [
|
||||||
|
|
@ -470,7 +470,7 @@
|
||||||
"label.filters": [
|
"label.filters": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Filters"
|
"value": "Filtry"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.first-seen": [
|
"label.first-seen": [
|
||||||
|
|
@ -494,13 +494,13 @@
|
||||||
"label.goal": [
|
"label.goal": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Goal"
|
"value": "Cíl"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.goals": [
|
"label.goals": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Goals"
|
"value": "Cíle"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.goals-description": [
|
"label.goals-description": [
|
||||||
|
|
@ -596,13 +596,13 @@
|
||||||
"label.language": [
|
"label.language": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Language"
|
"value": "Jazyk"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.languages": [
|
"label.languages": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Languages"
|
"value": "Jazyky"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.laptop": [
|
"label.laptop": [
|
||||||
|
|
@ -642,7 +642,7 @@
|
||||||
"label.last-months": [
|
"label.last-months": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Last "
|
"value": "Posledních "
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": 1,
|
"type": 1,
|
||||||
|
|
@ -650,7 +650,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": " months"
|
"value": " měsíců"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.last-seen": [
|
"label.last-seen": [
|
||||||
|
|
@ -662,13 +662,13 @@
|
||||||
"label.leave": [
|
"label.leave": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Leave"
|
"value": "Opustit"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.leave-team": [
|
"label.leave-team": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Leave team"
|
"value": "Opustit tým"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.less-than": [
|
"label.less-than": [
|
||||||
|
|
@ -698,13 +698,13 @@
|
||||||
"label.manage": [
|
"label.manage": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Manage"
|
"value": "Spravovat"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.manager": [
|
"label.manager": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Manager"
|
"value": "Správce"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.max": [
|
"label.max": [
|
||||||
|
|
@ -716,13 +716,13 @@
|
||||||
"label.member": [
|
"label.member": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Member"
|
"value": "Člen"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.members": [
|
"label.members": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Members"
|
"value": "Členové"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.min": [
|
"label.min": [
|
||||||
|
|
@ -746,13 +746,13 @@
|
||||||
"label.my-account": [
|
"label.my-account": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "My account"
|
"value": "Můj účet"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.my-websites": [
|
"label.my-websites": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "My websites"
|
"value": "Mé weby"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.name": [
|
"label.name": [
|
||||||
|
|
@ -822,13 +822,13 @@
|
||||||
"label.overview": [
|
"label.overview": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Overview"
|
"value": "Přehled"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.owner": [
|
"label.owner": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Owner"
|
"value": "Vlastník"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.page-of": [
|
"label.page-of": [
|
||||||
|
|
@ -858,7 +858,7 @@
|
||||||
"label.pageTitle": [
|
"label.pageTitle": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Page title"
|
"value": "Název stránky"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.pages": [
|
"label.pages": [
|
||||||
|
|
@ -876,13 +876,13 @@
|
||||||
"label.path": [
|
"label.path": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Path"
|
"value": "Cesta"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.paths": [
|
"label.paths": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Paths"
|
"value": "Cesty"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.powered-by": [
|
"label.powered-by": [
|
||||||
|
|
@ -1444,13 +1444,13 @@
|
||||||
"label.visitors": [
|
"label.visitors": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Návštěvy"
|
"value": "Návštěvníci"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.visits": [
|
"label.visits": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Visits"
|
"value": "Návštěvy"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.website": [
|
"label.website": [
|
||||||
|
|
@ -1474,13 +1474,13 @@
|
||||||
"label.window": [
|
"label.window": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Window"
|
"value": "Okno"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.yesterday": [
|
"label.yesterday": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Yesterday"
|
"value": "Včera"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"message.action-confirmation": [
|
"message.action-confirmation": [
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -410,13 +410,13 @@
|
||||||
"label.event": [
|
"label.event": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Ereigniss"
|
"value": "Ereignis"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.event-data": [
|
"label.event-data": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Ereignissdaten"
|
"value": "Ereignisdaten"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.events": [
|
"label.events": [
|
||||||
|
|
@ -1798,7 +1798,7 @@
|
||||||
"message.triggered-event": [
|
"message.triggered-event": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Ausgelöstes Ereigniss"
|
"value": "Ereignis ausgelöst"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"message.user-deleted": [
|
"message.user-deleted": [
|
||||||
|
|
|
||||||
|
|
@ -404,7 +404,7 @@
|
||||||
"label.entry": [
|
"label.entry": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Entry URL"
|
"value": "URL d'entrée"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.event": [
|
"label.event": [
|
||||||
|
|
@ -476,7 +476,7 @@
|
||||||
"label.first-seen": [
|
"label.first-seen": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "First seen"
|
"value": "Vu pour la première fois"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.funnel": [
|
"label.funnel": [
|
||||||
|
|
@ -506,7 +506,7 @@
|
||||||
"label.goals-description": [
|
"label.goals-description": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Track your goals for pageviews and events."
|
"value": "Suivez vos objectifs en matière de pages vues et d'événements."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.greater-than": [
|
"label.greater-than": [
|
||||||
|
|
@ -590,7 +590,7 @@
|
||||||
"label.journey-description": [
|
"label.journey-description": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Understand how users navigate through your website."
|
"value": "Comprendre comment les utilisateurs naviguent sur votre site web."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.language": [
|
"label.language": [
|
||||||
|
|
@ -886,19 +886,19 @@
|
||||||
"label.previous": [
|
"label.previous": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Previous"
|
"value": "Précédent"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.previous-period": [
|
"label.previous-period": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Previous period"
|
"value": "Période précédente"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.previous-year": [
|
"label.previous-year": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Previous year"
|
"value": "Année précédente"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.profile": [
|
"label.profile": [
|
||||||
|
|
@ -910,13 +910,13 @@
|
||||||
"label.properties": [
|
"label.properties": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Properties"
|
"value": "Propriétés"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.property": [
|
"label.property": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Property"
|
"value": "Propriété"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.queries": [
|
"label.queries": [
|
||||||
|
|
@ -1036,13 +1036,13 @@
|
||||||
"label.revenue-description": [
|
"label.revenue-description": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Look into your revenue across time."
|
"value": "Examinez vos revenus au fil du temps."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.revenue-property": [
|
"label.revenue-property": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Revenue Property"
|
"value": "Propriétés des revenues"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.role": [
|
"label.role": [
|
||||||
|
|
@ -1078,7 +1078,7 @@
|
||||||
"label.select": [
|
"label.select": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Select"
|
"value": "Selectionner"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.select-date": [
|
"label.select-date": [
|
||||||
|
|
@ -1132,7 +1132,7 @@
|
||||||
"label.start-step": [
|
"label.start-step": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Start Step"
|
"value": "Etape de démarrage"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.steps": [
|
"label.steps": [
|
||||||
|
|
@ -1168,7 +1168,7 @@
|
||||||
"label.team-manager": [
|
"label.team-manager": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Team manager"
|
"value": "Manager de l'équipe"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.team-member": [
|
"label.team-member": [
|
||||||
|
|
@ -1192,7 +1192,7 @@
|
||||||
"label.team-view-only": [
|
"label.team-view-only": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Team view only"
|
"value": "Vue d'équipe uniquement"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.team-websites": [
|
"label.team-websites": [
|
||||||
|
|
@ -1318,7 +1318,7 @@
|
||||||
"label.uniqueCustomers": [
|
"label.uniqueCustomers": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Unique Customers"
|
"value": "Clients uniques"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.unknown": [
|
"label.unknown": [
|
||||||
|
|
@ -1360,7 +1360,7 @@
|
||||||
"label.user-property": [
|
"label.user-property": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "User Property"
|
"value": "Propriétés d'utilisateurs"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.username": [
|
"label.username": [
|
||||||
|
|
|
||||||
|
|
@ -1450,7 +1450,7 @@
|
||||||
"label.visits": [
|
"label.visits": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "訪問者数"
|
"value": "訪問数"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.website": [
|
"label.website": [
|
||||||
|
|
|
||||||
|
|
@ -1012,7 +1012,7 @@
|
||||||
"label.retention-description": [
|
"label.retention-description": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "사용자가 얼마나 자주 돌아오는지를 추적하여 웹사이트의 리텐션을 측정하십시오."
|
"value": "사용자가 얼마나 자주 돌아오는지를 추적하여 웹사이트의 리텐션을 측정하세요."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.revenue": [
|
"label.revenue": [
|
||||||
|
|
@ -1372,7 +1372,7 @@
|
||||||
"label.utm-description": [
|
"label.utm-description": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "UTM 매개변수를 통해 캠페인을 추적합니다."
|
"value": "UTM 매개변수를 통해 캠페인을 추적하세요."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.value": [
|
"label.value": [
|
||||||
|
|
@ -1414,7 +1414,7 @@
|
||||||
"label.visit-duration": [
|
"label.visit-duration": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "평균 방문 시간"
|
"value": "방문 시간"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.visitors": [
|
"label.visitors": [
|
||||||
|
|
@ -1548,7 +1548,7 @@
|
||||||
"message.error": [
|
"message.error": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "오류가 발생했습니다."
|
"value": "문제가 발생했습니다."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"message.event-log": [
|
"message.event-log": [
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -38,13 +38,13 @@
|
||||||
"label.add-step": [
|
"label.add-step": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Add step"
|
"value": "Adaugă pas"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.add-website": [
|
"label.add-website": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Adăugare site web"
|
"value": "Adaugă site web"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.admin": [
|
"label.admin": [
|
||||||
|
|
@ -152,7 +152,7 @@
|
||||||
"label.compare": [
|
"label.compare": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Compare"
|
"value": "Compară"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.confirm": [
|
"label.confirm": [
|
||||||
|
|
@ -182,7 +182,7 @@
|
||||||
"label.count": [
|
"label.count": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Count"
|
"value": "Număr"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.countries": [
|
"label.countries": [
|
||||||
|
|
@ -230,13 +230,13 @@
|
||||||
"label.created-by": [
|
"label.created-by": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Created By"
|
"value": "Creat de"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.current": [
|
"label.current": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Current"
|
"value": "Curent"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.current-password": [
|
"label.current-password": [
|
||||||
|
|
@ -266,13 +266,13 @@
|
||||||
"label.date": [
|
"label.date": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Data"
|
"value": "Dată"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.date-range": [
|
"label.date-range": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Interval de date"
|
"value": "Interval"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.day": [
|
"label.day": [
|
||||||
|
|
@ -284,7 +284,7 @@
|
||||||
"label.default-date-range": [
|
"label.default-date-range": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Interval de date implicit"
|
"value": "Interval implicit"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.delete": [
|
"label.delete": [
|
||||||
|
|
@ -314,7 +314,7 @@
|
||||||
"label.delete-website": [
|
"label.delete-website": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Ștergere site web"
|
"value": "Șterge site web"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.description": [
|
"label.description": [
|
||||||
|
|
@ -398,13 +398,13 @@
|
||||||
"label.end-step": [
|
"label.end-step": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "End Step"
|
"value": "Pas final"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.entry": [
|
"label.entry": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Entry URL"
|
"value": "URL de intrare"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.event": [
|
"label.event": [
|
||||||
|
|
@ -428,7 +428,7 @@
|
||||||
"label.exit": [
|
"label.exit": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Exit URL"
|
"value": "URL de ieșire"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.false": [
|
"label.false": [
|
||||||
|
|
@ -476,7 +476,7 @@
|
||||||
"label.first-seen": [
|
"label.first-seen": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "First seen"
|
"value": "Văzut pentru prima dată"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.funnel": [
|
"label.funnel": [
|
||||||
|
|
@ -494,19 +494,19 @@
|
||||||
"label.goal": [
|
"label.goal": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Goal"
|
"value": "Obiectiv"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.goals": [
|
"label.goals": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Goals"
|
"value": "Obiective"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.goals-description": [
|
"label.goals-description": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Track your goals for pageviews and events."
|
"value": "Urmărește obiectivele de vizualizări și evenimente."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.greater-than": [
|
"label.greater-than": [
|
||||||
|
|
@ -584,13 +584,13 @@
|
||||||
"label.journey": [
|
"label.journey": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Journey"
|
"value": "Traseu"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.journey-description": [
|
"label.journey-description": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Understand how users navigate through your website."
|
"value": "Înțelege cum navighează vizitatorii prin website."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.language": [
|
"label.language": [
|
||||||
|
|
@ -642,7 +642,7 @@
|
||||||
"label.last-months": [
|
"label.last-months": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Last "
|
"value": "Ultimele "
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": 1,
|
"type": 1,
|
||||||
|
|
@ -650,13 +650,13 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": " months"
|
"value": " luni"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.last-seen": [
|
"label.last-seen": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Last seen"
|
"value": "Văzut ultima dată"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.leave": [
|
"label.leave": [
|
||||||
|
|
@ -876,13 +876,13 @@
|
||||||
"label.path": [
|
"label.path": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Path"
|
"value": "Rută"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.paths": [
|
"label.paths": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Paths"
|
"value": "Rute"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.powered-by": [
|
"label.powered-by": [
|
||||||
|
|
@ -898,19 +898,19 @@
|
||||||
"label.previous": [
|
"label.previous": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Previous"
|
"value": "Anterior"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.previous-period": [
|
"label.previous-period": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Previous period"
|
"value": "Perioda anterioară"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.previous-year": [
|
"label.previous-year": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Previous year"
|
"value": "Anul anterior"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.profile": [
|
"label.profile": [
|
||||||
|
|
@ -922,13 +922,13 @@
|
||||||
"label.properties": [
|
"label.properties": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Properties"
|
"value": "Proprietăți"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.property": [
|
"label.property": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Property"
|
"value": "Proprietate"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.queries": [
|
"label.queries": [
|
||||||
|
|
@ -1042,13 +1042,13 @@
|
||||||
"label.revenue": [
|
"label.revenue": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Revenue"
|
"value": "Venit"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.revenue-description": [
|
"label.revenue-description": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Look into your revenue across time."
|
"value": "Urmărește venitul în timp."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.revenue-property": [
|
"label.revenue-property": [
|
||||||
|
|
@ -1114,7 +1114,7 @@
|
||||||
"label.session": [
|
"label.session": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Session"
|
"value": "Sesiune"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.sessions": [
|
"label.sessions": [
|
||||||
|
|
@ -1144,13 +1144,13 @@
|
||||||
"label.start-step": [
|
"label.start-step": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Start Step"
|
"value": "Pas de început"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.steps": [
|
"label.steps": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Steps"
|
"value": "Pași"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.sum": [
|
"label.sum": [
|
||||||
|
|
@ -1174,13 +1174,13 @@
|
||||||
"label.team-id": [
|
"label.team-id": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "ID Echipa"
|
"value": "ID Echipă"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.team-manager": [
|
"label.team-manager": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Team manager"
|
"value": "Manager echipă"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.team-member": [
|
"label.team-member": [
|
||||||
|
|
@ -1288,7 +1288,7 @@
|
||||||
"label.transactions": [
|
"label.transactions": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Transactions"
|
"value": "Tranzacții"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.transfer": [
|
"label.transfer": [
|
||||||
|
|
@ -1330,7 +1330,7 @@
|
||||||
"label.uniqueCustomers": [
|
"label.uniqueCustomers": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Unique Customers"
|
"value": "Clienți unici"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.unknown": [
|
"label.unknown": [
|
||||||
|
|
@ -1372,7 +1372,7 @@
|
||||||
"label.user-property": [
|
"label.user-property": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "User Property"
|
"value": "Proprietatea utilizatorului"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.username": [
|
"label.username": [
|
||||||
|
|
@ -1396,7 +1396,7 @@
|
||||||
"label.utm-description": [
|
"label.utm-description": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Track your campaigns through UTM parameters."
|
"value": "Urmărește campaniile tale cu parametri UTM."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.value": [
|
"label.value": [
|
||||||
|
|
@ -1432,7 +1432,7 @@
|
||||||
"label.views-per-visit": [
|
"label.views-per-visit": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Views per visit"
|
"value": "Vizualizări per vizită"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.visit-duration": [
|
"label.visit-duration": [
|
||||||
|
|
@ -1450,7 +1450,7 @@
|
||||||
"label.visits": [
|
"label.visits": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Visits"
|
"value": "Vizite"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.website": [
|
"label.website": [
|
||||||
|
|
@ -1534,7 +1534,7 @@
|
||||||
"message.collected-data": [
|
"message.collected-data": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Collected data"
|
"value": "Date colectate"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"message.confirm-delete": [
|
"message.confirm-delete": [
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -476,7 +476,7 @@
|
||||||
"label.first-seen": [
|
"label.first-seen": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "First seen"
|
"value": "首次出现"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.funnel": [
|
"label.funnel": [
|
||||||
|
|
@ -656,7 +656,7 @@
|
||||||
"label.last-seen": [
|
"label.last-seen": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Last seen"
|
"value": "最后出现"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.leave": [
|
"label.leave": [
|
||||||
|
|
@ -1050,19 +1050,19 @@
|
||||||
"label.revenue": [
|
"label.revenue": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Revenue"
|
"value": "收入"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.revenue-description": [
|
"label.revenue-description": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Look into your revenue across time."
|
"value": "查看您的收入随时间的变化。"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.revenue-property": [
|
"label.revenue-property": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Revenue Property"
|
"value": "收入值"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.role": [
|
"label.role": [
|
||||||
|
|
@ -1296,7 +1296,7 @@
|
||||||
"label.transactions": [
|
"label.transactions": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Transactions"
|
"value": "交易"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.transfer": [
|
"label.transfer": [
|
||||||
|
|
@ -1338,7 +1338,7 @@
|
||||||
"label.uniqueCustomers": [
|
"label.uniqueCustomers": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Unique Customers"
|
"value": "独特客户"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.unknown": [
|
"label.unknown": [
|
||||||
|
|
@ -1380,7 +1380,7 @@
|
||||||
"label.user-property": [
|
"label.user-property": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "User Property"
|
"value": "用户属性"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.username": [
|
"label.username": [
|
||||||
|
|
|
||||||
|
|
@ -8,13 +8,13 @@
|
||||||
"label.actions": [
|
"label.actions": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "行動"
|
"value": "行為"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.activity": [
|
"label.activity": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "活動日誌"
|
"value": "活動紀錄"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.add": [
|
"label.add": [
|
||||||
|
|
@ -32,13 +32,13 @@
|
||||||
"label.add-member": [
|
"label.add-member": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Add member"
|
"value": "新增成員"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.add-step": [
|
"label.add-step": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Add step"
|
"value": "新增步驟"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.add-website": [
|
"label.add-website": [
|
||||||
|
|
@ -104,7 +104,7 @@
|
||||||
"label.breakdown": [
|
"label.breakdown": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "分解"
|
"value": "細項分析"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.browser": [
|
"label.browser": [
|
||||||
|
|
@ -152,7 +152,7 @@
|
||||||
"label.compare": [
|
"label.compare": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Compare"
|
"value": "比較"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.confirm": [
|
"label.confirm": [
|
||||||
|
|
@ -182,7 +182,7 @@
|
||||||
"label.count": [
|
"label.count": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Count"
|
"value": "數量"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.countries": [
|
"label.countries": [
|
||||||
|
|
@ -206,7 +206,7 @@
|
||||||
"label.create-report": [
|
"label.create-report": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "建立報告"
|
"value": "建立報表"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.create-team": [
|
"label.create-team": [
|
||||||
|
|
@ -230,13 +230,13 @@
|
||||||
"label.created-by": [
|
"label.created-by": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Created By"
|
"value": "建立者"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.current": [
|
"label.current": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Current"
|
"value": "目前"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.current-password": [
|
"label.current-password": [
|
||||||
|
|
@ -296,7 +296,7 @@
|
||||||
"label.delete-report": [
|
"label.delete-report": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Delete report"
|
"value": "刪除報表"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.delete-team": [
|
"label.delete-team": [
|
||||||
|
|
@ -368,7 +368,7 @@
|
||||||
"label.dropoff": [
|
"label.dropoff": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "退出"
|
"value": "離開"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.edit": [
|
"label.edit": [
|
||||||
|
|
@ -386,25 +386,25 @@
|
||||||
"label.edit-member": [
|
"label.edit-member": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Edit member"
|
"value": "編輯成員"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.enable-share-url": [
|
"label.enable-share-url": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "啟用分享網址"
|
"value": "啟用分享連結"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.end-step": [
|
"label.end-step": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "End Step"
|
"value": "結束步驟"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.entry": [
|
"label.entry": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Entry URL"
|
"value": "進入網址"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.event": [
|
"label.event": [
|
||||||
|
|
@ -428,7 +428,7 @@
|
||||||
"label.exit": [
|
"label.exit": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Exit URL"
|
"value": "離開網址"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.false": [
|
"label.false": [
|
||||||
|
|
@ -470,43 +470,43 @@
|
||||||
"label.filters": [
|
"label.filters": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "篩選器"
|
"value": "篩選條件"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.first-seen": [
|
"label.first-seen": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "First seen"
|
"value": "首次造訪"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.funnel": [
|
"label.funnel": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "漏斗"
|
"value": "漏斗分析"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.funnel-description": [
|
"label.funnel-description": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "瞭解使用者的轉換率和退出率"
|
"value": "瞭解使用者的轉換率與流失率。"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.goal": [
|
"label.goal": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Goal"
|
"value": "目標"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.goals": [
|
"label.goals": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Goals"
|
"value": "目標"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.goals-description": [
|
"label.goals-description": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Track your goals for pageviews and events."
|
"value": "追蹤網頁瀏覽和事件的目標。"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.greater-than": [
|
"label.greater-than": [
|
||||||
|
|
@ -524,13 +524,13 @@
|
||||||
"label.host": [
|
"label.host": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Host"
|
"value": "主機名稱"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.hosts": [
|
"label.hosts": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Hosts"
|
"value": "主機名稱"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.insights": [
|
"label.insights": [
|
||||||
|
|
@ -542,7 +542,7 @@
|
||||||
"label.insights-description": [
|
"label.insights-description": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "透過使用區段和篩選器來深入探索你的數據"
|
"value": "使用區段和篩選器來深入分析您的資料。"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.is": [
|
"label.is": [
|
||||||
|
|
@ -584,13 +584,13 @@
|
||||||
"label.journey": [
|
"label.journey": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Journey"
|
"value": "使用者旅程"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.journey-description": [
|
"label.journey-description": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Understand how users navigate through your website."
|
"value": "瞭解使用者如何瀏覽您的網站。"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.language": [
|
"label.language": [
|
||||||
|
|
@ -642,7 +642,7 @@
|
||||||
"label.last-months": [
|
"label.last-months": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Last "
|
"value": "最近 "
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": 1,
|
"type": 1,
|
||||||
|
|
@ -650,13 +650,13 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": " months"
|
"value": " 個月"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.last-seen": [
|
"label.last-seen": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Last seen"
|
"value": "最後造訪"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.leave": [
|
"label.leave": [
|
||||||
|
|
@ -698,25 +698,25 @@
|
||||||
"label.manage": [
|
"label.manage": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Manage"
|
"value": "管理"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.manager": [
|
"label.manager": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Manager"
|
"value": "管理者"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.max": [
|
"label.max": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "最大"
|
"value": "最大值"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.member": [
|
"label.member": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Member"
|
"value": "成員"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.members": [
|
"label.members": [
|
||||||
|
|
@ -728,7 +728,7 @@
|
||||||
"label.min": [
|
"label.min": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "最小"
|
"value": "最小值"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.mobile": [
|
"label.mobile": [
|
||||||
|
|
@ -746,7 +746,7 @@
|
||||||
"label.my-account": [
|
"label.my-account": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "My account"
|
"value": "我的帳號"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.my-websites": [
|
"label.my-websites": [
|
||||||
|
|
@ -780,31 +780,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": " "
|
"value": " 筆紀錄"
|
||||||
},
|
|
||||||
{
|
|
||||||
"offset": 0,
|
|
||||||
"options": {
|
|
||||||
"one": {
|
|
||||||
"value": [
|
|
||||||
{
|
|
||||||
"type": 0,
|
|
||||||
"value": "record"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"other": {
|
|
||||||
"value": [
|
|
||||||
{
|
|
||||||
"type": 0,
|
|
||||||
"value": "records"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"pluralType": "cardinal",
|
|
||||||
"type": 6,
|
|
||||||
"value": "x"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.ok": [
|
"label.ok": [
|
||||||
|
|
@ -822,7 +798,7 @@
|
||||||
"label.overview": [
|
"label.overview": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "概覽"
|
"value": "總覽"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.owner": [
|
"label.owner": [
|
||||||
|
|
@ -834,7 +810,7 @@
|
||||||
"label.page-of": [
|
"label.page-of": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "頁面 "
|
"value": "第 "
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": 1,
|
"type": 1,
|
||||||
|
|
@ -842,29 +818,33 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": " / "
|
"value": " 頁,共 "
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": 1,
|
"type": 1,
|
||||||
"value": "total"
|
"value": "total"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": " 頁"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.page-views": [
|
"label.page-views": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "頁面瀏覽"
|
"value": "網頁瀏覽次數"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.pageTitle": [
|
"label.pageTitle": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "頁面標題"
|
"value": "網頁標題"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.pages": [
|
"label.pages": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "頁面"
|
"value": "網頁"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.password": [
|
"label.password": [
|
||||||
|
|
@ -876,13 +856,13 @@
|
||||||
"label.path": [
|
"label.path": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Path"
|
"value": "路徑"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.paths": [
|
"label.paths": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Paths"
|
"value": "路徑"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.powered-by": [
|
"label.powered-by": [
|
||||||
|
|
@ -896,43 +876,43 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": " 提供"
|
"value": " 提供技術支援"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.previous": [
|
"label.previous": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Previous"
|
"value": "上一個"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.previous-period": [
|
"label.previous-period": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Previous period"
|
"value": "上一期間"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.previous-year": [
|
"label.previous-year": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Previous year"
|
"value": "去年"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.profile": [
|
"label.profile": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "個人資料"
|
"value": "個人檔案"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.properties": [
|
"label.properties": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Properties"
|
"value": "屬性"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.property": [
|
"label.property": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Property"
|
"value": "屬性"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.queries": [
|
"label.queries": [
|
||||||
|
|
@ -986,13 +966,13 @@
|
||||||
"label.region": [
|
"label.region": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "區域"
|
"value": "地區"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.regions": [
|
"label.regions": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "區域"
|
"value": "地區"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.remove": [
|
"label.remove": [
|
||||||
|
|
@ -1004,13 +984,13 @@
|
||||||
"label.remove-member": [
|
"label.remove-member": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Remove member"
|
"value": "移除成員"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.reports": [
|
"label.reports": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "報告"
|
"value": "報表"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.required": [
|
"label.required": [
|
||||||
|
|
@ -1028,13 +1008,13 @@
|
||||||
"label.reset-website": [
|
"label.reset-website": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "重設網站"
|
"value": "重設網站統計資料"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.retention": [
|
"label.retention": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "保留"
|
"value": "留存率"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.retention-description": [
|
"label.retention-description": [
|
||||||
|
|
@ -1046,19 +1026,19 @@
|
||||||
"label.revenue": [
|
"label.revenue": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Revenue"
|
"value": "營收"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.revenue-description": [
|
"label.revenue-description": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Look into your revenue across time."
|
"value": "查看您的營收趨勢。"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.revenue-property": [
|
"label.revenue-property": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Revenue Property"
|
"value": "營收屬性"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.role": [
|
"label.role": [
|
||||||
|
|
@ -1088,37 +1068,37 @@
|
||||||
"label.search": [
|
"label.search": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Search"
|
"value": "搜尋"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.select": [
|
"label.select": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Select"
|
"value": "選取"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.select-date": [
|
"label.select-date": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "選擇日期"
|
"value": "選取日期"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.select-role": [
|
"label.select-role": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Select role"
|
"value": "選取角色"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.select-website": [
|
"label.select-website": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "選擇網站"
|
"value": "選取網站"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.session": [
|
"label.session": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Session"
|
"value": "工作階段"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.sessions": [
|
"label.sessions": [
|
||||||
|
|
@ -1136,7 +1116,7 @@
|
||||||
"label.share-url": [
|
"label.share-url": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "分享網址"
|
"value": "分享連結"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.single-day": [
|
"label.single-day": [
|
||||||
|
|
@ -1148,13 +1128,13 @@
|
||||||
"label.start-step": [
|
"label.start-step": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Start Step"
|
"value": "起始步驟"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.steps": [
|
"label.steps": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Steps"
|
"value": "步驟"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.sum": [
|
"label.sum": [
|
||||||
|
|
@ -1184,7 +1164,7 @@
|
||||||
"label.team-manager": [
|
"label.team-manager": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Team manager"
|
"value": "團隊管理者"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.team-member": [
|
"label.team-member": [
|
||||||
|
|
@ -1208,7 +1188,7 @@
|
||||||
"label.team-view-only": [
|
"label.team-view-only": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Team view only"
|
"value": "團隊僅供檢視"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.team-websites": [
|
"label.team-websites": [
|
||||||
|
|
@ -1280,7 +1260,7 @@
|
||||||
"label.total-records": [
|
"label.total-records": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "總記錄"
|
"value": "紀錄總數"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.tracking-code": [
|
"label.tracking-code": [
|
||||||
|
|
@ -1292,19 +1272,19 @@
|
||||||
"label.transactions": [
|
"label.transactions": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Transactions"
|
"value": "交易"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.transfer": [
|
"label.transfer": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Transfer"
|
"value": "轉移"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.transfer-website": [
|
"label.transfer-website": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Transfer website"
|
"value": "轉移網站"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.true": [
|
"label.true": [
|
||||||
|
|
@ -1322,19 +1302,19 @@
|
||||||
"label.unique": [
|
"label.unique": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "獨立"
|
"value": "不重複"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.unique-visitors": [
|
"label.unique-visitors": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "獨立訪客"
|
"value": "不重複訪客"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.uniqueCustomers": [
|
"label.uniqueCustomers": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Unique Customers"
|
"value": "不重複客戶"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.unknown": [
|
"label.unknown": [
|
||||||
|
|
@ -1346,13 +1326,13 @@
|
||||||
"label.untitled": [
|
"label.untitled": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "無標題"
|
"value": "未命名"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.update": [
|
"label.update": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Update"
|
"value": "更新"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.url": [
|
"label.url": [
|
||||||
|
|
@ -1376,7 +1356,7 @@
|
||||||
"label.user-property": [
|
"label.user-property": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "User Property"
|
"value": "使用者屬性"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.username": [
|
"label.username": [
|
||||||
|
|
@ -1400,7 +1380,7 @@
|
||||||
"label.utm-description": [
|
"label.utm-description": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Track your campaigns through UTM parameters."
|
"value": "透過 UTM 參數追蹤您的行銷活動。"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.value": [
|
"label.value": [
|
||||||
|
|
@ -1430,19 +1410,19 @@
|
||||||
"label.views": [
|
"label.views": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "檢視"
|
"value": "瀏覽次數"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.views-per-visit": [
|
"label.views-per-visit": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Views per visit"
|
"value": "每次造訪的瀏覽次數"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.visit-duration": [
|
"label.visit-duration": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "平均造訪時間"
|
"value": "造訪時間"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.visitors": [
|
"label.visitors": [
|
||||||
|
|
@ -1454,7 +1434,7 @@
|
||||||
"label.visits": [
|
"label.visits": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Visits"
|
"value": "造訪次數"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.website": [
|
"label.website": [
|
||||||
|
|
@ -1490,7 +1470,7 @@
|
||||||
"message.action-confirmation": [
|
"message.action-confirmation": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Type "
|
"value": "請在下方欄位輸入 "
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": 1,
|
"type": 1,
|
||||||
|
|
@ -1498,7 +1478,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": " in the box below to confirm."
|
"value": " 以確認。"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"message.active-users": [
|
"message.active-users": [
|
||||||
|
|
@ -1512,13 +1492,13 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": " 個活躍的訪客"
|
"value": " 位訪客"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"message.collected-data": [
|
"message.collected-data": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Collected data"
|
"value": "已蒐集的資料"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"message.confirm-delete": [
|
"message.confirm-delete": [
|
||||||
|
|
@ -1552,7 +1532,7 @@
|
||||||
"message.confirm-remove": [
|
"message.confirm-remove": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Are you sure you want to remove "
|
"value": "您確定要移除 "
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": 1,
|
"type": 1,
|
||||||
|
|
@ -1560,7 +1540,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "?"
|
"value": " 嗎?"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"message.confirm-reset": [
|
"message.confirm-reset": [
|
||||||
|
|
@ -1574,19 +1554,19 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": " 嗎?"
|
"value": " 的統計資料嗎?"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"message.delete-team-warning": [
|
"message.delete-team-warning": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Deleting a team will also delete all team websites."
|
"value": "刪除團隊的同時也會刪除所有團隊的網站。"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"message.delete-website-warning": [
|
"message.delete-website-warning": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "所有網站資料將被刪除。"
|
"value": "所有網站資料都將被刪除。"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"message.error": [
|
"message.error": [
|
||||||
|
|
@ -1596,17 +1576,21 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"message.event-log": [
|
"message.event-log": [
|
||||||
{
|
|
||||||
"type": 1,
|
|
||||||
"value": "event"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": " 在 "
|
"value": "在 "
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": 1,
|
"type": 1,
|
||||||
"value": "url"
|
"value": "url"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": " 上的 "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "event"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"message.go-to-settings": [
|
"message.go-to-settings": [
|
||||||
|
|
@ -1618,19 +1602,19 @@
|
||||||
"message.incorrect-username-password": [
|
"message.incorrect-username-password": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "使用者名稱和/或密碼不正確。"
|
"value": "使用者名稱或密碼不正確。"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"message.invalid-domain": [
|
"message.invalid-domain": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "無效的網域。請不要包含 http/https。"
|
"value": "無效的網域。請勿包含 http/https。"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"message.min-password-length": [
|
"message.min-password-length": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "最少需要 "
|
"value": "密碼長度至少需 "
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": 1,
|
"type": 1,
|
||||||
|
|
@ -1652,7 +1636,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": " 的新版本已經可以使用!"
|
"value": " 的新版本已推出!"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"message.no-data-available": [
|
"message.no-data-available": [
|
||||||
|
|
@ -1694,7 +1678,7 @@
|
||||||
"message.no-users": [
|
"message.no-users": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "沒有使用者。"
|
"value": "沒有任何使用者。"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"message.no-websites-configured": [
|
"message.no-websites-configured": [
|
||||||
|
|
@ -1706,13 +1690,13 @@
|
||||||
"message.page-not-found": [
|
"message.page-not-found": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "找不到頁面"
|
"value": "找不到網頁"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"message.reset-website": [
|
"message.reset-website": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "要重設此網站,請在下方的方框中輸入 "
|
"value": "要重設此網站的統計資料,請在下方欄位輸入 "
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": 1,
|
"type": 1,
|
||||||
|
|
@ -1726,7 +1710,7 @@
|
||||||
"message.reset-website-warning": [
|
"message.reset-website-warning": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "此網站的所有統計將被刪除,但您的設定將保持不變。"
|
"value": "此網站的所有統計資料都將被刪除,但您的設定將保持不變。"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"message.saved": [
|
"message.saved": [
|
||||||
|
|
@ -1738,13 +1722,13 @@
|
||||||
"message.share-url": [
|
"message.share-url": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "您的網站統計資料可以在以下網址公開檢視:"
|
"value": "您的網站統計資料可在以下網址公開檢視:"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"message.team-already-member": [
|
"message.team-already-member": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "您已經是團隊的成員。"
|
"value": "您已是該團隊的成員。"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"message.team-not-found": [
|
"message.team-not-found": [
|
||||||
|
|
@ -1756,13 +1740,13 @@
|
||||||
"message.team-websites-info": [
|
"message.team-websites-info": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "團隊的任何成員都可以檢視網站。"
|
"value": "團隊中的所有成員都可以檢視網站。"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"message.tracking-code": [
|
"message.tracking-code": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "要追蹤此網站的統計,請將以下代碼放在您的 HTML 的 "
|
"value": "要追蹤此網站的統計資料,請將以下程式碼放在您 HTML 的 "
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"children": [
|
"children": [
|
||||||
|
|
@ -1782,25 +1766,25 @@
|
||||||
"message.transfer-team-website-to-user": [
|
"message.transfer-team-website-to-user": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Transfer this website to your account?"
|
"value": "要將此網站轉移至您的帳號嗎?"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"message.transfer-user-website-to-team": [
|
"message.transfer-user-website-to-team": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Select the team to transfer this website to."
|
"value": "請選擇要轉移此網站的團隊。"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"message.transfer-website": [
|
"message.transfer-website": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Transfer website ownership to your account or another team."
|
"value": "將網站所有權轉移至您的帳號或其他團隊。"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"message.triggered-event": [
|
"message.triggered-event": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Triggered event"
|
"value": "已觸發的事件"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"message.user-deleted": [
|
"message.user-deleted": [
|
||||||
|
|
@ -1812,7 +1796,7 @@
|
||||||
"message.viewed-page": [
|
"message.viewed-page": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Viewed page"
|
"value": "已瀏覽的網頁"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"message.visitor-log": [
|
"message.visitor-log": [
|
||||||
|
|
@ -1856,7 +1840,7 @@
|
||||||
"message.visitors-dropped-off": [
|
"message.visitors-dropped-off": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Visitors dropped off"
|
"value": "訪客已離開"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,9 @@ async function sendTelemetry(type) {
|
||||||
node: process.version,
|
node: process.version,
|
||||||
platform: os.platform(),
|
platform: os.platform(),
|
||||||
arch: os.arch(),
|
arch: os.arch(),
|
||||||
os: `${os.type()} (${os.version()})`,
|
os: `${os.type()} ${os.version()}`,
|
||||||
isDocker: isDocker(),
|
is_docker: isDocker(),
|
||||||
isCi: isCI,
|
is_ci: isCI,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,8 @@ import TeamsButton from 'components/input/TeamsButton';
|
||||||
import Icons from 'components/icons';
|
import Icons from 'components/icons';
|
||||||
import { useMessages, useNavigation, useTeamUrl } from 'components/hooks';
|
import { useMessages, useNavigation, useTeamUrl } from 'components/hooks';
|
||||||
import styles from './NavBar.module.css';
|
import styles from './NavBar.module.css';
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
import { getItem, setItem } from 'next-basics';
|
||||||
|
|
||||||
export function NavBar() {
|
export function NavBar() {
|
||||||
const { formatMessage, labels } = useMessages();
|
const { formatMessage, labels } = useMessages();
|
||||||
|
|
@ -74,10 +76,24 @@ export function NavBar() {
|
||||||
|
|
||||||
const handleTeamChange = (teamId: string) => {
|
const handleTeamChange = (teamId: string) => {
|
||||||
const url = teamId ? `/teams/${teamId}` : '/';
|
const url = teamId ? `/teams/${teamId}` : '/';
|
||||||
|
if (!cloudMode) {
|
||||||
|
setItem('umami.team', { id: teamId });
|
||||||
|
}
|
||||||
router.push(cloudMode ? `${process.env.cloudUrl}${url}` : url);
|
router.push(cloudMode ? `${process.env.cloudUrl}${url}` : url);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!cloudMode) {
|
||||||
|
const teamIdLocal = getItem('umami.team')?.id;
|
||||||
|
|
||||||
|
if (teamIdLocal && teamIdLocal !== teamId) {
|
||||||
|
router.push(
|
||||||
|
pathname !== '/' && pathname !== '/dashboard' ? '/' : `/teams/${teamIdLocal}/dashboard`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [cloudMode]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.navbar}>
|
<div className={styles.navbar}>
|
||||||
<div className={styles.logo}>
|
<div className={styles.logo}>
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,46 @@ export function TestConsole({ websiteId }: { websiteId: string }) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleRunRevenue() {
|
||||||
|
window['umami'].track(props => ({
|
||||||
|
...props,
|
||||||
|
url: '/checkout-cart',
|
||||||
|
referrer: 'https://www.google.com',
|
||||||
|
}));
|
||||||
|
window['umami'].track('checkout-cart', {
|
||||||
|
revenue: parseFloat((Math.random() * 1000).toFixed(2)),
|
||||||
|
currency: 'USD',
|
||||||
|
});
|
||||||
|
window['umami'].track('affiliate-link', {
|
||||||
|
revenue: parseFloat((Math.random() * 1000).toFixed(2)),
|
||||||
|
currency: 'USD',
|
||||||
|
});
|
||||||
|
window['umami'].track('promotion-link', {
|
||||||
|
revenue: parseFloat((Math.random() * 1000).toFixed(2)),
|
||||||
|
currency: 'USD',
|
||||||
|
});
|
||||||
|
window['umami'].track('checkout-cart', {
|
||||||
|
revenue: parseFloat((Math.random() * 1000).toFixed(2)),
|
||||||
|
currency: 'EUR',
|
||||||
|
});
|
||||||
|
window['umami'].track('promotion-link', {
|
||||||
|
revenue: parseFloat((Math.random() * 1000).toFixed(2)),
|
||||||
|
currency: 'EUR',
|
||||||
|
});
|
||||||
|
window['umami'].track('affiliate-link', {
|
||||||
|
item1: {
|
||||||
|
productIdentity: 'ABC424',
|
||||||
|
revenue: parseFloat((Math.random() * 10000).toFixed(2)),
|
||||||
|
currency: 'JPY',
|
||||||
|
},
|
||||||
|
item2: {
|
||||||
|
productIdentity: 'ZYW684',
|
||||||
|
revenue: parseFloat((Math.random() * 10000).toFixed(2)),
|
||||||
|
currency: 'JPY',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function handleRunIdentify() {
|
function handleRunIdentify() {
|
||||||
window['umami'].identify({
|
window['umami'].identify({
|
||||||
userId: 123,
|
userId: 123,
|
||||||
|
|
@ -127,10 +167,19 @@ export function TestConsole({ websiteId }: { websiteId: string }) {
|
||||||
>
|
>
|
||||||
Send event with data
|
Send event with data
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button
|
||||||
|
id="generate-revenue-button"
|
||||||
|
data-umami-event="checkout-cart"
|
||||||
|
data-umami-event-revenue={(Math.random() * 10000).toFixed(2).toString()}
|
||||||
|
data-umami-event-currency="USD"
|
||||||
|
variant="primary"
|
||||||
|
>
|
||||||
|
Generate revenue data
|
||||||
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
id="button-with-div-button"
|
id="button-with-div-button"
|
||||||
data-umami-event="button-click"
|
data-umami-event="button-click"
|
||||||
data-umami-event-name="bob"
|
data-umami-event-name={'bob'}
|
||||||
data-umami-event-id="123"
|
data-umami-event-id="123"
|
||||||
variant="primary"
|
variant="primary"
|
||||||
>
|
>
|
||||||
|
|
@ -155,6 +204,9 @@ export function TestConsole({ websiteId }: { websiteId: string }) {
|
||||||
<Button id="manual-button" variant="primary" onClick={handleRunIdentify}>
|
<Button id="manual-button" variant="primary" onClick={handleRunIdentify}>
|
||||||
Run identify
|
Run identify
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button id="manual-button" variant="primary" onClick={handleRunRevenue}>
|
||||||
|
Revenue script
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<WebsiteChart websiteId={website.id} />
|
<WebsiteChart websiteId={website.id} />
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,9 @@ async function getEnabled() {
|
||||||
return !!process.env.ENABLE_TEST_CONSOLE;
|
return !!process.env.ENABLE_TEST_CONSOLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function ({ params: { websiteId } }) {
|
export default async function ({ params }: { params: { websiteId: string } }) {
|
||||||
|
const { websiteId } = await params;
|
||||||
|
|
||||||
const enabled = await getEnabled();
|
const enabled = await getEnabled();
|
||||||
|
|
||||||
if (!enabled) {
|
if (!enabled) {
|
||||||
|
|
|
||||||
|
|
@ -1,34 +1,34 @@
|
||||||
.buttons {
|
.buttons {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.item {
|
.item {
|
||||||
padding: 5px 0;
|
display: flex;
|
||||||
}
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
.item h1 {
|
width: 100%;
|
||||||
font-weight: 600;
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.item h2 {
|
|
||||||
font-size: 14px;
|
|
||||||
color: var(--base700);
|
|
||||||
}
|
|
||||||
|
|
||||||
.text {
|
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
border: 1px solid var(--base400);
|
border: 1px solid var(--base400);
|
||||||
background: var(--base50);
|
background: var(--base50);
|
||||||
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.active .text {
|
.text {
|
||||||
border-color: var(--base600);
|
position: relative;
|
||||||
box-shadow: 4px 4px 4px var(--base100);
|
}
|
||||||
|
|
||||||
|
.name {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.domain {
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--base700);
|
||||||
}
|
}
|
||||||
|
|
||||||
.dragActive {
|
.dragActive {
|
||||||
|
|
@ -38,3 +38,20 @@
|
||||||
.dragActive:active {
|
.dragActive:active {
|
||||||
cursor: grabbing;
|
cursor: grabbing;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search {
|
||||||
|
max-width: 360px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.active {
|
||||||
|
border-color: var(--base600);
|
||||||
|
box-shadow: 4px 4px 4px var(--base100);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { useState, useMemo } from 'react';
|
import { useState, useMemo, useEffect } from 'react';
|
||||||
import { DragDropContext, Draggable, Droppable } from 'react-beautiful-dnd';
|
import { DragDropContext, Draggable, Droppable } from 'react-beautiful-dnd';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { Button, Loading } from 'react-basics';
|
import { Button, Loading, Toggle, SearchField } from 'react-basics';
|
||||||
import { firstBy } from 'thenby';
|
import { firstBy } from 'thenby';
|
||||||
import useDashboard, { saveDashboard } from 'store/dashboard';
|
import useDashboard, { saveDashboard } from 'store/dashboard';
|
||||||
import { useMessages, useWebsites } from 'components/hooks';
|
import { useMessages, useWebsites } from 'components/hooks';
|
||||||
|
|
@ -11,20 +11,39 @@ const DRAG_ID = 'dashboard-website-ordering';
|
||||||
|
|
||||||
export function DashboardEdit({ teamId }: { teamId: string }) {
|
export function DashboardEdit({ teamId }: { teamId: string }) {
|
||||||
const settings = useDashboard();
|
const settings = useDashboard();
|
||||||
const { websiteOrder } = settings;
|
const { websiteOrder, websiteActive, isEdited } = settings;
|
||||||
const { formatMessage, labels } = useMessages();
|
const { formatMessage, labels } = useMessages();
|
||||||
const [order, setOrder] = useState(websiteOrder || []);
|
const [order, setOrder] = useState(websiteOrder || []);
|
||||||
|
const [active, setActive] = useState(websiteActive || []);
|
||||||
|
const [edited, setEdited] = useState(isEdited);
|
||||||
|
const [websites, setWebsites] = useState([]);
|
||||||
|
const [search, setSearch] = useState('');
|
||||||
|
|
||||||
const {
|
const {
|
||||||
result,
|
result,
|
||||||
query: { isLoading },
|
query: { isLoading },
|
||||||
|
setParams,
|
||||||
} = useWebsites({ teamId });
|
} = useWebsites({ teamId });
|
||||||
|
|
||||||
const websites = result?.data;
|
useEffect(() => {
|
||||||
|
if (result?.data) {
|
||||||
|
setWebsites(prevWebsites => {
|
||||||
|
const newWebsites = [...prevWebsites, ...result.data];
|
||||||
|
if (newWebsites.length < result.count) {
|
||||||
|
setParams(prevParams => ({ ...prevParams, page: prevParams.page + 1 }));
|
||||||
|
}
|
||||||
|
return newWebsites;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [result]);
|
||||||
|
|
||||||
const ordered = useMemo(() => {
|
const ordered = useMemo(() => {
|
||||||
if (websites) {
|
if (websites) {
|
||||||
return websites
|
return websites
|
||||||
.map((website: { id: any }) => ({ ...website, order: order.indexOf(website.id) }))
|
.map((website: { id: any; name: string; domain: string }) => ({
|
||||||
|
...website,
|
||||||
|
order: order.indexOf(website.id),
|
||||||
|
}))
|
||||||
.sort(firstBy('order'));
|
.sort(firstBy('order'));
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
|
|
@ -38,21 +57,33 @@ export function DashboardEdit({ teamId }: { teamId: string }) {
|
||||||
orderedWebsites.splice(destination.index, 0, removed);
|
orderedWebsites.splice(destination.index, 0, removed);
|
||||||
|
|
||||||
setOrder(orderedWebsites.map(website => website?.id || 0));
|
setOrder(orderedWebsites.map(website => website?.id || 0));
|
||||||
|
setEdited(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleActiveWebsites(id: string) {
|
||||||
|
setActive(prevActive =>
|
||||||
|
prevActive.includes(id) ? prevActive.filter(a => a !== id) : [...prevActive, id],
|
||||||
|
);
|
||||||
|
setEdited(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSave() {
|
function handleSave() {
|
||||||
saveDashboard({
|
saveDashboard({
|
||||||
editing: false,
|
editing: false,
|
||||||
|
isEdited: edited,
|
||||||
websiteOrder: order,
|
websiteOrder: order,
|
||||||
|
websiteActive: active,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleCancel() {
|
function handleCancel() {
|
||||||
saveDashboard({ editing: false, websiteOrder });
|
saveDashboard({ editing: false, websiteOrder, websiteActive, isEdited });
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleReset() {
|
function handleReset() {
|
||||||
setOrder([]);
|
setOrder([]);
|
||||||
|
setActive([]);
|
||||||
|
setEdited(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
|
|
@ -61,16 +92,19 @@ export function DashboardEdit({ teamId }: { teamId: string }) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className={styles.buttons}>
|
<div className={styles.header}>
|
||||||
<Button onClick={handleSave} variant="primary" size="sm">
|
<SearchField className={styles.search} value={search} onSearch={setSearch} />
|
||||||
{formatMessage(labels.save)}
|
<div className={styles.buttons}>
|
||||||
</Button>
|
<Button onClick={handleSave} variant="primary" size="sm">
|
||||||
<Button onClick={handleCancel} size="sm">
|
{formatMessage(labels.save)}
|
||||||
{formatMessage(labels.cancel)}
|
</Button>
|
||||||
</Button>
|
<Button onClick={handleCancel} size="sm">
|
||||||
<Button onClick={handleReset} size="sm">
|
{formatMessage(labels.cancel)}
|
||||||
{formatMessage(labels.reset)}
|
</Button>
|
||||||
</Button>
|
<Button onClick={handleReset} size="sm">
|
||||||
|
{formatMessage(labels.reset)}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.dragActive}>
|
<div className={styles.dragActive}>
|
||||||
<DragDropContext onDragEnd={handleWebsiteDrag}>
|
<DragDropContext onDragEnd={handleWebsiteDrag}>
|
||||||
|
|
@ -81,25 +115,38 @@ export function DashboardEdit({ teamId }: { teamId: string }) {
|
||||||
ref={provided.innerRef}
|
ref={provided.innerRef}
|
||||||
style={{ marginBottom: snapshot.isDraggingOver ? 260 : null }}
|
style={{ marginBottom: snapshot.isDraggingOver ? 260 : null }}
|
||||||
>
|
>
|
||||||
{ordered.map(({ id, name, domain }, index) => (
|
{ordered.map(({ id, name, domain }, index) => {
|
||||||
<Draggable key={id} draggableId={`${DRAG_ID}-${id}`} index={index}>
|
if (
|
||||||
{(provided, snapshot) => (
|
search &&
|
||||||
<div
|
!`${name.toLowerCase()}${domain.toLowerCase()}`.includes(search.toLowerCase())
|
||||||
ref={provided.innerRef}
|
) {
|
||||||
className={classNames(styles.item, {
|
return null;
|
||||||
[styles.active]: snapshot.isDragging,
|
}
|
||||||
})}
|
|
||||||
{...provided.draggableProps}
|
return (
|
||||||
{...provided.dragHandleProps}
|
<Draggable key={id} draggableId={`${DRAG_ID}-${id}`} index={index}>
|
||||||
>
|
{(provided, snapshot) => (
|
||||||
<div className={styles.text}>
|
<div
|
||||||
<h1>{name}</h1>
|
ref={provided.innerRef}
|
||||||
<h2>{domain}</h2>
|
className={classNames(styles.item, {
|
||||||
|
[styles.active]: snapshot.isDragging,
|
||||||
|
})}
|
||||||
|
{...provided.draggableProps}
|
||||||
|
{...provided.dragHandleProps}
|
||||||
|
>
|
||||||
|
<div className={styles.text}>
|
||||||
|
<div className={styles.name}>{name}</div>
|
||||||
|
<div className={styles.domain}>{domain}</div>
|
||||||
|
</div>
|
||||||
|
<Toggle
|
||||||
|
checked={active.includes(id)}
|
||||||
|
onChange={() => handleActiveWebsites(id)}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)}
|
||||||
)}
|
</Draggable>
|
||||||
</Draggable>
|
);
|
||||||
))}
|
})}
|
||||||
{provided.placeholder}
|
{provided.placeholder}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -13,9 +13,9 @@ import LinkButton from 'components/common/LinkButton';
|
||||||
export function DashboardPage() {
|
export function DashboardPage() {
|
||||||
const { formatMessage, labels, messages } = useMessages();
|
const { formatMessage, labels, messages } = useMessages();
|
||||||
const { teamId, renderTeamUrl } = useTeamUrl();
|
const { teamId, renderTeamUrl } = useTeamUrl();
|
||||||
const { showCharts, editing } = useDashboard();
|
const { showCharts, editing, isEdited } = useDashboard();
|
||||||
const { dir } = useLocale();
|
const { dir } = useLocale();
|
||||||
const pageSize = 10;
|
const pageSize = isEdited ? 200 : 10;
|
||||||
|
|
||||||
const { result, query, params, setParams } = useWebsites({ teamId }, { pageSize });
|
const { result, query, params, setParams } = useWebsites({ teamId }, { pageSize });
|
||||||
const { page } = params;
|
const { page } = params;
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { Dropdown, Item, Button, Flexbox } from 'react-basics';
|
import { Dropdown, Item, Button, Flexbox } from 'react-basics';
|
||||||
import moment from 'moment-timezone';
|
|
||||||
import { useTimezone, useMessages } from 'components/hooks';
|
import { useTimezone, useMessages } from 'components/hooks';
|
||||||
import { getTimezone } from 'lib/date';
|
import { getTimezone } from 'lib/date';
|
||||||
import styles from './TimezoneSetting.module.css';
|
import styles from './TimezoneSetting.module.css';
|
||||||
|
|
||||||
const timezones = moment.tz.names();
|
const timezones = Intl.supportedValuesOf('timeZone');
|
||||||
|
|
||||||
export function TimezoneSetting() {
|
export function TimezoneSetting() {
|
||||||
const [search, setSearch] = useState('');
|
const [search, setSearch] = useState('');
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
import { Metadata } from 'next';
|
import { Metadata } from 'next';
|
||||||
import ReportPage from './ReportPage';
|
import ReportPage from './ReportPage';
|
||||||
|
|
||||||
export default function ({ params: { reportId } }) {
|
export default async function ({ params }: { params: { reportId: string } }) {
|
||||||
|
const { reportId } = await params;
|
||||||
|
|
||||||
return <ReportPage reportId={reportId} />;
|
return <ReportPage reportId={reportId} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import Funnel from 'assets/funnel.svg';
|
import Funnel from 'assets/funnel.svg';
|
||||||
|
import Money from 'assets/money.svg';
|
||||||
import Lightbulb from 'assets/lightbulb.svg';
|
import Lightbulb from 'assets/lightbulb.svg';
|
||||||
import Magnet from 'assets/magnet.svg';
|
import Magnet from 'assets/magnet.svg';
|
||||||
import Path from 'assets/path.svg';
|
import Path from 'assets/path.svg';
|
||||||
|
|
@ -51,12 +52,12 @@ export function ReportTemplates({ showHeader = true }: { showHeader?: boolean })
|
||||||
url: renderTeamUrl('/reports/journey'),
|
url: renderTeamUrl('/reports/journey'),
|
||||||
icon: <Path />,
|
icon: <Path />,
|
||||||
},
|
},
|
||||||
// {
|
{
|
||||||
// title: formatMessage(labels.revenue),
|
title: formatMessage(labels.revenue),
|
||||||
// description: formatMessage(labels.revenueDescription),
|
description: formatMessage(labels.revenueDescription),
|
||||||
// url: renderTeamUrl('/reports/revenue'),
|
url: renderTeamUrl('/reports/revenue'),
|
||||||
// icon: <Money />,
|
icon: <Money />,
|
||||||
// },
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -1,98 +0,0 @@
|
||||||
import BarChart, { BarChartProps } from 'components/charts/BarChart';
|
|
||||||
import { useLocale, useMessages } from 'components/hooks';
|
|
||||||
import MetricCard from 'components/metrics/MetricCard';
|
|
||||||
import MetricsBar from 'components/metrics/MetricsBar';
|
|
||||||
import { renderDateLabels } from 'lib/charts';
|
|
||||||
import { formatLongNumber } from 'lib/format';
|
|
||||||
import { useContext, useMemo } from 'react';
|
|
||||||
import { ReportContext } from '../[reportId]/Report';
|
|
||||||
|
|
||||||
export interface PageviewsChartProps extends BarChartProps {
|
|
||||||
isLoading?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function RevenueChart({ isLoading, ...props }: PageviewsChartProps) {
|
|
||||||
const { formatMessage, labels } = useMessages();
|
|
||||||
const { locale } = useLocale();
|
|
||||||
const { report } = useContext(ReportContext);
|
|
||||||
const { data, parameters } = report || {};
|
|
||||||
|
|
||||||
const chartData = useMemo(() => {
|
|
||||||
if (!data) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
datasets: [
|
|
||||||
{
|
|
||||||
label: formatMessage(labels.average),
|
|
||||||
data: data?.chart.map(a => ({ x: a.time, y: a.avg })),
|
|
||||||
borderWidth: 2,
|
|
||||||
backgroundColor: '#8601B0',
|
|
||||||
borderColor: '#8601B0',
|
|
||||||
order: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: formatMessage(labels.total),
|
|
||||||
data: data?.chart.map(a => ({ x: a.time, y: a.sum })),
|
|
||||||
borderWidth: 2,
|
|
||||||
backgroundColor: '#f15bb5',
|
|
||||||
borderColor: '#f15bb5',
|
|
||||||
order: 2,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
}, [data, locale]);
|
|
||||||
|
|
||||||
const metricData = useMemo(() => {
|
|
||||||
if (!data) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
const { sum, avg, count, uniqueCount } = data.total;
|
|
||||||
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
value: sum,
|
|
||||||
label: formatMessage(labels.total),
|
|
||||||
formatValue: formatLongNumber,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: avg,
|
|
||||||
label: formatMessage(labels.average),
|
|
||||||
formatValue: formatLongNumber,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: count,
|
|
||||||
label: formatMessage(labels.transactions),
|
|
||||||
formatValue: formatLongNumber,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: uniqueCount,
|
|
||||||
label: formatMessage(labels.uniqueCustomers),
|
|
||||||
formatValue: formatLongNumber,
|
|
||||||
},
|
|
||||||
] as any;
|
|
||||||
}, [data, locale]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<MetricsBar isFetched={data}>
|
|
||||||
{metricData?.map(({ label, value, formatValue }) => {
|
|
||||||
return <MetricCard key={label} value={value} label={label} formatValue={formatValue} />;
|
|
||||||
})}
|
|
||||||
</MetricsBar>
|
|
||||||
{data && (
|
|
||||||
<BarChart
|
|
||||||
{...props}
|
|
||||||
data={chartData}
|
|
||||||
unit={parameters?.dateRange.unit}
|
|
||||||
isLoading={isLoading}
|
|
||||||
renderXLabel={renderDateLabels(parameters?.dateRange.unit, locale)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default RevenueChart;
|
|
||||||
|
|
@ -1,46 +1,41 @@
|
||||||
import { useMessages } from 'components/hooks';
|
import { useMessages } from 'components/hooks';
|
||||||
|
import useRevenueValues from 'components/hooks/queries/useRevenueValues';
|
||||||
import { useContext } from 'react';
|
import { useContext } from 'react';
|
||||||
import { Form, FormButtons, FormInput, FormRow, SubmitButton, TextField } from 'react-basics';
|
import { Dropdown, Form, FormButtons, FormInput, FormRow, Item, SubmitButton } from 'react-basics';
|
||||||
import BaseParameters from '../[reportId]/BaseParameters';
|
import BaseParameters from '../[reportId]/BaseParameters';
|
||||||
import { ReportContext } from '../[reportId]/Report';
|
import { ReportContext } from '../[reportId]/Report';
|
||||||
|
|
||||||
export function RevenueParameters() {
|
export function RevenueParameters() {
|
||||||
const { report, runReport, isRunning } = useContext(ReportContext);
|
const { report, runReport, isRunning } = useContext(ReportContext);
|
||||||
const { formatMessage, labels } = useMessages();
|
const { formatMessage, labels } = useMessages();
|
||||||
|
|
||||||
const { id, parameters } = report || {};
|
const { id, parameters } = report || {};
|
||||||
const { websiteId, dateRange } = parameters || {};
|
const { websiteId, dateRange } = parameters || {};
|
||||||
const queryDisabled = !websiteId || !dateRange;
|
const queryEnabled = websiteId && dateRange;
|
||||||
|
const { data: values = [] } = useRevenueValues(
|
||||||
|
websiteId,
|
||||||
|
dateRange?.startDate,
|
||||||
|
dateRange?.endDate,
|
||||||
|
);
|
||||||
|
|
||||||
const handleSubmit = (data: any, e: any) => {
|
const handleSubmit = (data: any, e: any) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
if (!queryDisabled) {
|
runReport(data);
|
||||||
runReport(data);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form values={parameters} onSubmit={handleSubmit} preventSubmit={true}>
|
<Form values={parameters} onSubmit={handleSubmit} preventSubmit={true}>
|
||||||
<BaseParameters showDateSelect={true} allowWebsiteSelect={!id} />
|
<BaseParameters showDateSelect={true} allowWebsiteSelect={!id} />
|
||||||
<FormRow label={formatMessage(labels.event)}>
|
<FormRow label={formatMessage(labels.currency)}>
|
||||||
<FormInput name="eventName" rules={{ required: formatMessage(labels.required) }}>
|
<FormInput name="currency" rules={{ required: formatMessage(labels.required) }}>
|
||||||
<TextField autoComplete="off" />
|
<Dropdown items={values.map(item => item.currency)}>
|
||||||
</FormInput>
|
{item => <Item key={item}>{item}</Item>}
|
||||||
</FormRow>
|
</Dropdown>
|
||||||
<FormRow label={formatMessage(labels.revenueProperty)}>
|
|
||||||
<FormInput name="revenueProperty" rules={{ required: formatMessage(labels.required) }}>
|
|
||||||
<TextField autoComplete="off" />
|
|
||||||
</FormInput>
|
|
||||||
</FormRow>
|
|
||||||
<FormRow label={formatMessage(labels.userProperty)}>
|
|
||||||
<FormInput name="userProperty">
|
|
||||||
<TextField autoComplete="off" />
|
|
||||||
</FormInput>
|
</FormInput>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
<FormButtons>
|
<FormButtons>
|
||||||
<SubmitButton variant="primary" disabled={queryDisabled} isLoading={isRunning}>
|
<SubmitButton variant="primary" disabled={!queryEnabled} isLoading={isRunning}>
|
||||||
{formatMessage(labels.runQuery)}
|
{formatMessage(labels.runQuery)}
|
||||||
</SubmitButton>
|
</SubmitButton>
|
||||||
</FormButtons>
|
</FormButtons>
|
||||||
|
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
.filters {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: space-between;
|
|
||||||
border: 1px solid var(--base400);
|
|
||||||
border-radius: var(--border-radius);
|
|
||||||
line-height: 32px;
|
|
||||||
padding: 10px;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
@ -1,15 +1,15 @@
|
||||||
import RevenueChart from './RevenueChart';
|
|
||||||
import RevenueParameters from './RevenueParameters';
|
|
||||||
import Report from '../[reportId]/Report';
|
|
||||||
import ReportHeader from '../[reportId]/ReportHeader';
|
|
||||||
import ReportMenu from '../[reportId]/ReportMenu';
|
|
||||||
import ReportBody from '../[reportId]/ReportBody';
|
|
||||||
import Money from 'assets/money.svg';
|
import Money from 'assets/money.svg';
|
||||||
import { REPORT_TYPES } from 'lib/constants';
|
import { REPORT_TYPES } from 'lib/constants';
|
||||||
|
import Report from '../[reportId]/Report';
|
||||||
|
import ReportBody from '../[reportId]/ReportBody';
|
||||||
|
import ReportHeader from '../[reportId]/ReportHeader';
|
||||||
|
import ReportMenu from '../[reportId]/ReportMenu';
|
||||||
|
import RevenueParameters from './RevenueParameters';
|
||||||
|
import RevenueView from './RevenueView';
|
||||||
|
|
||||||
const defaultParameters = {
|
const defaultParameters = {
|
||||||
type: REPORT_TYPES.revenue,
|
type: REPORT_TYPES.revenue,
|
||||||
parameters: { Revenue: [] },
|
parameters: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function RevenueReport({ reportId }: { reportId?: string }) {
|
export default function RevenueReport({ reportId }: { reportId?: string }) {
|
||||||
|
|
@ -20,7 +20,7 @@ export default function RevenueReport({ reportId }: { reportId?: string }) {
|
||||||
<RevenueParameters />
|
<RevenueParameters />
|
||||||
</ReportMenu>
|
</ReportMenu>
|
||||||
<ReportBody>
|
<ReportBody>
|
||||||
<RevenueChart unit="day" />
|
<RevenueView />
|
||||||
</ReportBody>
|
</ReportBody>
|
||||||
</Report>
|
</Report>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
38
src/app/(main)/reports/revenue/RevenueTable.tsx
Normal file
38
src/app/(main)/reports/revenue/RevenueTable.tsx
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
import EmptyPlaceholder from 'components/common/EmptyPlaceholder';
|
||||||
|
import { useMessages } from 'components/hooks';
|
||||||
|
import { useContext } from 'react';
|
||||||
|
import { GridColumn, GridTable } from 'react-basics';
|
||||||
|
import { ReportContext } from '../[reportId]/Report';
|
||||||
|
import { formatLongCurrency } from 'lib/format';
|
||||||
|
|
||||||
|
export function RevenueTable() {
|
||||||
|
const { report } = useContext(ReportContext);
|
||||||
|
const { formatMessage, labels } = useMessages();
|
||||||
|
const { data } = report || {};
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
return <EmptyPlaceholder />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<GridTable data={data.table || []}>
|
||||||
|
<GridColumn name="currency" label={formatMessage(labels.currency)} alignment="end">
|
||||||
|
{row => row.currency}
|
||||||
|
</GridColumn>
|
||||||
|
<GridColumn name="currency" label={formatMessage(labels.total)} width="300px" alignment="end">
|
||||||
|
{row => formatLongCurrency(row.sum, row.currency)}
|
||||||
|
</GridColumn>
|
||||||
|
<GridColumn name="currency" label={formatMessage(labels.average)} alignment="end">
|
||||||
|
{row => formatLongCurrency(row.count ? row.sum / row.count : 0, row.currency)}
|
||||||
|
</GridColumn>
|
||||||
|
<GridColumn name="currency" label={formatMessage(labels.transactions)} alignment="end">
|
||||||
|
{row => row.count}
|
||||||
|
</GridColumn>
|
||||||
|
<GridColumn name="currency" label={formatMessage(labels.uniqueCustomers)} alignment="end">
|
||||||
|
{row => row.unique_count}
|
||||||
|
</GridColumn>
|
||||||
|
</GridTable>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default RevenueTable;
|
||||||
11
src/app/(main)/reports/revenue/RevenueView.module.css
Normal file
11
src/app/(main)/reports/revenue/RevenueView.module.css
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
.container {
|
||||||
|
display: grid;
|
||||||
|
gap: 20px;
|
||||||
|
margin-bottom: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
156
src/app/(main)/reports/revenue/RevenueView.tsx
Normal file
156
src/app/(main)/reports/revenue/RevenueView.tsx
Normal file
|
|
@ -0,0 +1,156 @@
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import { colord } from 'colord';
|
||||||
|
import BarChart from 'components/charts/BarChart';
|
||||||
|
import PieChart from 'components/charts/PieChart';
|
||||||
|
import TypeIcon from 'components/common/TypeIcon';
|
||||||
|
import { useCountryNames, useLocale, useMessages } from 'components/hooks';
|
||||||
|
import { GridRow } from 'components/layout/Grid';
|
||||||
|
import ListTable from 'components/metrics/ListTable';
|
||||||
|
import MetricCard from 'components/metrics/MetricCard';
|
||||||
|
import MetricsBar from 'components/metrics/MetricsBar';
|
||||||
|
import { renderDateLabels } from 'lib/charts';
|
||||||
|
import { CHART_COLORS } from 'lib/constants';
|
||||||
|
import { formatLongCurrency, formatLongNumber } from 'lib/format';
|
||||||
|
import { useCallback, useContext, useMemo } from 'react';
|
||||||
|
import { ReportContext } from '../[reportId]/Report';
|
||||||
|
import RevenueTable from './RevenueTable';
|
||||||
|
import styles from './RevenueView.module.css';
|
||||||
|
|
||||||
|
export interface RevenueViewProps {
|
||||||
|
isLoading?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function RevenueView({ isLoading }: RevenueViewProps) {
|
||||||
|
const { formatMessage, labels } = useMessages();
|
||||||
|
const { locale } = useLocale();
|
||||||
|
const { countryNames } = useCountryNames(locale);
|
||||||
|
const { report } = useContext(ReportContext);
|
||||||
|
const {
|
||||||
|
data,
|
||||||
|
parameters: { dateRange, currency },
|
||||||
|
} = report || {};
|
||||||
|
const showTable = data?.table.length > 1;
|
||||||
|
|
||||||
|
const renderCountryName = useCallback(
|
||||||
|
({ x: code }) => (
|
||||||
|
<span className={classNames(locale, styles.row)}>
|
||||||
|
<TypeIcon type="country" value={code?.toLowerCase()} />
|
||||||
|
{countryNames[code]}
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
[countryNames, locale],
|
||||||
|
);
|
||||||
|
|
||||||
|
const chartData = useMemo(() => {
|
||||||
|
if (!data) return [];
|
||||||
|
|
||||||
|
const map = (data.chart as any[]).reduce((obj, { x, t, y }) => {
|
||||||
|
if (!obj[x]) {
|
||||||
|
obj[x] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
obj[x].push({ x: t, y });
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
return {
|
||||||
|
datasets: Object.keys(map).map((key, index) => {
|
||||||
|
const color = colord(CHART_COLORS[index % CHART_COLORS.length]);
|
||||||
|
return {
|
||||||
|
label: key,
|
||||||
|
data: map[key],
|
||||||
|
lineTension: 0,
|
||||||
|
backgroundColor: color.alpha(0.6).toRgbString(),
|
||||||
|
borderColor: color.alpha(0.7).toRgbString(),
|
||||||
|
borderWidth: 1,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
}, [data]);
|
||||||
|
|
||||||
|
const countryData = useMemo(() => {
|
||||||
|
if (!data) return [];
|
||||||
|
|
||||||
|
const labels = data.country.map(({ name }) => name);
|
||||||
|
const datasets = [
|
||||||
|
{
|
||||||
|
data: data.country.map(({ value }) => value),
|
||||||
|
backgroundColor: CHART_COLORS,
|
||||||
|
borderWidth: 0,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return { labels, datasets };
|
||||||
|
}, [data]);
|
||||||
|
|
||||||
|
const metricData = useMemo(() => {
|
||||||
|
if (!data) return [];
|
||||||
|
|
||||||
|
const { sum, count, unique_count } = data.total;
|
||||||
|
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
value: sum,
|
||||||
|
label: formatMessage(labels.total),
|
||||||
|
formatValue: n => formatLongCurrency(n, currency),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: count ? sum / count : 0,
|
||||||
|
label: formatMessage(labels.average),
|
||||||
|
formatValue: n => formatLongCurrency(n, currency),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: count,
|
||||||
|
label: formatMessage(labels.transactions),
|
||||||
|
formatValue: formatLongNumber,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: unique_count,
|
||||||
|
label: formatMessage(labels.uniqueCustomers),
|
||||||
|
formatValue: formatLongNumber,
|
||||||
|
},
|
||||||
|
] as any;
|
||||||
|
}, [data, locale]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className={styles.container}>
|
||||||
|
<MetricsBar isFetched={data}>
|
||||||
|
{metricData?.map(({ label, value, formatValue }) => {
|
||||||
|
return <MetricCard key={label} value={value} label={label} formatValue={formatValue} />;
|
||||||
|
})}
|
||||||
|
</MetricsBar>
|
||||||
|
{data && (
|
||||||
|
<>
|
||||||
|
<BarChart
|
||||||
|
minDate={dateRange?.startDate}
|
||||||
|
maxDate={dateRange?.endDate}
|
||||||
|
data={chartData}
|
||||||
|
unit={dateRange?.unit}
|
||||||
|
stacked={true}
|
||||||
|
currency={currency}
|
||||||
|
renderXLabel={renderDateLabels(dateRange?.unit, locale)}
|
||||||
|
isLoading={isLoading}
|
||||||
|
/>
|
||||||
|
<GridRow columns="two">
|
||||||
|
<ListTable
|
||||||
|
metric={formatMessage(labels.country)}
|
||||||
|
data={data?.country.map(({ name, value }) => ({
|
||||||
|
x: name,
|
||||||
|
y: Number(value),
|
||||||
|
z: (value / data?.total.sum) * 100,
|
||||||
|
}))}
|
||||||
|
renderLabel={renderCountryName}
|
||||||
|
/>
|
||||||
|
<PieChart type="doughnut" data={countryData} />
|
||||||
|
</GridRow>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{showTable && <RevenueTable />}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default RevenueView;
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
import UserPage from './UserPage';
|
import UserPage from './UserPage';
|
||||||
import { Metadata } from 'next';
|
import { Metadata } from 'next';
|
||||||
|
|
||||||
export default function ({ params: { userId } }) {
|
export default async function ({ params }: { params: { userId: string } }) {
|
||||||
|
const { userId } = await params;
|
||||||
|
|
||||||
return <UserPage userId={userId} />;
|
return <UserPage userId={userId} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
import WebsiteSettingsPage from './WebsiteSettingsPage';
|
import WebsiteSettingsPage from './WebsiteSettingsPage';
|
||||||
import { Metadata } from 'next';
|
import { Metadata } from 'next';
|
||||||
|
|
||||||
export default async function ({ params: { websiteId } }) {
|
export default async function ({ params }: { params: { websiteId: string } }) {
|
||||||
|
const { websiteId } = await params;
|
||||||
|
|
||||||
return <WebsiteSettingsPage websiteId={websiteId} />;
|
return <WebsiteSettingsPage websiteId={websiteId} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
import { Metadata } from 'next';
|
import { Metadata } from 'next';
|
||||||
import WebsitesSettingsPage from './WebsitesSettingsPage';
|
import WebsitesSettingsPage from './WebsitesSettingsPage';
|
||||||
|
|
||||||
export default function ({ params: { teamId } }: { params: { teamId: string } }) {
|
export default async function ({ params }: { params: { teamId: string } }) {
|
||||||
|
const { teamId } = await params;
|
||||||
|
|
||||||
return <WebsitesSettingsPage teamId={teamId} />;
|
return <WebsitesSettingsPage teamId={teamId} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,15 @@ import TeamProvider from './TeamProvider';
|
||||||
import { Metadata } from 'next';
|
import { Metadata } from 'next';
|
||||||
import TeamSettingsLayout from './settings/TeamSettingsLayout';
|
import TeamSettingsLayout from './settings/TeamSettingsLayout';
|
||||||
|
|
||||||
export default function ({ children, params: { teamId } }) {
|
export default async function ({
|
||||||
|
children,
|
||||||
|
params,
|
||||||
|
}: {
|
||||||
|
children: any;
|
||||||
|
params: { teamId: string };
|
||||||
|
}) {
|
||||||
|
const { teamId } = await params;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TeamProvider teamId={teamId}>
|
<TeamProvider teamId={teamId}>
|
||||||
<TeamSettingsLayout>{children}</TeamSettingsLayout>
|
<TeamSettingsLayout>{children}</TeamSettingsLayout>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
import TeamMembersPage from './TeamMembersPage';
|
|
||||||
import { Metadata } from 'next';
|
import { Metadata } from 'next';
|
||||||
|
import TeamMembersPage from './TeamMembersPage';
|
||||||
|
|
||||||
|
export default async function ({ params }: { params: { teamId: string } }) {
|
||||||
|
const { teamId } = await params;
|
||||||
|
|
||||||
export default function ({ params: { teamId } }) {
|
|
||||||
return <TeamMembersPage teamId={teamId} />;
|
return <TeamMembersPage teamId={teamId} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
import { Metadata } from 'next';
|
import { Metadata } from 'next';
|
||||||
import TeamPage from './TeamPage';
|
import TeamPage from './TeamPage';
|
||||||
|
|
||||||
export default function ({ params: { teamId } }) {
|
export default async function ({ params }: { params: { teamId: string } }) {
|
||||||
|
const { teamId } = await params;
|
||||||
|
|
||||||
return <TeamPage teamId={teamId} />;
|
return <TeamPage teamId={teamId} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
import TeamWebsitesPage from './TeamWebsitesPage';
|
import TeamWebsitesPage from './TeamWebsitesPage';
|
||||||
import { Metadata } from 'next';
|
import { Metadata } from 'next';
|
||||||
|
|
||||||
export default function ({ params: { teamId } }) {
|
export default async function ({ params }: { params: { teamId: string } }) {
|
||||||
|
const { teamId } = await params;
|
||||||
|
|
||||||
return <TeamWebsitesPage teamId={teamId} />;
|
return <TeamWebsitesPage teamId={teamId} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ export function WebsiteChart({
|
||||||
compareMode?: boolean;
|
compareMode?: boolean;
|
||||||
}) {
|
}) {
|
||||||
const { dateRange, dateCompare } = useDateRange(websiteId);
|
const { dateRange, dateCompare } = useDateRange(websiteId);
|
||||||
const { startDate, endDate, unit } = dateRange;
|
const { startDate, endDate, unit, value } = dateRange;
|
||||||
const { data, isLoading } = useWebsitePageviews(websiteId, compareMode ? dateCompare : undefined);
|
const { data, isLoading } = useWebsitePageviews(websiteId, compareMode ? dateCompare : undefined);
|
||||||
const { pageviews, sessions, compare } = (data || {}) as any;
|
const { pageviews, sessions, compare } = (data || {}) as any;
|
||||||
|
|
||||||
|
|
@ -49,6 +49,7 @@ export function WebsiteChart({
|
||||||
maxDate={endDate.toISOString()}
|
maxDate={endDate.toISOString()}
|
||||||
unit={unit}
|
unit={unit}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
|
isAllTime={value === 'all'}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,17 +18,16 @@ export default function WebsiteChartList({
|
||||||
limit?: number;
|
limit?: number;
|
||||||
}) {
|
}) {
|
||||||
const { formatMessage, labels } = useMessages();
|
const { formatMessage, labels } = useMessages();
|
||||||
const { websiteOrder } = useDashboard();
|
const { websiteOrder, websiteActive } = useDashboard();
|
||||||
const { renderTeamUrl } = useTeamUrl();
|
const { renderTeamUrl } = useTeamUrl();
|
||||||
const { dir } = useLocale();
|
const { dir } = useLocale();
|
||||||
|
|
||||||
const ordered = useMemo(
|
const ordered = useMemo(() => {
|
||||||
() =>
|
return websites
|
||||||
websites
|
.filter(website => (websiteActive.length ? websiteActive.includes(website.id) : true))
|
||||||
.map(website => ({ ...website, order: websiteOrder.indexOf(website.id) || 0 }))
|
.map(website => ({ ...website, order: websiteOrder.indexOf(website.id) || 0 }))
|
||||||
.sort(firstBy('order')),
|
.sort(firstBy('order'));
|
||||||
[websites, websiteOrder],
|
}, [websites, websiteOrder, websiteActive]);
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
|
|
||||||
|
|
@ -59,5 +59,6 @@
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding-inline-end: 0;
|
padding-inline-end: 0;
|
||||||
|
z-index: 10;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,21 @@
|
||||||
import { Icons, Icon, Text, Dropdown, Item } from 'react-basics';
|
import LinkButton from 'components/common/LinkButton';
|
||||||
|
import { useLocale, useMessages, useNavigation } from 'components/hooks';
|
||||||
|
import SideNav from 'components/layout/SideNav';
|
||||||
import BrowsersTable from 'components/metrics/BrowsersTable';
|
import BrowsersTable from 'components/metrics/BrowsersTable';
|
||||||
import CountriesTable from 'components/metrics/CountriesTable';
|
|
||||||
import RegionsTable from 'components/metrics/RegionsTable';
|
|
||||||
import CitiesTable from 'components/metrics/CitiesTable';
|
import CitiesTable from 'components/metrics/CitiesTable';
|
||||||
|
import CountriesTable from 'components/metrics/CountriesTable';
|
||||||
import DevicesTable from 'components/metrics/DevicesTable';
|
import DevicesTable from 'components/metrics/DevicesTable';
|
||||||
|
import EventsTable from 'components/metrics/EventsTable';
|
||||||
|
import HostsTable from 'components/metrics/HostsTable';
|
||||||
import LanguagesTable from 'components/metrics/LanguagesTable';
|
import LanguagesTable from 'components/metrics/LanguagesTable';
|
||||||
import OSTable from 'components/metrics/OSTable';
|
import OSTable from 'components/metrics/OSTable';
|
||||||
import PagesTable from 'components/metrics/PagesTable';
|
import PagesTable from 'components/metrics/PagesTable';
|
||||||
import QueryParametersTable from 'components/metrics/QueryParametersTable';
|
import QueryParametersTable from 'components/metrics/QueryParametersTable';
|
||||||
import ReferrersTable from 'components/metrics/ReferrersTable';
|
import ReferrersTable from 'components/metrics/ReferrersTable';
|
||||||
import HostsTable from 'components/metrics/HostsTable';
|
import RegionsTable from 'components/metrics/RegionsTable';
|
||||||
import ScreenTable from 'components/metrics/ScreenTable';
|
import ScreenTable from 'components/metrics/ScreenTable';
|
||||||
import EventsTable from 'components/metrics/EventsTable';
|
import TagsTable from 'components/metrics/TagsTable';
|
||||||
import SideNav from 'components/layout/SideNav';
|
import { Dropdown, Icon, Icons, Item, Text } from 'react-basics';
|
||||||
import { useNavigation, useMessages, useLocale } from 'components/hooks';
|
|
||||||
import LinkButton from 'components/common/LinkButton';
|
|
||||||
import styles from './WebsiteExpandedView.module.css';
|
import styles from './WebsiteExpandedView.module.css';
|
||||||
|
|
||||||
const views = {
|
const views = {
|
||||||
|
|
@ -34,6 +35,7 @@ const views = {
|
||||||
language: LanguagesTable,
|
language: LanguagesTable,
|
||||||
event: EventsTable,
|
event: EventsTable,
|
||||||
query: QueryParametersTable,
|
query: QueryParametersTable,
|
||||||
|
tag: TagsTable,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function WebsiteExpandedView({
|
export default function WebsiteExpandedView({
|
||||||
|
|
@ -117,6 +119,11 @@ export default function WebsiteExpandedView({
|
||||||
label: formatMessage(labels.hosts),
|
label: formatMessage(labels.hosts),
|
||||||
url: renderUrl({ view: 'host' }),
|
url: renderUrl({ view: 'host' }),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: 'tag',
|
||||||
|
label: formatMessage(labels.tags),
|
||||||
|
url: renderUrl({ view: 'tag' }),
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const DetailsComponent = views[view] || (() => null);
|
const DetailsComponent = views[view] || (() => null);
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,25 @@
|
||||||
import { useState } from 'react';
|
|
||||||
import SideNav from 'components/layout/SideNav';
|
|
||||||
import { useDateRange, useMessages, useNavigation } from 'components/hooks';
|
import { useDateRange, useMessages, useNavigation } from 'components/hooks';
|
||||||
import PagesTable from 'components/metrics/PagesTable';
|
|
||||||
import ReferrersTable from 'components/metrics/ReferrersTable';
|
|
||||||
import BrowsersTable from 'components/metrics/BrowsersTable';
|
|
||||||
import OSTable from 'components/metrics/OSTable';
|
|
||||||
import DevicesTable from 'components/metrics/DevicesTable';
|
|
||||||
import ScreenTable from 'components/metrics/ScreenTable';
|
|
||||||
import CountriesTable from 'components/metrics/CountriesTable';
|
|
||||||
import RegionsTable from 'components/metrics/RegionsTable';
|
|
||||||
import CitiesTable from 'components/metrics/CitiesTable';
|
|
||||||
import LanguagesTable from 'components/metrics/LanguagesTable';
|
|
||||||
import EventsTable from 'components/metrics/EventsTable';
|
|
||||||
import QueryParametersTable from 'components/metrics/QueryParametersTable';
|
|
||||||
import { Grid, GridRow } from 'components/layout/Grid';
|
import { Grid, GridRow } from 'components/layout/Grid';
|
||||||
|
import SideNav from 'components/layout/SideNav';
|
||||||
|
import BrowsersTable from 'components/metrics/BrowsersTable';
|
||||||
|
import ChangeLabel from 'components/metrics/ChangeLabel';
|
||||||
|
import CitiesTable from 'components/metrics/CitiesTable';
|
||||||
|
import CountriesTable from 'components/metrics/CountriesTable';
|
||||||
|
import DevicesTable from 'components/metrics/DevicesTable';
|
||||||
|
import EventsTable from 'components/metrics/EventsTable';
|
||||||
|
import LanguagesTable from 'components/metrics/LanguagesTable';
|
||||||
import MetricsTable from 'components/metrics/MetricsTable';
|
import MetricsTable from 'components/metrics/MetricsTable';
|
||||||
import useStore from 'store/websites';
|
import OSTable from 'components/metrics/OSTable';
|
||||||
|
import PagesTable from 'components/metrics/PagesTable';
|
||||||
|
import QueryParametersTable from 'components/metrics/QueryParametersTable';
|
||||||
|
import ReferrersTable from 'components/metrics/ReferrersTable';
|
||||||
|
import RegionsTable from 'components/metrics/RegionsTable';
|
||||||
|
import ScreenTable from 'components/metrics/ScreenTable';
|
||||||
|
import TagsTable from 'components/metrics/TagsTable';
|
||||||
import { getCompareDate } from 'lib/date';
|
import { getCompareDate } from 'lib/date';
|
||||||
import { formatNumber } from 'lib/format';
|
import { formatNumber } from 'lib/format';
|
||||||
import ChangeLabel from 'components/metrics/ChangeLabel';
|
import { useState } from 'react';
|
||||||
|
import useStore from 'store/websites';
|
||||||
import styles from './WebsiteCompareTables.module.css';
|
import styles from './WebsiteCompareTables.module.css';
|
||||||
|
|
||||||
const views = {
|
const views = {
|
||||||
|
|
@ -35,6 +36,7 @@ const views = {
|
||||||
language: LanguagesTable,
|
language: LanguagesTable,
|
||||||
event: EventsTable,
|
event: EventsTable,
|
||||||
query: QueryParametersTable,
|
query: QueryParametersTable,
|
||||||
|
tag: TagsTable,
|
||||||
};
|
};
|
||||||
|
|
||||||
export function WebsiteCompareTables({ websiteId }: { websiteId: string }) {
|
export function WebsiteCompareTables({ websiteId }: { websiteId: string }) {
|
||||||
|
|
@ -109,6 +111,16 @@ export function WebsiteCompareTables({ websiteId }: { websiteId: string }) {
|
||||||
label: formatMessage(labels.queryParameters),
|
label: formatMessage(labels.queryParameters),
|
||||||
url: renderUrl({ view: 'query' }),
|
url: renderUrl({ view: 'query' }),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: 'host',
|
||||||
|
label: formatMessage(labels.hosts),
|
||||||
|
url: renderUrl({ view: 'host' }),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'tag',
|
||||||
|
label: formatMessage(labels.tags),
|
||||||
|
url: renderUrl({ view: 'tag' }),
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const renderChange = ({ x, y }) => {
|
const renderChange = ({ x, y }) => {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
import WebsiteComparePage from './WebsiteComparePage';
|
import WebsiteComparePage from './WebsiteComparePage';
|
||||||
import { Metadata } from 'next';
|
import { Metadata } from 'next';
|
||||||
|
|
||||||
export default function ({ params: { websiteId } }) {
|
export default async function ({ params }: { params: { websiteId: string } }) {
|
||||||
|
const { websiteId } = await params;
|
||||||
|
|
||||||
return <WebsiteComparePage websiteId={websiteId} />;
|
return <WebsiteComparePage websiteId={websiteId} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ export function EventProperties({ websiteId }: { websiteId: string }) {
|
||||||
{propertyName && (
|
{propertyName && (
|
||||||
<div className={styles.chart}>
|
<div className={styles.chart}>
|
||||||
<div className={styles.title}>{propertyName}</div>
|
<div className={styles.title}>{propertyName}</div>
|
||||||
<PieChart key={propertyName} type="doughnut" data={chartData} />
|
<PieChart key={propertyName + eventName} type="doughnut" data={chartData} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
import { Metadata } from 'next';
|
import { Metadata } from 'next';
|
||||||
import EventsPage from './EventsPage';
|
import EventsPage from './EventsPage';
|
||||||
|
|
||||||
export default async function ({ params: { websiteId } }) {
|
export default async function ({ params }: { params: { websiteId: string } }) {
|
||||||
|
const { websiteId } = await params;
|
||||||
|
|
||||||
return <EventsPage websiteId={websiteId} />;
|
return <EventsPage websiteId={websiteId} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,15 @@
|
||||||
import { Metadata } from 'next';
|
import { Metadata } from 'next';
|
||||||
import WebsiteProvider from './WebsiteProvider';
|
import WebsiteProvider from './WebsiteProvider';
|
||||||
|
|
||||||
export default function ({ children, params: { websiteId } }) {
|
export default async function ({
|
||||||
|
children,
|
||||||
|
params,
|
||||||
|
}: {
|
||||||
|
children: any;
|
||||||
|
params: { websiteId: string };
|
||||||
|
}) {
|
||||||
|
const { websiteId } = await params;
|
||||||
|
|
||||||
return <WebsiteProvider websiteId={websiteId}>{children}</WebsiteProvider>;
|
return <WebsiteProvider websiteId={websiteId}>{children}</WebsiteProvider>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
import WebsiteDetailsPage from './WebsiteDetailsPage';
|
import WebsiteDetailsPage from './WebsiteDetailsPage';
|
||||||
import { Metadata } from 'next';
|
import { Metadata } from 'next';
|
||||||
|
|
||||||
export default function WebsitePage({ params: { websiteId } }) {
|
export default async function WebsitePage({ params }: { params: { websiteId: string } }) {
|
||||||
|
const { websiteId } = await params;
|
||||||
|
|
||||||
return <WebsiteDetailsPage websiteId={websiteId} />;
|
return <WebsiteDetailsPage websiteId={websiteId} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ export function RealtimeCountries({ data }) {
|
||||||
|
|
||||||
const renderCountryName = useCallback(
|
const renderCountryName = useCallback(
|
||||||
({ x: code }) => (
|
({ x: code }) => (
|
||||||
<span className={classNames(locale, styles.row)}>
|
<span className={classNames(styles.row)}>
|
||||||
<TypeIcon type="country" value={code?.toLowerCase()} />
|
<TypeIcon type="country" value={code?.toLowerCase()} />
|
||||||
{countryNames[code]}
|
{countryNames[code]}
|
||||||
</span>
|
</span>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
import WebsiteRealtimePage from './WebsiteRealtimePage';
|
import WebsiteRealtimePage from './WebsiteRealtimePage';
|
||||||
import { Metadata } from 'next';
|
import { Metadata } from 'next';
|
||||||
|
|
||||||
export default function ({ params: { websiteId } }) {
|
export default async function ({ params }: { params: { websiteId: string } }) {
|
||||||
|
const { websiteId } = await params;
|
||||||
|
|
||||||
return <WebsiteRealtimePage websiteId={websiteId} />;
|
return <WebsiteRealtimePage websiteId={websiteId} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
import WebsiteReportsPage from './WebsiteReportsPage';
|
import WebsiteReportsPage from './WebsiteReportsPage';
|
||||||
import { Metadata } from 'next';
|
import { Metadata } from 'next';
|
||||||
|
|
||||||
export default function ({ params: { websiteId } }) {
|
export default async function ({ params }: { params: { websiteId: string } }) {
|
||||||
|
const { websiteId } = await params;
|
||||||
|
|
||||||
return <WebsiteReportsPage websiteId={websiteId} />;
|
return <WebsiteReportsPage websiteId={websiteId} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.row {
|
.row {
|
||||||
display: flex;
|
display: grid;
|
||||||
|
grid-template-columns: max-content max-content 1fr;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
}
|
}
|
||||||
|
|
@ -15,10 +16,6 @@
|
||||||
width: 150px;
|
width: 150px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.value {
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ export function SessionActivity({
|
||||||
</StatusLight>
|
</StatusLight>
|
||||||
</div>
|
</div>
|
||||||
<Icon>{eventName ? <Icons.Bolt /> : <Icons.Eye />}</Icon>
|
<Icon>{eventName ? <Icons.Bolt /> : <Icons.Eye />}</Icon>
|
||||||
<div className={styles.value}>{eventName || urlPath}</div>
|
<div>{eventName || urlPath}</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,13 @@
|
||||||
import SessionDetailsPage from './SessionDetailsPage';
|
import SessionDetailsPage from './SessionDetailsPage';
|
||||||
import { Metadata } from 'next';
|
import { Metadata } from 'next';
|
||||||
|
|
||||||
export default function WebsitePage({ params: { websiteId, sessionId } }) {
|
export default async function WebsitePage({
|
||||||
|
params,
|
||||||
|
}: {
|
||||||
|
params: { websiteId: string; sessionId: string };
|
||||||
|
}) {
|
||||||
|
const { websiteId, sessionId } = await params;
|
||||||
|
|
||||||
return <SessionDetailsPage websiteId={websiteId} sessionId={sessionId} />;
|
return <SessionDetailsPage websiteId={websiteId} sessionId={sessionId} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
import SessionsPage from './SessionsPage';
|
import SessionsPage from './SessionsPage';
|
||||||
import { Metadata } from 'next';
|
import { Metadata } from 'next';
|
||||||
|
|
||||||
export default function ({ params: { websiteId } }) {
|
export default async function ({ params }: { params: { websiteId: string } }) {
|
||||||
|
const { websiteId } = await params;
|
||||||
|
|
||||||
return <SessionsPage websiteId={websiteId} />;
|
return <SessionsPage websiteId={websiteId} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import { Metadata } from 'next';
|
import { Metadata } from 'next';
|
||||||
import Providers from './Providers';
|
import Providers from './Providers';
|
||||||
|
import '@fontsource/inter/300.css';
|
||||||
import '@fontsource/inter/400.css';
|
import '@fontsource/inter/400.css';
|
||||||
|
import '@fontsource/inter/500.css';
|
||||||
import '@fontsource/inter/700.css';
|
import '@fontsource/inter/700.css';
|
||||||
import '@fontsource/inter/800.css';
|
|
||||||
import 'react-basics/dist/styles.css';
|
import 'react-basics/dist/styles.css';
|
||||||
import 'styles/locale.css';
|
|
||||||
import 'styles/index.css';
|
import 'styles/index.css';
|
||||||
import 'styles/variables.css';
|
import 'styles/variables.css';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
import SharePage from './SharePage';
|
import SharePage from './SharePage';
|
||||||
|
|
||||||
export default function ({ params: { shareId } }) {
|
export default async function ({ params }: { params: { shareId: string } }) {
|
||||||
|
const { shareId } = await params;
|
||||||
|
|
||||||
return <SharePage shareId={shareId[0]} />;
|
return <SharePage shareId={shareId[0]} />;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
8
src/assets/pushpin.svg
Normal file
8
src/assets/pushpin.svg
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 1024 1024"
|
||||||
|
fill="currentColor"
|
||||||
|
height="1em"
|
||||||
|
width="1em"
|
||||||
|
>
|
||||||
|
<path d="M878.3 392.1L631.9 145.7c-6.5-6.5-15-9.7-23.5-9.7s-17 3.2-23.5 9.7L423.8 306.9c-12.2-1.4-24.5-2-36.8-2-73.2 0-146.4 24.1-206.5 72.3-15.4 12.3-16.6 35.4-2.7 49.4l181.7 181.7-215.4 215.2a15.8 15.8 0 00-4.6 9.8l-3.4 37.2c-.9 9.4 6.6 17.4 15.9 17.4.5 0 1 0 1.5-.1l37.2-3.4c3.7-.3 7.2-2 9.8-4.6l215.4-215.4 181.7 181.7c6.5 6.5 15 9.7 23.5 9.7 9.7 0 19.3-4.2 25.9-12.4 56.3-70.3 79.7-158.3 70.2-243.4l161.1-161.1c12.9-12.8 12.9-33.8 0-46.8z" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 571 B |
|
|
@ -7,12 +7,14 @@ import { useMemo, useState } from 'react';
|
||||||
export interface BarChartProps extends ChartProps {
|
export interface BarChartProps extends ChartProps {
|
||||||
unit: string;
|
unit: string;
|
||||||
stacked?: boolean;
|
stacked?: boolean;
|
||||||
|
currency?: string;
|
||||||
renderXLabel?: (label: string, index: number, values: any[]) => string;
|
renderXLabel?: (label: string, index: number, values: any[]) => string;
|
||||||
renderYLabel?: (label: string, index: number, values: any[]) => string;
|
renderYLabel?: (label: string, index: number, values: any[]) => string;
|
||||||
XAxisType?: string;
|
XAxisType?: string;
|
||||||
YAxisType?: string;
|
YAxisType?: string;
|
||||||
minDate?: number | string;
|
minDate?: number | string;
|
||||||
maxDate?: number | string;
|
maxDate?: number | string;
|
||||||
|
isAllTime?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function BarChart(props: BarChartProps) {
|
export function BarChart(props: BarChartProps) {
|
||||||
|
|
@ -27,6 +29,8 @@ export function BarChart(props: BarChartProps) {
|
||||||
stacked = false,
|
stacked = false,
|
||||||
minDate,
|
minDate,
|
||||||
maxDate,
|
maxDate,
|
||||||
|
currency,
|
||||||
|
isAllTime,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const options: any = useMemo(() => {
|
const options: any = useMemo(() => {
|
||||||
|
|
@ -35,7 +39,7 @@ export function BarChart(props: BarChartProps) {
|
||||||
x: {
|
x: {
|
||||||
type: XAxisType,
|
type: XAxisType,
|
||||||
stacked: true,
|
stacked: true,
|
||||||
min: minDate && new Date(minDate).getSeconds() === 0 ? minDate : '',
|
min: isAllTime ? '' : minDate,
|
||||||
max: maxDate,
|
max: maxDate,
|
||||||
time: {
|
time: {
|
||||||
unit,
|
unit,
|
||||||
|
|
@ -76,7 +80,9 @@ export function BarChart(props: BarChartProps) {
|
||||||
const handleTooltip = ({ tooltip }: { tooltip: any }) => {
|
const handleTooltip = ({ tooltip }: { tooltip: any }) => {
|
||||||
const { opacity } = tooltip;
|
const { opacity } = tooltip;
|
||||||
|
|
||||||
setTooltip(opacity ? <BarChartTooltip tooltip={tooltip} unit={unit} /> : null);
|
setTooltip(
|
||||||
|
opacity ? <BarChartTooltip tooltip={tooltip} unit={unit} currency={currency} /> : null,
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { formatDate } from 'lib/date';
|
|
||||||
import { Flexbox, StatusLight } from 'react-basics';
|
|
||||||
import { formatLongNumber } from 'lib/format';
|
|
||||||
import { useLocale } from 'components/hooks';
|
import { useLocale } from 'components/hooks';
|
||||||
|
import { formatDate } from 'lib/date';
|
||||||
|
import { formatLongCurrency, formatLongNumber } from 'lib/format';
|
||||||
|
import { Flexbox, StatusLight } from 'react-basics';
|
||||||
|
|
||||||
const formats = {
|
const formats = {
|
||||||
millisecond: 'T',
|
millisecond: 'T',
|
||||||
|
|
@ -15,7 +15,7 @@ const formats = {
|
||||||
year: 'yyyy',
|
year: 'yyyy',
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function BarChartTooltip({ tooltip, unit }) {
|
export default function BarChartTooltip({ tooltip, unit, currency }) {
|
||||||
const { locale } = useLocale();
|
const { locale } = useLocale();
|
||||||
const { labelColors, dataPoints } = tooltip;
|
const { labelColors, dataPoints } = tooltip;
|
||||||
|
|
||||||
|
|
@ -26,7 +26,10 @@ export default function BarChartTooltip({ tooltip, unit }) {
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<StatusLight color={labelColors?.[0]?.backgroundColor}>
|
<StatusLight color={labelColors?.[0]?.backgroundColor}>
|
||||||
{formatLongNumber(dataPoints[0].raw.y)} {dataPoints[0].dataset.label}
|
{currency
|
||||||
|
? formatLongCurrency(dataPoints[0].raw.y, currency)
|
||||||
|
: formatLongNumber(dataPoints[0].raw.y)}{' '}
|
||||||
|
{dataPoints[0].dataset.label}
|
||||||
</StatusLight>
|
</StatusLight>
|
||||||
</div>
|
</div>
|
||||||
</Flexbox>
|
</Flexbox>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
function getHostName(url: string) {
|
function getHostName(url: string) {
|
||||||
const match = url.match(/^(?:https?:\/\/)?(?:[^@\n]+@)?(?:www\.)?([^:/\n?=]+)/im);
|
const match = url.match(/^(?:https?:\/\/)?(?:[^@\n]+@)?([^:/\n?=]+)/im);
|
||||||
return match && match.length > 1 ? match[1] : null;
|
return match && match.length > 1 ? match[1] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,12 @@ export function TypeIcon({
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<img
|
<img
|
||||||
src={`${process.env.basePath || ''}/images/${type}/${
|
src={`${process.env.basePath || ''}/images/${type}/${value
|
||||||
value?.replaceAll(' ', '-').toLowerCase() || 'unknown'
|
?.replaceAll(' ', '-')
|
||||||
}.png`}
|
.toLowerCase()}.png`}
|
||||||
|
onError={e => {
|
||||||
|
e.currentTarget.src = `${process.env.basePath || ''}/images/${type}/unknown.png`;
|
||||||
|
}}
|
||||||
alt={value}
|
alt={value}
|
||||||
width={type === 'country' ? undefined : 16}
|
width={type === 'country' ? undefined : 16}
|
||||||
height={type === 'country' ? undefined : 16}
|
height={type === 'country' ? undefined : 16}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,7 @@
|
||||||
export * from './queries/useApi';
|
|
||||||
export * from './queries/useConfig';
|
export * from './queries/useConfig';
|
||||||
export * from './queries/useEventDataEvents';
|
export * from './queries/useEventDataEvents';
|
||||||
export * from './queries/useEventDataProperties';
|
export * from './queries/useEventDataProperties';
|
||||||
export * from './queries/useEventDataValues';
|
export * from './queries/useEventDataValues';
|
||||||
export * from './queries/usePagedQuery';
|
|
||||||
export * from './queries/useLogin';
|
export * from './queries/useLogin';
|
||||||
export * from './queries/useRealtime';
|
export * from './queries/useRealtime';
|
||||||
export * from './queries/useReport';
|
export * from './queries/useReport';
|
||||||
|
|
@ -28,6 +26,7 @@ export * from './queries/useWebsiteEvents';
|
||||||
export * from './queries/useWebsiteEventsSeries';
|
export * from './queries/useWebsiteEventsSeries';
|
||||||
export * from './queries/useWebsiteMetrics';
|
export * from './queries/useWebsiteMetrics';
|
||||||
export * from './queries/useWebsiteValues';
|
export * from './queries/useWebsiteValues';
|
||||||
|
export * from './useApi';
|
||||||
export * from './useCountryNames';
|
export * from './useCountryNames';
|
||||||
export * from './useDateRange';
|
export * from './useDateRange';
|
||||||
export * from './useDocumentClick';
|
export * from './useDocumentClick';
|
||||||
|
|
@ -41,6 +40,7 @@ export * from './useLocale';
|
||||||
export * from './useMessages';
|
export * from './useMessages';
|
||||||
export * from './useModified';
|
export * from './useModified';
|
||||||
export * from './useNavigation';
|
export * from './useNavigation';
|
||||||
|
export * from './usePagedQuery';
|
||||||
export * from './useRegionNames';
|
export * from './useRegionNames';
|
||||||
export * from './useSticky';
|
export * from './useSticky';
|
||||||
export * from './useTeamUrl';
|
export * from './useTeamUrl';
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import useStore, { setConfig } from 'store/app';
|
import useStore, { setConfig } from 'store/app';
|
||||||
import { useApi } from './useApi';
|
import { useApi } from '../useApi';
|
||||||
|
|
||||||
let loading = false;
|
let loading = false;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import useApi from './useApi';
|
import { useApi } from '../useApi';
|
||||||
import { UseQueryOptions } from '@tanstack/react-query';
|
import { UseQueryOptions } from '@tanstack/react-query';
|
||||||
import { useFilterParams } from '../useFilterParams';
|
import { useFilterParams } from '../useFilterParams';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import useApi from './useApi';
|
|
||||||
import { UseQueryOptions } from '@tanstack/react-query';
|
import { UseQueryOptions } from '@tanstack/react-query';
|
||||||
|
import { useApi } from '../useApi';
|
||||||
import { useFilterParams } from '../useFilterParams';
|
import { useFilterParams } from '../useFilterParams';
|
||||||
|
|
||||||
export function useEventDataProperties(
|
export function useEventDataProperties(
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import useApi from './useApi';
|
|
||||||
import { UseQueryOptions } from '@tanstack/react-query';
|
import { UseQueryOptions } from '@tanstack/react-query';
|
||||||
|
import { useApi } from '../useApi';
|
||||||
import { useFilterParams } from '../useFilterParams';
|
import { useFilterParams } from '../useFilterParams';
|
||||||
|
|
||||||
export function useEventDataValues(
|
export function useEventDataValues(
|
||||||
|
|
@ -12,7 +12,7 @@ export function useEventDataValues(
|
||||||
const params = useFilterParams(websiteId);
|
const params = useFilterParams(websiteId);
|
||||||
|
|
||||||
return useQuery<any>({
|
return useQuery<any>({
|
||||||
queryKey: ['websites:event-data:values', { websiteId, propertyName, ...params }],
|
queryKey: ['websites:event-data:values', { websiteId, eventName, propertyName, ...params }],
|
||||||
queryFn: () =>
|
queryFn: () =>
|
||||||
get(`/websites/${websiteId}/event-data/values`, { ...params, eventName, propertyName }),
|
get(`/websites/${websiteId}/event-data/values`, { ...params, eventName, propertyName }),
|
||||||
enabled: !!(websiteId && propertyName),
|
enabled: !!(websiteId && propertyName),
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import useStore, { setUser } from 'store/app';
|
|
||||||
import useApi from './useApi';
|
|
||||||
import { UseQueryResult } from '@tanstack/react-query';
|
import { UseQueryResult } from '@tanstack/react-query';
|
||||||
|
import useStore, { setUser } from 'store/app';
|
||||||
|
import { useApi } from '../useApi';
|
||||||
|
|
||||||
const selector = (state: { user: any }) => state.user;
|
const selector = (state: { user: any }) => state.user;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { useTimezone } from 'components/hooks';
|
import { useTimezone } from 'components/hooks';
|
||||||
import { REALTIME_INTERVAL } from 'lib/constants';
|
import { REALTIME_INTERVAL } from 'lib/constants';
|
||||||
import { RealtimeData } from 'lib/types';
|
import { RealtimeData } from 'lib/types';
|
||||||
import { useApi } from './useApi';
|
import { useApi } from '../useApi';
|
||||||
|
|
||||||
export function useRealtime(websiteId: string) {
|
export function useRealtime(websiteId: string) {
|
||||||
const { get, useQuery } = useApi();
|
const { get, useQuery } = useApi();
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { produce } from 'immer';
|
import { produce } from 'immer';
|
||||||
import { useCallback, useEffect, useState } from 'react';
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
import { useApi } from './useApi';
|
import { useApi } from '../useApi';
|
||||||
import { useTimezone } from '../useTimezone';
|
import { useTimezone } from '../useTimezone';
|
||||||
import { useMessages } from '../useMessages';
|
import { useMessages } from '../useMessages';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import useApi from './useApi';
|
import useApi from '../useApi';
|
||||||
import usePagedQuery from './usePagedQuery';
|
import usePagedQuery from '../usePagedQuery';
|
||||||
import useModified from '../useModified';
|
import useModified from '../useModified';
|
||||||
|
|
||||||
export function useReports({ websiteId, teamId }: { websiteId?: string; teamId?: string }) {
|
export function useReports({ websiteId, teamId }: { websiteId?: string; teamId?: string }) {
|
||||||
|
|
|
||||||
18
src/components/hooks/queries/useRevenueValues.ts
Normal file
18
src/components/hooks/queries/useRevenueValues.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { useApi } from '../useApi';
|
||||||
|
|
||||||
|
export function useRevenueValues(websiteId: string, startDate: Date, endDate: Date) {
|
||||||
|
const { get, useQuery } = useApi();
|
||||||
|
|
||||||
|
return useQuery({
|
||||||
|
queryKey: ['revenue:values', { websiteId, startDate, endDate }],
|
||||||
|
queryFn: () =>
|
||||||
|
get(`/reports/revenue`, {
|
||||||
|
websiteId,
|
||||||
|
startDate,
|
||||||
|
endDate,
|
||||||
|
}),
|
||||||
|
enabled: !!(websiteId && startDate && endDate),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useRevenueValues;
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { useApi } from './useApi';
|
import { useApi } from '../useApi';
|
||||||
|
|
||||||
export function useSessionActivity(
|
export function useSessionActivity(
|
||||||
websiteId: string,
|
websiteId: string,
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { useApi } from './useApi';
|
import { useApi } from '../useApi';
|
||||||
|
|
||||||
export function useSessionData(websiteId: string, sessionId: string) {
|
export function useSessionData(websiteId: string, sessionId: string) {
|
||||||
const { get, useQuery } = useApi();
|
const { get, useQuery } = useApi();
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import useApi from './useApi';
|
import { useApi } from '../useApi';
|
||||||
import { UseQueryOptions } from '@tanstack/react-query';
|
import { UseQueryOptions } from '@tanstack/react-query';
|
||||||
import { useFilterParams } from '../useFilterParams';
|
import { useFilterParams } from '../useFilterParams';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import useApi from './useApi';
|
import { useApi } from '../useApi';
|
||||||
import { UseQueryOptions } from '@tanstack/react-query';
|
import { UseQueryOptions } from '@tanstack/react-query';
|
||||||
import { useFilterParams } from '../useFilterParams';
|
import { useFilterParams } from '../useFilterParams';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import useStore, { setShareToken } from 'store/app';
|
import useStore, { setShareToken } from 'store/app';
|
||||||
import useApi from './useApi';
|
import { useApi } from '../useApi';
|
||||||
|
|
||||||
const selector = (state: { shareToken: string }) => state.shareToken;
|
const selector = (state: { shareToken: string }) => state.shareToken;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import useApi from './useApi';
|
import { useApi } from '../useApi';
|
||||||
|
|
||||||
export function useTeam(teamId: string) {
|
export function useTeam(teamId: string) {
|
||||||
const { get, useQuery } = useApi();
|
const { get, useQuery } = useApi();
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import useApi from './useApi';
|
import { useApi } from '../useApi';
|
||||||
import usePagedQuery from './usePagedQuery';
|
import usePagedQuery from '../usePagedQuery';
|
||||||
import useModified from '../useModified';
|
import useModified from '../useModified';
|
||||||
|
|
||||||
export function useTeamMembers(teamId: string) {
|
export function useTeamMembers(teamId: string) {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import useApi from './useApi';
|
import { useApi } from '../useApi';
|
||||||
import usePagedQuery from './usePagedQuery';
|
import { usePagedQuery } from '../usePagedQuery';
|
||||||
import useModified from '../useModified';
|
import useModified from '../useModified';
|
||||||
|
|
||||||
export function useTeamWebsites(teamId: string) {
|
export function useTeamWebsites(teamId: string) {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import useApi from './useApi';
|
import { useApi } from '../useApi';
|
||||||
import usePagedQuery from './usePagedQuery';
|
import { usePagedQuery } from '../usePagedQuery';
|
||||||
import useModified from '../useModified';
|
import useModified from '../useModified';
|
||||||
|
|
||||||
export function useTeams(userId: string) {
|
export function useTeams(userId: string) {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import useApi from './useApi';
|
import { useApi } from '../useApi';
|
||||||
|
|
||||||
export function useUser(userId: string, options?: { [key: string]: any }) {
|
export function useUser(userId: string, options?: { [key: string]: any }) {
|
||||||
const { get, useQuery } = useApi();
|
const { get, useQuery } = useApi();
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import useApi from './useApi';
|
import { useApi } from '../useApi';
|
||||||
import usePagedQuery from './usePagedQuery';
|
import { usePagedQuery } from '../usePagedQuery';
|
||||||
import useModified from '../useModified';
|
import useModified from '../useModified';
|
||||||
|
|
||||||
export function useUsers() {
|
export function useUsers() {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import useApi from './useApi';
|
import { useApi } from '../useApi';
|
||||||
|
|
||||||
export function useWebsite(websiteId: string, options?: { [key: string]: any }) {
|
export function useWebsite(websiteId: string, options?: { [key: string]: any }) {
|
||||||
const { get, useQuery } = useApi();
|
const { get, useQuery } = useApi();
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue