mirror of
https://github.com/umami-software/umami.git
synced 2026-02-11 08:07:12 +01:00
Merge branch 'dev' into analytics
This commit is contained in:
commit
4853437afa
525 changed files with 5436 additions and 4786 deletions
|
|
@ -30,6 +30,7 @@
|
||||||
"rules": {
|
"rules": {
|
||||||
"no-console": "error",
|
"no-console": "error",
|
||||||
"react/display-name": "off",
|
"react/display-name": "off",
|
||||||
|
"react-hooks/exhaustive-deps": "off",
|
||||||
"react/react-in-jsx-scope": "off",
|
"react/react-in-jsx-scope": "off",
|
||||||
"react/prop-types": "off",
|
"react/prop-types": "off",
|
||||||
"import/no-anonymous-default-export": "off",
|
"import/no-anonymous-default-export": "off",
|
||||||
|
|
|
||||||
6
.github/ISSUE_TEMPLATE/1.bug_report.yml
vendored
6
.github/ISSUE_TEMPLATE/1.bug_report.yml
vendored
|
|
@ -1,4 +1,4 @@
|
||||||
name: "🐛 Bug Report"
|
name: '🐛 Bug Report'
|
||||||
description: Create a bug report for Umami.
|
description: Create a bug report for Umami.
|
||||||
body:
|
body:
|
||||||
- type: textarea
|
- type: textarea
|
||||||
|
|
@ -22,6 +22,10 @@ body:
|
||||||
label: Relevant log output
|
label: Relevant log output
|
||||||
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
|
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
|
||||||
render: shell
|
render: shell
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: Which Umami version are you using? (if relevant)
|
||||||
|
description: 'For example: Chrome, Edge, Firefox, etc'
|
||||||
- type: input
|
- type: input
|
||||||
attributes:
|
attributes:
|
||||||
label: Which browser are you using? (if relevant)
|
label: Which browser are you using? (if relevant)
|
||||||
|
|
|
||||||
22
.github/workflows/cd.yml
vendored
22
.github/workflows/cd.yml
vendored
|
|
@ -2,6 +2,21 @@ name: Create docker images
|
||||||
|
|
||||||
on: [create]
|
on: [create]
|
||||||
|
|
||||||
|
env:
|
||||||
|
# set Docker OCI Labels
|
||||||
|
DOCKER_LABELS: >
|
||||||
|
org.opencontainers.image.title=${{github.event.repository.name}},
|
||||||
|
org.opencontainers.image.description="Umami is a simple, fast, privacy-focused alternative to Google Analytics",
|
||||||
|
org.opencontainers.image.vendor=${{github.repository_owner}},
|
||||||
|
org.opencontainers.image.licenses="MIT",
|
||||||
|
org.opencontainers.image.version=${{github.ref_name}},
|
||||||
|
org.opencontainers.image.created=${{ env.NOW }},
|
||||||
|
org.opencontainers.image.source=${{github.server_url}}/${{github.repository}},
|
||||||
|
org.opencontainers.image.revision=${{github.sha}},
|
||||||
|
org.opencontainers.image.url="https://umami.is/",
|
||||||
|
org.opencontainers.image.documentation="https://umami.is/docs",
|
||||||
|
org.opencontainers.image.base.name="docker.io/library/node:18-alpine"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: Build, push, and deploy
|
name: Build, push, and deploy
|
||||||
|
|
@ -16,13 +31,16 @@ jobs:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Set env
|
- name: Set env
|
||||||
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
|
run: |
|
||||||
|
echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
|
||||||
|
echo "NOW=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $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: ${{ matrix.db-type }}-${{ env.RELEASE_VERSION }}, ${{ matrix.db-type }}-latest
|
||||||
|
labels: $DOCKER_LABELS
|
||||||
buildArgs: DATABASE_TYPE=${{ matrix.db-type }}
|
buildArgs: DATABASE_TYPE=${{ matrix.db-type }}
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
multiPlatform: true
|
multiPlatform: true
|
||||||
|
|
@ -30,11 +48,13 @@ 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: ${{ matrix.db-type }}-${{ env.RELEASE_VERSION }}, ${{ matrix.db-type }}-latest
|
||||||
|
labels: $DOCKER_LABELS
|
||||||
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 }}
|
||||||
|
|
|
||||||
8
.github/workflows/ci.yml
vendored
8
.github/workflows/ci.yml
vendored
|
|
@ -16,15 +16,15 @@ jobs:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- node-version: 18.x
|
- node-version: 18.17
|
||||||
db-type: postgresql
|
db-type: postgresql
|
||||||
- node-version: 18.x
|
- node-version: 18.17
|
||||||
db-type: mysql
|
db-type: mysql
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Use Node.js ${{ matrix.node-version }}
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
cache: 'npm'
|
cache: 'npm'
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ mysql://username:mypassword@localhost:3306/mydb
|
||||||
yarn build
|
yarn build
|
||||||
```
|
```
|
||||||
|
|
||||||
The build step will also create tables in your database if you ae installing for the first time. It will also create a login user with username **admin** and password **umami**.
|
The build step will also create tables in your database if you are installing for the first time. It will also create a login user with username **admin** and password **umami**.
|
||||||
|
|
||||||
### Start the application
|
### Start the application
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
SET allow_experimental_object_type = 1;
|
|
||||||
|
|
||||||
-- Create Event
|
-- Create Event
|
||||||
CREATE TABLE umami.website_event
|
CREATE TABLE umami.website_event
|
||||||
(
|
(
|
||||||
|
|
@ -34,82 +32,6 @@ CREATE TABLE umami.website_event
|
||||||
ORDER BY (website_id, session_id, created_at)
|
ORDER BY (website_id, session_id, created_at)
|
||||||
SETTINGS index_granularity = 8192;
|
SETTINGS index_granularity = 8192;
|
||||||
|
|
||||||
CREATE TABLE umami.website_event_queue (
|
|
||||||
website_id UUID,
|
|
||||||
session_id UUID,
|
|
||||||
event_id UUID,
|
|
||||||
--sessions
|
|
||||||
hostname LowCardinality(String),
|
|
||||||
browser LowCardinality(String),
|
|
||||||
os LowCardinality(String),
|
|
||||||
device LowCardinality(String),
|
|
||||||
screen LowCardinality(String),
|
|
||||||
language LowCardinality(String),
|
|
||||||
country LowCardinality(String),
|
|
||||||
subdivision1 LowCardinality(String),
|
|
||||||
subdivision2 LowCardinality(String),
|
|
||||||
city String,
|
|
||||||
--pageviews
|
|
||||||
url_path String,
|
|
||||||
url_query String,
|
|
||||||
referrer_path String,
|
|
||||||
referrer_query String,
|
|
||||||
referrer_domain String,
|
|
||||||
page_title String,
|
|
||||||
--events
|
|
||||||
event_type UInt32,
|
|
||||||
event_name String,
|
|
||||||
created_at DateTime('UTC'),
|
|
||||||
--virtual columns
|
|
||||||
_error String,
|
|
||||||
_raw_message String
|
|
||||||
)
|
|
||||||
ENGINE = Kafka
|
|
||||||
SETTINGS kafka_broker_list = 'domain:9092,domain:9093,domain:9094', -- input broker list
|
|
||||||
kafka_topic_list = 'event',
|
|
||||||
kafka_group_name = 'event_consumer_group',
|
|
||||||
kafka_format = 'JSONEachRow',
|
|
||||||
kafka_max_block_size = 1048576,
|
|
||||||
kafka_handle_error_mode = 'stream';
|
|
||||||
|
|
||||||
CREATE MATERIALIZED VIEW umami.website_event_queue_mv TO umami.website_event AS
|
|
||||||
SELECT website_id,
|
|
||||||
session_id,
|
|
||||||
event_id,
|
|
||||||
hostname,
|
|
||||||
browser,
|
|
||||||
os,
|
|
||||||
device,
|
|
||||||
screen,
|
|
||||||
language,
|
|
||||||
country,
|
|
||||||
subdivision1,
|
|
||||||
subdivision2,
|
|
||||||
city,
|
|
||||||
url_path,
|
|
||||||
url_query,
|
|
||||||
referrer_path,
|
|
||||||
referrer_query,
|
|
||||||
referrer_domain,
|
|
||||||
page_title,
|
|
||||||
event_type,
|
|
||||||
event_name,
|
|
||||||
created_at
|
|
||||||
FROM umami.website_event_queue;
|
|
||||||
|
|
||||||
CREATE MATERIALIZED VIEW umami.website_event_errors_mv
|
|
||||||
(
|
|
||||||
error String,
|
|
||||||
raw String
|
|
||||||
)
|
|
||||||
ENGINE = MergeTree
|
|
||||||
ORDER BY (error, raw)
|
|
||||||
SETTINGS index_granularity = 8192 AS
|
|
||||||
SELECT _error AS error,
|
|
||||||
_raw_message AS raw
|
|
||||||
FROM umami.website_event_queue
|
|
||||||
WHERE length(_error) > 0;
|
|
||||||
|
|
||||||
CREATE TABLE umami.event_data
|
CREATE TABLE umami.event_data
|
||||||
(
|
(
|
||||||
website_id UUID,
|
website_id UUID,
|
||||||
|
|
@ -128,54 +50,3 @@ CREATE TABLE umami.event_data
|
||||||
engine = MergeTree
|
engine = MergeTree
|
||||||
ORDER BY (website_id, event_id, event_key, created_at)
|
ORDER BY (website_id, event_id, event_key, created_at)
|
||||||
SETTINGS index_granularity = 8192;
|
SETTINGS index_granularity = 8192;
|
||||||
|
|
||||||
CREATE TABLE umami.event_data_queue (
|
|
||||||
website_id UUID,
|
|
||||||
session_id UUID,
|
|
||||||
event_id UUID,
|
|
||||||
url_path String,
|
|
||||||
event_name String,
|
|
||||||
event_key String,
|
|
||||||
string_value Nullable(String),
|
|
||||||
number_value Nullable(Decimal64(4)), --922337203685477.5625
|
|
||||||
date_value Nullable(DateTime('UTC')),
|
|
||||||
data_type UInt32,
|
|
||||||
created_at DateTime('UTC'),
|
|
||||||
--virtual columns
|
|
||||||
_error String,
|
|
||||||
_raw_message String
|
|
||||||
)
|
|
||||||
ENGINE = Kafka
|
|
||||||
SETTINGS kafka_broker_list = 'domain:9092,domain:9093,domain:9094', -- input broker list
|
|
||||||
kafka_topic_list = 'event_data',
|
|
||||||
kafka_group_name = 'event_data_consumer_group',
|
|
||||||
kafka_format = 'JSONEachRow',
|
|
||||||
kafka_max_block_size = 1048576,
|
|
||||||
kafka_handle_error_mode = 'stream';
|
|
||||||
|
|
||||||
CREATE MATERIALIZED VIEW umami.event_data_queue_mv TO umami.event_data AS
|
|
||||||
SELECT website_id,
|
|
||||||
session_id,
|
|
||||||
event_id,
|
|
||||||
url_path,
|
|
||||||
event_name,
|
|
||||||
event_key,
|
|
||||||
string_value,
|
|
||||||
number_value,
|
|
||||||
date_value,
|
|
||||||
data_type,
|
|
||||||
created_at
|
|
||||||
FROM umami.event_data_queue;
|
|
||||||
|
|
||||||
CREATE MATERIALIZED VIEW umami.event_data_errors_mv
|
|
||||||
(
|
|
||||||
error String,
|
|
||||||
raw String
|
|
||||||
)
|
|
||||||
ENGINE = MergeTree
|
|
||||||
ORDER BY (error, raw)
|
|
||||||
SETTINGS index_granularity = 8192 AS
|
|
||||||
SELECT _error AS error,
|
|
||||||
_raw_message AS raw
|
|
||||||
FROM umami.event_data_queue
|
|
||||||
WHERE length(_error) > 0;
|
|
||||||
29
db/mysql/migrations/04_team_redesign/migration.sql
Normal file
29
db/mysql/migrations/04_team_redesign/migration.sql
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- You are about to drop the `team_website` table. If the table is not empty, all the data it contains will be lost.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `team` ADD COLUMN `deleted_at` TIMESTAMP(0) NULL,
|
||||||
|
ADD COLUMN `logo_url` VARCHAR(2183) NULL;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `user` ADD COLUMN `display_name` VARCHAR(255) NULL,
|
||||||
|
ADD COLUMN `logo_url` VARCHAR(2183) NULL;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `website` ADD COLUMN `created_by` VARCHAR(36) NULL,
|
||||||
|
ADD COLUMN `team_id` VARCHAR(36) NULL;
|
||||||
|
|
||||||
|
-- MigrateData
|
||||||
|
UPDATE `website` SET created_by = user_id WHERE team_id IS NULL;
|
||||||
|
|
||||||
|
-- DropTable
|
||||||
|
DROP TABLE `team_website`;
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX `website_team_id_idx` ON `website`(`team_id`);
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX `website_created_by_idx` ON `website`(`created_by`);
|
||||||
|
|
@ -9,15 +9,18 @@ datasource db {
|
||||||
}
|
}
|
||||||
|
|
||||||
model User {
|
model User {
|
||||||
id String @id @unique @map("user_id") @db.VarChar(36)
|
id String @id @unique @map("user_id") @db.VarChar(36)
|
||||||
username String @unique @db.VarChar(255)
|
username String @unique @db.VarChar(255)
|
||||||
password String @db.VarChar(60)
|
password String @db.VarChar(60)
|
||||||
role String @map("role") @db.VarChar(50)
|
role String @map("role") @db.VarChar(50)
|
||||||
createdAt DateTime? @default(now()) @map("created_at") @db.Timestamp(0)
|
logoUrl String? @map("logo_url") @db.VarChar(2183)
|
||||||
updatedAt DateTime? @updatedAt @map("updated_at") @db.Timestamp(0)
|
displayName String? @map("display_name") @db.VarChar(255)
|
||||||
deletedAt DateTime? @map("deleted_at") @db.Timestamp(0)
|
createdAt DateTime? @default(now()) @map("created_at") @db.Timestamp(0)
|
||||||
|
updatedAt DateTime? @updatedAt @map("updated_at") @db.Timestamp(0)
|
||||||
|
deletedAt DateTime? @map("deleted_at") @db.Timestamp(0)
|
||||||
|
|
||||||
website Website[]
|
websiteUser Website[] @relation("user")
|
||||||
|
websiteCreateUser Website[] @relation("createUser")
|
||||||
teamUser TeamUser[]
|
teamUser TeamUser[]
|
||||||
report Report[]
|
report Report[]
|
||||||
|
|
||||||
|
|
@ -64,19 +67,24 @@ model Website {
|
||||||
shareId String? @unique @map("share_id") @db.VarChar(50)
|
shareId String? @unique @map("share_id") @db.VarChar(50)
|
||||||
resetAt DateTime? @map("reset_at") @db.Timestamp(0)
|
resetAt DateTime? @map("reset_at") @db.Timestamp(0)
|
||||||
userId String? @map("user_id") @db.VarChar(36)
|
userId String? @map("user_id") @db.VarChar(36)
|
||||||
|
teamId String? @map("team_id") @db.VarChar(36)
|
||||||
|
createdBy String? @map("created_by") @db.VarChar(36)
|
||||||
createdAt DateTime? @default(now()) @map("created_at") @db.Timestamp(0)
|
createdAt DateTime? @default(now()) @map("created_at") @db.Timestamp(0)
|
||||||
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)
|
||||||
|
|
||||||
user User? @relation(fields: [userId], references: [id])
|
user User? @relation("user", fields: [userId], references: [id])
|
||||||
teamWebsite TeamWebsite[]
|
createUser User? @relation("createUser", fields: [createdBy], references: [id])
|
||||||
|
team Team? @relation(fields: [teamId], references: [id])
|
||||||
eventData EventData[]
|
eventData EventData[]
|
||||||
report Report[]
|
report Report[]
|
||||||
sessionData SessionData[]
|
sessionData SessionData[]
|
||||||
|
|
||||||
@@index([userId])
|
@@index([userId])
|
||||||
|
@@index([teamId])
|
||||||
@@index([createdAt])
|
@@index([createdAt])
|
||||||
@@index([shareId])
|
@@index([shareId])
|
||||||
|
@@index([createdBy])
|
||||||
@@map("website")
|
@@map("website")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -157,11 +165,13 @@ model Team {
|
||||||
id String @id() @unique() @map("team_id") @db.VarChar(36)
|
id String @id() @unique() @map("team_id") @db.VarChar(36)
|
||||||
name String @db.VarChar(50)
|
name String @db.VarChar(50)
|
||||||
accessCode String? @unique @map("access_code") @db.VarChar(50)
|
accessCode String? @unique @map("access_code") @db.VarChar(50)
|
||||||
|
logoUrl String? @map("logo_url") @db.VarChar(2183)
|
||||||
createdAt DateTime? @default(now()) @map("created_at") @db.Timestamp(0)
|
createdAt DateTime? @default(now()) @map("created_at") @db.Timestamp(0)
|
||||||
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)
|
||||||
|
|
||||||
|
website Website[]
|
||||||
teamUser TeamUser[]
|
teamUser TeamUser[]
|
||||||
teamWebsite TeamWebsite[]
|
|
||||||
|
|
||||||
@@index([accessCode])
|
@@index([accessCode])
|
||||||
@@map("team")
|
@@map("team")
|
||||||
|
|
@ -183,20 +193,6 @@ model TeamUser {
|
||||||
@@map("team_user")
|
@@map("team_user")
|
||||||
}
|
}
|
||||||
|
|
||||||
model TeamWebsite {
|
|
||||||
id String @id() @unique() @map("team_website_id") @db.VarChar(36)
|
|
||||||
teamId String @map("team_id") @db.VarChar(36)
|
|
||||||
websiteId String @map("website_id") @db.VarChar(36)
|
|
||||||
createdAt DateTime? @default(now()) @map("created_at") @db.Timestamp(0)
|
|
||||||
|
|
||||||
team Team @relation(fields: [teamId], references: [id])
|
|
||||||
website Website @relation(fields: [websiteId], references: [id])
|
|
||||||
|
|
||||||
@@index([teamId])
|
|
||||||
@@index([websiteId])
|
|
||||||
@@map("team_website")
|
|
||||||
}
|
|
||||||
|
|
||||||
model Report {
|
model Report {
|
||||||
id String @id() @unique() @map("report_id") @db.VarChar(36)
|
id String @id() @unique() @map("report_id") @db.VarChar(36)
|
||||||
userId String @map("user_id") @db.VarChar(36)
|
userId String @map("user_id") @db.VarChar(36)
|
||||||
|
|
|
||||||
29
db/postgresql/migrations/04_team_redesign/migration.sql
Normal file
29
db/postgresql/migrations/04_team_redesign/migration.sql
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- You are about to drop the `team_website` table. If the table is not empty, all the data it contains will be lost.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "team" ADD COLUMN "deleted_at" TIMESTAMPTZ(6),
|
||||||
|
ADD COLUMN "logo_url" VARCHAR(2183);
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "user" ADD COLUMN "display_name" VARCHAR(255),
|
||||||
|
ADD COLUMN "logo_url" VARCHAR(2183);
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "website" ADD COLUMN "created_by" UUID,
|
||||||
|
ADD COLUMN "team_id" UUID;
|
||||||
|
|
||||||
|
-- MigrateData
|
||||||
|
UPDATE "website" SET created_by = user_id WHERE team_id IS NULL;
|
||||||
|
|
||||||
|
-- DropTable
|
||||||
|
DROP TABLE "team_website";
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "website_team_id_idx" ON "website"("team_id");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "website_created_by_idx" ON "website"("created_by");
|
||||||
|
|
@ -9,17 +9,20 @@ datasource db {
|
||||||
}
|
}
|
||||||
|
|
||||||
model User {
|
model User {
|
||||||
id String @id @unique @map("user_id") @db.Uuid
|
id String @id @unique @map("user_id") @db.Uuid
|
||||||
username String @unique @db.VarChar(255)
|
username String @unique @db.VarChar(255)
|
||||||
password String @db.VarChar(60)
|
password String @db.VarChar(60)
|
||||||
role String @map("role") @db.VarChar(50)
|
role String @map("role") @db.VarChar(50)
|
||||||
createdAt DateTime? @default(now()) @map("created_at") @db.Timestamptz(6)
|
logoUrl String? @map("logo_url") @db.VarChar(2183)
|
||||||
updatedAt DateTime? @updatedAt @map("updated_at") @db.Timestamptz(6)
|
displayName String? @map("display_name") @db.VarChar(255)
|
||||||
deletedAt DateTime? @map("deleted_at") @db.Timestamptz(6)
|
createdAt DateTime? @default(now()) @map("created_at") @db.Timestamptz(6)
|
||||||
|
updatedAt DateTime? @updatedAt @map("updated_at") @db.Timestamptz(6)
|
||||||
|
deletedAt DateTime? @map("deleted_at") @db.Timestamptz(6)
|
||||||
|
|
||||||
website Website[]
|
websiteUser Website[] @relation("user")
|
||||||
teamUser TeamUser[]
|
websiteCreateUser Website[] @relation("createUser")
|
||||||
report Report[]
|
teamUser TeamUser[]
|
||||||
|
report Report[]
|
||||||
|
|
||||||
@@map("user")
|
@@map("user")
|
||||||
}
|
}
|
||||||
|
|
@ -64,19 +67,24 @@ model Website {
|
||||||
shareId String? @unique @map("share_id") @db.VarChar(50)
|
shareId String? @unique @map("share_id") @db.VarChar(50)
|
||||||
resetAt DateTime? @map("reset_at") @db.Timestamptz(6)
|
resetAt DateTime? @map("reset_at") @db.Timestamptz(6)
|
||||||
userId String? @map("user_id") @db.Uuid
|
userId String? @map("user_id") @db.Uuid
|
||||||
|
teamId String? @map("team_id") @db.Uuid
|
||||||
|
createdBy String? @map("created_by") @db.Uuid
|
||||||
createdAt DateTime? @default(now()) @map("created_at") @db.Timestamptz(6)
|
createdAt DateTime? @default(now()) @map("created_at") @db.Timestamptz(6)
|
||||||
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)
|
||||||
|
|
||||||
user User? @relation(fields: [userId], references: [id])
|
user User? @relation("user", fields: [userId], references: [id])
|
||||||
teamWebsite TeamWebsite[]
|
createUser User? @relation("createUser", fields: [createdBy], references: [id])
|
||||||
|
team Team? @relation(fields: [teamId], references: [id])
|
||||||
eventData EventData[]
|
eventData EventData[]
|
||||||
report Report[]
|
report Report[]
|
||||||
sessionData SessionData[]
|
sessionData SessionData[]
|
||||||
|
|
||||||
@@index([userId])
|
@@index([userId])
|
||||||
|
@@index([teamId])
|
||||||
@@index([createdAt])
|
@@index([createdAt])
|
||||||
@@index([shareId])
|
@@index([shareId])
|
||||||
|
@@index([createdBy])
|
||||||
@@map("website")
|
@@map("website")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -157,11 +165,13 @@ model Team {
|
||||||
id String @id() @unique() @map("team_id") @db.Uuid
|
id String @id() @unique() @map("team_id") @db.Uuid
|
||||||
name String @db.VarChar(50)
|
name String @db.VarChar(50)
|
||||||
accessCode String? @unique @map("access_code") @db.VarChar(50)
|
accessCode String? @unique @map("access_code") @db.VarChar(50)
|
||||||
|
logoUrl String? @map("logo_url") @db.VarChar(2183)
|
||||||
createdAt DateTime? @default(now()) @map("created_at") @db.Timestamptz(6)
|
createdAt DateTime? @default(now()) @map("created_at") @db.Timestamptz(6)
|
||||||
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)
|
||||||
|
|
||||||
teamUser TeamUser[]
|
website Website[]
|
||||||
teamWebsite TeamWebsite[]
|
teamUser TeamUser[]
|
||||||
|
|
||||||
@@index([accessCode])
|
@@index([accessCode])
|
||||||
@@map("team")
|
@@map("team")
|
||||||
|
|
@ -183,20 +193,6 @@ model TeamUser {
|
||||||
@@map("team_user")
|
@@map("team_user")
|
||||||
}
|
}
|
||||||
|
|
||||||
model TeamWebsite {
|
|
||||||
id String @id() @unique() @map("team_website_id") @db.Uuid
|
|
||||||
teamId String @map("team_id") @db.Uuid
|
|
||||||
websiteId String @map("website_id") @db.Uuid
|
|
||||||
createdAt DateTime? @default(now()) @map("created_at") @db.Timestamptz(6)
|
|
||||||
|
|
||||||
team Team @relation(fields: [teamId], references: [id])
|
|
||||||
website Website @relation(fields: [websiteId], references: [id])
|
|
||||||
|
|
||||||
@@index([teamId])
|
|
||||||
@@index([websiteId])
|
|
||||||
@@map("team_website")
|
|
||||||
}
|
|
||||||
|
|
||||||
model Report {
|
model Report {
|
||||||
id String @id() @unique() @map("report_id") @db.Uuid
|
id String @id() @unique() @map("report_id") @db.Uuid
|
||||||
userId String @map("user_id") @db.Uuid
|
userId String @map("user_id") @db.Uuid
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@
|
||||||
"metrics.referrers",
|
"metrics.referrers",
|
||||||
"message.powered-by"
|
"message.powered-by"
|
||||||
],
|
],
|
||||||
|
"mn-MN": ["label.max", "label.min", "label.os", "label.query", "label.url", "label.urls"],
|
||||||
"nb-NO": ["label.administrator", "label.dashboard"],
|
"nb-NO": ["label.administrator", "label.dashboard"],
|
||||||
"nl-NL": [
|
"nl-NL": [
|
||||||
"label.analytics",
|
"label.analytics",
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,25 @@ require('dotenv').config();
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const pkg = require('./package.json');
|
const pkg = require('./package.json');
|
||||||
|
|
||||||
|
const basePath = process.env.BASE_PATH || '';
|
||||||
|
const forceSSL = process.env.FORCE_SSL || '';
|
||||||
|
const collectApiEndpoint = process.env.COLLECT_API_ENDPOINT || '';
|
||||||
|
const defaultLocale = process.env.DEFAULT_LOCALE || '';
|
||||||
|
const trackerScriptName = process.env.TRACKER_SCRIPT_NAME || '';
|
||||||
|
const cloudMode = process.env.CLOUD_MODE || '';
|
||||||
|
const cloudUrl = process.env.CLOUD_URL || '';
|
||||||
|
const frameAncestors = process.env.ALLOWED_FRAME_URLS || '';
|
||||||
|
const disableLogin = process.env.DISABLE_LOGIN || '';
|
||||||
|
const disableUI = process.env.DISABLE_UI || '';
|
||||||
|
const hostURL = process.env.HOST_URL || '';
|
||||||
|
|
||||||
const contentSecurityPolicy = [
|
const contentSecurityPolicy = [
|
||||||
`default-src 'self'`,
|
`default-src 'self'`,
|
||||||
`img-src *`,
|
`img-src *`,
|
||||||
`script-src 'self' 'unsafe-eval' 'unsafe-inline'`,
|
`script-src 'self' 'unsafe-eval' 'unsafe-inline'`,
|
||||||
`style-src 'self' 'unsafe-inline'`,
|
`style-src 'self' 'unsafe-inline'`,
|
||||||
`connect-src 'self' api.umami.is cloud.umami.is`,
|
`connect-src 'self' api.umami.is cloud.umami.is`,
|
||||||
`frame-ancestors 'self' ${process.env.ALLOWED_FRAME_URLS || ''}`,
|
`frame-ancestors 'self' ${frameAncestors}`,
|
||||||
];
|
];
|
||||||
|
|
||||||
const headers = [
|
const headers = [
|
||||||
|
|
@ -26,7 +38,7 @@ const headers = [
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
if (process.env.FORCE_SSL) {
|
if (forceSSL) {
|
||||||
headers.push({
|
headers.push({
|
||||||
key: 'Strict-Transport-Security',
|
key: 'Strict-Transport-Security',
|
||||||
value: 'max-age=63072000; includeSubDomains; preload',
|
value: 'max-age=63072000; includeSubDomains; preload',
|
||||||
|
|
@ -35,15 +47,15 @@ if (process.env.FORCE_SSL) {
|
||||||
|
|
||||||
const rewrites = [];
|
const rewrites = [];
|
||||||
|
|
||||||
if (process.env.COLLECT_API_ENDPOINT) {
|
if (collectApiEndpoint) {
|
||||||
rewrites.push({
|
rewrites.push({
|
||||||
source: process.env.COLLECT_API_ENDPOINT,
|
source: collectApiEndpoint,
|
||||||
destination: '/api/send',
|
destination: '/api/send',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (process.env.TRACKER_SCRIPT_NAME) {
|
if (trackerScriptName) {
|
||||||
const names = process.env.TRACKER_SCRIPT_NAME?.split(',').map(name => name.trim());
|
const names = trackerScriptName?.split(',').map(name => name.trim());
|
||||||
|
|
||||||
if (names) {
|
if (names) {
|
||||||
names.forEach(name => {
|
names.forEach(name => {
|
||||||
|
|
@ -58,36 +70,56 @@ if (process.env.TRACKER_SCRIPT_NAME) {
|
||||||
const redirects = [
|
const redirects = [
|
||||||
{
|
{
|
||||||
source: '/settings',
|
source: '/settings',
|
||||||
destination: process.env.CLOUD_MODE
|
destination: '/settings/websites',
|
||||||
? `${process.env.CLOUD_URL}/settings/websites`
|
permanent: true,
|
||||||
: '/settings/websites',
|
},
|
||||||
|
{
|
||||||
|
source: '/teams/:id',
|
||||||
|
destination: '/teams/:id/dashboard',
|
||||||
|
permanent: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
source: '/teams/:id/settings',
|
||||||
|
destination: '/teams/:id/settings/team',
|
||||||
permanent: true,
|
permanent: true,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
if (process.env.CLOUD_MODE && process.env.CLOUD_URL && process.env.DISABLE_LOGIN) {
|
if (cloudMode && cloudUrl) {
|
||||||
redirects.push({
|
redirects.push({
|
||||||
source: '/login',
|
source: '/settings/:path*',
|
||||||
destination: process.env.CLOUD_URL,
|
destination: `${cloudUrl}/settings/:path*`,
|
||||||
permanent: false,
|
permanent: false,
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
const basePath = process.env.BASE_PATH;
|
redirects.push({
|
||||||
|
source: '/teams/:id/settings/:path*',
|
||||||
|
destination: `${cloudUrl}/teams/:id/settings/:path*`,
|
||||||
|
permanent: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (disableLogin) {
|
||||||
|
redirects.push({
|
||||||
|
source: '/login',
|
||||||
|
destination: cloudUrl,
|
||||||
|
permanent: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** @type {import('next').NextConfig} */
|
/** @type {import('next').NextConfig} */
|
||||||
const config = {
|
const config = {
|
||||||
reactStrictMode: false,
|
reactStrictMode: false,
|
||||||
env: {
|
env: {
|
||||||
basePath: basePath || '',
|
basePath,
|
||||||
cloudMode: process.env.CLOUD_MODE || '',
|
cloudMode,
|
||||||
cloudUrl: process.env.CLOUD_URL || '',
|
cloudUrl,
|
||||||
configUrl: '/config',
|
configUrl: '/config',
|
||||||
currentVersion: pkg.version,
|
currentVersion: pkg.version,
|
||||||
defaultLocale: process.env.DEFAULT_LOCALE || '',
|
defaultLocale,
|
||||||
disableLogin: process.env.DISABLE_LOGIN || '',
|
disableLogin,
|
||||||
disableUI: process.env.DISABLE_UI || '',
|
disableUI,
|
||||||
hostUrl: process.env.HOST_URL || '',
|
hostURL,
|
||||||
},
|
},
|
||||||
basePath,
|
basePath,
|
||||||
output: 'standalone',
|
output: 'standalone',
|
||||||
|
|
|
||||||
10
package.json
10
package.json
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "umami",
|
"name": "umami",
|
||||||
"version": "2.9.0",
|
"version": "2.10.0",
|
||||||
"description": "A simple, fast, privacy-focused alternative to Google Analytics.",
|
"description": "A simple, fast, privacy-focused alternative to Google Analytics.",
|
||||||
"author": "Mike Cao <mike@mikecao.com>",
|
"author": "Mike Cao <mike@mikecao.com>",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|
@ -67,7 +67,7 @@
|
||||||
"@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.12.2",
|
"@tanstack/react-query": "^5.12.2",
|
||||||
"@umami/prisma-client": "^0.8.0",
|
"@umami/prisma-client": "^0.14.0",
|
||||||
"@umami/redis-client": "^0.18.0",
|
"@umami/redis-client": "^0.18.0",
|
||||||
"chalk": "^4.1.1",
|
"chalk": "^4.1.1",
|
||||||
"chart.js": "^4.2.1",
|
"chart.js": "^4.2.1",
|
||||||
|
|
@ -92,6 +92,7 @@
|
||||||
"isbot": "^3.4.5",
|
"isbot": "^3.4.5",
|
||||||
"kafkajs": "^2.1.0",
|
"kafkajs": "^2.1.0",
|
||||||
"maxmind": "^4.3.6",
|
"maxmind": "^4.3.6",
|
||||||
|
"md5": "^2.3.0",
|
||||||
"moment-timezone": "^0.5.35",
|
"moment-timezone": "^0.5.35",
|
||||||
"next": "14.0.4",
|
"next": "14.0.4",
|
||||||
"next-basics": "^0.39.0",
|
"next-basics": "^0.39.0",
|
||||||
|
|
@ -99,7 +100,7 @@
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"prisma": "5.7.0",
|
"prisma": "5.7.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-basics": "^0.114.0",
|
"react-basics": "^0.122.0",
|
||||||
"react-beautiful-dnd": "^13.1.0",
|
"react-beautiful-dnd": "^13.1.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-error-boundary": "^4.0.4",
|
"react-error-boundary": "^4.0.4",
|
||||||
|
|
@ -117,9 +118,8 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@formatjs/cli": "^4.2.29",
|
"@formatjs/cli": "^4.2.29",
|
||||||
"@netlify/plugin-nextjs": "^4.27.3",
|
"@netlify/plugin-nextjs": "^4.41.3",
|
||||||
"@rollup/plugin-alias": "^5.0.0",
|
"@rollup/plugin-alias": "^5.0.0",
|
||||||
"@rollup/plugin-buble": "^1.0.2",
|
|
||||||
"@rollup/plugin-commonjs": "^25.0.4",
|
"@rollup/plugin-commonjs": "^25.0.4",
|
||||||
"@rollup/plugin-json": "^6.0.0",
|
"@rollup/plugin-json": "^6.0.0",
|
||||||
"@rollup/plugin-node-resolve": "^15.2.0",
|
"@rollup/plugin-node-resolve": "^15.2.0",
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
"value": "Add website"
|
"value": "Add website"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.admin": [
|
"label.administrator": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Administrator"
|
"value": "Administrator"
|
||||||
|
|
|
||||||
|
|
@ -20,13 +20,13 @@
|
||||||
"label.add": [
|
"label.add": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Add"
|
"value": "أضِف"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.add-description": [
|
"label.add-description": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Add description"
|
"value": "أضِف وصف"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.add-website": [
|
"label.add-website": [
|
||||||
|
|
@ -44,7 +44,7 @@
|
||||||
"label.after": [
|
"label.after": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "After"
|
"value": "يعد"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.all": [
|
"label.all": [
|
||||||
|
|
@ -68,7 +68,7 @@
|
||||||
"label.average": [
|
"label.average": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Average"
|
"value": "المتوسط"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.average-visit-time": [
|
"label.average-visit-time": [
|
||||||
|
|
@ -86,7 +86,7 @@
|
||||||
"label.before": [
|
"label.before": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Before"
|
"value": "قبل"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.bounce-rate": [
|
"label.bounce-rate": [
|
||||||
|
|
@ -98,13 +98,13 @@
|
||||||
"label.breakdown": [
|
"label.breakdown": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Breakdown"
|
"value": "التصنيف"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.browser": [
|
"label.browser": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Browser"
|
"value": "المتصفح"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.browsers": [
|
"label.browsers": [
|
||||||
|
|
@ -116,7 +116,7 @@
|
||||||
"label.cancel": [
|
"label.cancel": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "إلغاء"
|
"value": "ألغِ"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.change-password": [
|
"label.change-password": [
|
||||||
|
|
@ -134,7 +134,7 @@
|
||||||
"label.city": [
|
"label.city": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "City"
|
"value": "المدينة"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.clear-all": [
|
"label.clear-all": [
|
||||||
|
|
@ -158,13 +158,13 @@
|
||||||
"label.contains": [
|
"label.contains": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Contains"
|
"value": "يحتوي"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.continue": [
|
"label.continue": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "متابعة"
|
"value": "تابع"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.countries": [
|
"label.countries": [
|
||||||
|
|
@ -176,37 +176,37 @@
|
||||||
"label.country": [
|
"label.country": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Country"
|
"value": "الدولة"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.create": [
|
"label.create": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Create"
|
"value": "أنشِئ"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.create-report": [
|
"label.create-report": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Create report"
|
"value": "أنشِئ تقرير"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.create-team": [
|
"label.create-team": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "انشاء مجموعة"
|
"value": "أنشِئ فريق"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.create-user": [
|
"label.create-user": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "انشاء مستخدم"
|
"value": "أنشِئ مستخدم"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.created": [
|
"label.created": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "تم الانشاء"
|
"value": "أُنشئت"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.current-password": [
|
"label.current-password": [
|
||||||
|
|
@ -218,7 +218,7 @@
|
||||||
"label.custom-range": [
|
"label.custom-range": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "فترة مخصصة"
|
"value": "فترة مخصّصة"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.dashboard": [
|
"label.dashboard": [
|
||||||
|
|
@ -236,25 +236,25 @@
|
||||||
"label.date": [
|
"label.date": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Date"
|
"value": "التاريخ"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.date-range": [
|
"label.date-range": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "فترة مخصصة"
|
"value": "فترة مخصّصة"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.day": [
|
"label.day": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Day"
|
"value": "يوم"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.default-date-range": [
|
"label.default-date-range": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "الفترة المخصصة الافتراضية"
|
"value": "الفترة المخصّصة الافتراضية"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.delete": [
|
"label.delete": [
|
||||||
|
|
@ -266,7 +266,7 @@
|
||||||
"label.delete-team": [
|
"label.delete-team": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "حذف مجموعة"
|
"value": "حذف الفريق"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.delete-user": [
|
"label.delete-user": [
|
||||||
|
|
@ -284,7 +284,7 @@
|
||||||
"label.description": [
|
"label.description": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Description"
|
"value": "الوصف"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.desktop": [
|
"label.desktop": [
|
||||||
|
|
@ -302,7 +302,7 @@
|
||||||
"label.device": [
|
"label.device": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Device"
|
"value": "الجهاز"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.devices": [
|
"label.devices": [
|
||||||
|
|
@ -314,7 +314,7 @@
|
||||||
"label.dismiss": [
|
"label.dismiss": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "اخفاء"
|
"value": "تجاهل"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.does-not-contain": [
|
"label.does-not-contain": [
|
||||||
|
|
@ -332,31 +332,31 @@
|
||||||
"label.dropoff": [
|
"label.dropoff": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Dropoff"
|
"value": "إنزال"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.edit": [
|
"label.edit": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "تعديل"
|
"value": "عدّل"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.edit-dashboard": [
|
"label.edit-dashboard": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "تعديل لوحة التحكم"
|
"value": "عدّل لوحة التحكم"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.enable-share-url": [
|
"label.enable-share-url": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "تفعيل مشاركة الرابط"
|
"value": "فعّل مشاركة الرابط"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.event": [
|
"label.event": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Event"
|
"value": "الحدث"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.event-data": [
|
"label.event-data": [
|
||||||
|
|
@ -374,115 +374,115 @@
|
||||||
"label.false": [
|
"label.false": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "False"
|
"value": "خطأ"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.field": [
|
"label.field": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Field"
|
"value": "الحقل"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.fields": [
|
"label.fields": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Fields"
|
"value": "الحقول"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.filter": [
|
"label.filter": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Filter"
|
"value": "تصفيَة"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.filter-combined": [
|
"label.filter-combined": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "مجمعة"
|
"value": "مُجمّعة"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.filter-raw": [
|
"label.filter-raw": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "مفصلة"
|
"value": "خام"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.filters": [
|
"label.filters": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Filters"
|
"value": "التصفيات"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.funnel": [
|
"label.funnel": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Funnel"
|
"value": "قمع"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.funnel-description": [
|
"label.funnel-description": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Understand the conversion and drop-off rate of users."
|
"value": "فهم معدل التحويل والانقطاع عن المستخدمين."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.greater-than": [
|
"label.greater-than": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Greater than"
|
"value": "أكبَر مِن"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.greater-than-equals": [
|
"label.greater-than-equals": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Greater than or equals"
|
"value": "أكبَر مِن أو يساوي"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.insights": [
|
"label.insights": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Insights"
|
"value": "نتائج التحليلات"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.insights-description": [
|
"label.insights-description": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Dive deeper into your data by using segments and filters."
|
"value": "تعمق في بياناتك باستخدام الشرائح والتصفيات."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.is": [
|
"label.is": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Is"
|
"value": "هو"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.is-not": [
|
"label.is-not": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Is not"
|
"value": "لم"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.is-not-set": [
|
"label.is-not-set": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Is not set"
|
"value": "لم ضُبط"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.is-set": [
|
"label.is-set": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Is set"
|
"value": "ضُبط"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.join": [
|
"label.join": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "انضمام"
|
"value": "انضم"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.join-team": [
|
"label.join-team": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "الانضمام للمجموعة"
|
"value": "انضم للفريق"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.language": [
|
"label.language": [
|
||||||
|
|
@ -506,7 +506,7 @@
|
||||||
"label.last-days": [
|
"label.last-days": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "اخر "
|
"value": "آخر "
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": 1,
|
"type": 1,
|
||||||
|
|
@ -520,7 +520,7 @@
|
||||||
"label.last-hours": [
|
"label.last-hours": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "اخر "
|
"value": "آخر "
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": 1,
|
"type": 1,
|
||||||
|
|
@ -528,13 +528,13 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": " ساعة/ساعات"
|
"value": " ساعة"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.leave": [
|
"label.leave": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "مغادرة"
|
"value": "غادر"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.leave-team": [
|
"label.leave-team": [
|
||||||
|
|
@ -546,13 +546,13 @@
|
||||||
"label.less-than": [
|
"label.less-than": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Less than"
|
"value": "أقل مِن"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.less-than-equals": [
|
"label.less-than-equals": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Less than or equals"
|
"value": "أقل مِن أو يساوي"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.login": [
|
"label.login": [
|
||||||
|
|
@ -570,7 +570,7 @@
|
||||||
"label.max": [
|
"label.max": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Max"
|
"value": "الحد الأقصى"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.members": [
|
"label.members": [
|
||||||
|
|
@ -582,7 +582,7 @@
|
||||||
"label.min": [
|
"label.min": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Min"
|
"value": "الحد الأدنى"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.mobile": [
|
"label.mobile": [
|
||||||
|
|
@ -600,13 +600,13 @@
|
||||||
"label.my-websites": [
|
"label.my-websites": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "My websites"
|
"value": "مواقعي"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.name": [
|
"label.name": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "الإسم"
|
"value": "الاسم"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.new-password": [
|
"label.new-password": [
|
||||||
|
|
@ -618,19 +618,19 @@
|
||||||
"label.none": [
|
"label.none": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "غير معرف"
|
"value": "غير معرّف"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.os": [
|
"label.os": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "OS"
|
"value": "نظام التشغيل"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.overview": [
|
"label.overview": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Overview"
|
"value": "نظرة عامة"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.owner": [
|
"label.owner": [
|
||||||
|
|
@ -642,7 +642,7 @@
|
||||||
"label.page-of": [
|
"label.page-of": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Page "
|
"value": "صفحة "
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": 1,
|
"type": 1,
|
||||||
|
|
@ -650,7 +650,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": " of "
|
"value": " من "
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": 1,
|
"type": 1,
|
||||||
|
|
@ -666,7 +666,7 @@
|
||||||
"label.pageTitle": [
|
"label.pageTitle": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Page title"
|
"value": "عنوان الصفحة"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.pages": [
|
"label.pages": [
|
||||||
|
|
@ -706,7 +706,7 @@
|
||||||
"label.query": [
|
"label.query": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Query"
|
"value": "استعلام"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.query-parameters": [
|
"label.query-parameters": [
|
||||||
|
|
@ -724,7 +724,7 @@
|
||||||
"label.referrer": [
|
"label.referrer": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Referrer"
|
"value": "المرجع"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.referrers": [
|
"label.referrers": [
|
||||||
|
|
@ -742,13 +742,13 @@
|
||||||
"label.regenerate": [
|
"label.regenerate": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "اعادة انشاء"
|
"value": "إعادة توليد"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.region": [
|
"label.region": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Region"
|
"value": "المنطقة"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.regions": [
|
"label.regions": [
|
||||||
|
|
@ -760,13 +760,13 @@
|
||||||
"label.remove": [
|
"label.remove": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "إزالة"
|
"value": "أزِل"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.reports": [
|
"label.reports": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Reports"
|
"value": "التقارير"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.required": [
|
"label.required": [
|
||||||
|
|
@ -790,13 +790,13 @@
|
||||||
"label.retention": [
|
"label.retention": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Retention"
|
"value": "الاحتفاظ"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.retention-description": [
|
"label.retention-description": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Measure your website stickiness by tracking how often users return."
|
"value": "قس مدى ثبات موقعك على الويب من خلال تتبع عدد مرات عودة المستخدمين."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.role": [
|
"label.role": [
|
||||||
|
|
@ -808,7 +808,7 @@
|
||||||
"label.run-query": [
|
"label.run-query": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Run query"
|
"value": "شغّل الاستعلام"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.save": [
|
"label.save": [
|
||||||
|
|
@ -826,19 +826,19 @@
|
||||||
"label.search": [
|
"label.search": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Search"
|
"value": "بحث"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.select-date": [
|
"label.select-date": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Select date"
|
"value": "حدد التاريخ"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.select-website": [
|
"label.select-website": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "اختيار موقع"
|
"value": "حدد موقع"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.sessions": [
|
"label.sessions": [
|
||||||
|
|
@ -850,7 +850,7 @@
|
||||||
"label.settings": [
|
"label.settings": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "اعدادات"
|
"value": "الإعدادات"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.share-url": [
|
"label.share-url": [
|
||||||
|
|
@ -868,7 +868,7 @@
|
||||||
"label.sum": [
|
"label.sum": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Sum"
|
"value": "المجموع"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.tablet": [
|
"label.tablet": [
|
||||||
|
|
@ -880,55 +880,55 @@
|
||||||
"label.team": [
|
"label.team": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "مجموعة"
|
"value": "الفريق"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.team-guest": [
|
"label.team-guest": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "زائر للمجموعة"
|
"value": "ضيف الفريق"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.team-id": [
|
"label.team-id": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "معرف المجموعة"
|
"value": "معرّف الفريق"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.team-member": [
|
"label.team-member": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "عضو المجموعة"
|
"value": "عضو الفريق"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.team-name": [
|
"label.team-name": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Team name"
|
"value": "اسم الفريق"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.team-owner": [
|
"label.team-owner": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "مدير المجموعة"
|
"value": "مدير الفريق"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.team-websites": [
|
"label.team-websites": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Team websites"
|
"value": "مواقع الفريق"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.teams": [
|
"label.teams": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "المجموعات"
|
"value": "الفرق"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.theme": [
|
"label.theme": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "المظهر"
|
"value": "السمة"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.this-month": [
|
"label.this-month": [
|
||||||
|
|
@ -976,13 +976,13 @@
|
||||||
"label.total": [
|
"label.total": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Total"
|
"value": "الإجمالي"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.total-records": [
|
"label.total-records": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Total records"
|
"value": "إجمالي السجلات"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.tracking-code": [
|
"label.tracking-code": [
|
||||||
|
|
@ -994,19 +994,19 @@
|
||||||
"label.true": [
|
"label.true": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "True"
|
"value": "حقيقي"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.type": [
|
"label.type": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Type"
|
"value": "النوع"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.unique": [
|
"label.unique": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Unique"
|
"value": "فريد"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.unique-visitors": [
|
"label.unique-visitors": [
|
||||||
|
|
@ -1024,7 +1024,7 @@
|
||||||
"label.untitled": [
|
"label.untitled": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Untitled"
|
"value": "بدون عنوان"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.url": [
|
"label.url": [
|
||||||
|
|
@ -1042,7 +1042,7 @@
|
||||||
"label.user": [
|
"label.user": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "مستخدم"
|
"value": "المستخدم"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.username": [
|
"label.username": [
|
||||||
|
|
@ -1060,7 +1060,7 @@
|
||||||
"label.value": [
|
"label.value": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Value"
|
"value": "القيمة"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.view": [
|
"label.view": [
|
||||||
|
|
@ -1078,7 +1078,7 @@
|
||||||
"label.view-only": [
|
"label.view-only": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "View only"
|
"value": "عرض فقط"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.views": [
|
"label.views": [
|
||||||
|
|
@ -1096,13 +1096,13 @@
|
||||||
"label.website": [
|
"label.website": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Website"
|
"value": "الموقع"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.website-id": [
|
"label.website-id": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "معرف الموقع"
|
"value": "معرّف الموقع"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.websites": [
|
"label.websites": [
|
||||||
|
|
@ -1114,7 +1114,7 @@
|
||||||
"label.window": [
|
"label.window": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Window"
|
"value": "النافذة"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.yesterday": [
|
"label.yesterday": [
|
||||||
|
|
@ -1202,7 +1202,7 @@
|
||||||
"message.delete-account": [
|
"message.delete-account": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "To delete this account, type "
|
"value": "لحذف هذا الحساب، اكتب "
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": 1,
|
"type": 1,
|
||||||
|
|
@ -1210,13 +1210,13 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": " in the box below to confirm."
|
"value": " في المربع أدناه للتأكيد."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"message.delete-website": [
|
"message.delete-website": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "To delete this website, type "
|
"value": "لحذف هذا الموقع، اكتب "
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": 1,
|
"type": 1,
|
||||||
|
|
@ -1224,13 +1224,13 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": " in the box below to confirm."
|
"value": " في المربع أدناه للتأكيد."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"message.delete-website-warning": [
|
"message.delete-website-warning": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "كافة البيانات المرتبطة سيم حذفها ايضا."
|
"value": "سيتم حذف كافة بيانات الموقع."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"message.error": [
|
"message.error": [
|
||||||
|
|
@ -1288,7 +1288,7 @@
|
||||||
"message.new-version-available": [
|
"message.new-version-available": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "A new version of Umami "
|
"value": "إصدار جديد من Umami "
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": 1,
|
"type": 1,
|
||||||
|
|
@ -1296,7 +1296,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": " is available!"
|
"value": " متاح!"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"message.no-data-available": [
|
"message.no-data-available": [
|
||||||
|
|
@ -1308,7 +1308,7 @@
|
||||||
"message.no-event-data": [
|
"message.no-event-data": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "No event data is available."
|
"value": "لا توجد بيانات الحدث متاحة."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"message.no-match-password": [
|
"message.no-match-password": [
|
||||||
|
|
@ -1320,19 +1320,19 @@
|
||||||
"message.no-results-found": [
|
"message.no-results-found": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "No results were found."
|
"value": "لا توجد نتائج."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"message.no-team-websites": [
|
"message.no-team-websites": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "هذه المجموعة ليس لديه اي موقع."
|
"value": "هذا الفريق ليس لديه أي مواقع."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"message.no-teams": [
|
"message.no-teams": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "لم تقم بإنشاء اي مجموعة."
|
"value": "لم تنشِئ اي فرق."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"message.no-users": [
|
"message.no-users": [
|
||||||
|
|
@ -1356,7 +1356,7 @@
|
||||||
"message.reset-website": [
|
"message.reset-website": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "To reset this website, type "
|
"value": "لإعادة ضبط موقع الويب هذا، اكتب "
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": 1,
|
"type": 1,
|
||||||
|
|
@ -1364,7 +1364,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": " in the box below to confirm."
|
"value": " في المربع أدناه للتأكيد."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"message.reset-website-warning": [
|
"message.reset-website-warning": [
|
||||||
|
|
@ -1396,19 +1396,19 @@
|
||||||
"message.team-already-member": [
|
"message.team-already-member": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "أنت عضو في المجموعة"
|
"value": "أنت عضو في الفريق"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"message.team-not-found": [
|
"message.team-not-found": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "لم يتم العثور على المجموعة"
|
"value": "لم يتم العثور على الفريق"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"message.team-websites-info": [
|
"message.team-websites-info": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "يمكن مشاهدة الموقع من اي عضو في المجموعة."
|
"value": "يمكن مشاهدة الموقع من اي عضو في الفريق."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"message.tracking-code": [
|
"message.tracking-code": [
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
"value": "Дадаць сайт"
|
"value": "Дадаць сайт"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.admin": [
|
"label.administrator": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Адміністратар"
|
"value": "Адміністратар"
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
"value": "ওয়েবসাইট যুক্ত করুন"
|
"value": "ওয়েবসাইট যুক্ত করুন"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.admin": [
|
"label.administrator": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "অ্যাডমিন"
|
"value": "অ্যাডমিন"
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
"value": "Afegeix lloc web"
|
"value": "Afegeix lloc web"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.admin": [
|
"label.administrator": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Administrador"
|
"value": "Administrador"
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
"value": "Přidat web"
|
"value": "Přidat web"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.admin": [
|
"label.administrator": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Administrátor"
|
"value": "Administrátor"
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
"value": "Tilføj hjemmeside"
|
"value": "Tilføj hjemmeside"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.admin": [
|
"label.administrator": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Administrator"
|
"value": "Administrator"
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
"value": "Websiite hinzuefüege"
|
"value": "Websiite hinzuefüege"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.admin": [
|
"label.administrator": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Administrator"
|
"value": "Administrator"
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
"value": "Website hinzufügen"
|
"value": "Website hinzufügen"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.admin": [
|
"label.administrator": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Administrator"
|
"value": "Administrator"
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
"value": "Προσθήκη ιστότοπου"
|
"value": "Προσθήκη ιστότοπου"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.admin": [
|
"label.administrator": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Διαχειριστής"
|
"value": "Διαχειριστής"
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
"value": "Add website"
|
"value": "Add website"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.admin": [
|
"label.administrator": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Administrator"
|
"value": "Administrator"
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
"value": "Add website"
|
"value": "Add website"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.admin": [
|
"label.administrator": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Administrator"
|
"value": "Administrator"
|
||||||
|
|
@ -1382,7 +1382,7 @@
|
||||||
"message.share-url": [
|
"message.share-url": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Your website stats are publically available at the following URL:"
|
"value": "Your website stats are publicly available at the following URL:"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"message.team-already-member": [
|
"message.team-already-member": [
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
"value": "Nuevo sitio web"
|
"value": "Nuevo sitio web"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.admin": [
|
"label.administrator": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Administrador"
|
"value": "Administrador"
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
"value": "افزودن وبسایت"
|
"value": "افزودن وبسایت"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.admin": [
|
"label.administrator": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "مدیر"
|
"value": "مدیر"
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
"value": "Lisää verkkosivu"
|
"value": "Lisää verkkosivu"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.admin": [
|
"label.administrator": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Järjestelmänvalvoja"
|
"value": "Järjestelmänvalvoja"
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
"value": "Legg heimasíðu afturat"
|
"value": "Legg heimasíðu afturat"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.admin": [
|
"label.administrator": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Fyrisitari"
|
"value": "Fyrisitari"
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
"value": "Ajouter un site"
|
"value": "Ajouter un site"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.admin": [
|
"label.administrator": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Administrateur"
|
"value": "Administrateur"
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
"value": "Engadir sitio web"
|
"value": "Engadir sitio web"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.admin": [
|
"label.administrator": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Administradora"
|
"value": "Administradora"
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
"value": "הוספת אתר"
|
"value": "הוספת אתר"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.admin": [
|
"label.administrator": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "מנהל"
|
"value": "מנהל"
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
"value": "वेबसाइट"
|
"value": "वेबसाइट"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.admin": [
|
"label.administrator": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "प्रशासक"
|
"value": "प्रशासक"
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
"value": "Dodaj web stranicu"
|
"value": "Dodaj web stranicu"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.admin": [
|
"label.administrator": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Administrator"
|
"value": "Administrator"
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
"value": "Weboldal hozzáadása"
|
"value": "Weboldal hozzáadása"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.admin": [
|
"label.administrator": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Adminisztrátor"
|
"value": "Adminisztrátor"
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
"value": "Tambah situs web"
|
"value": "Tambah situs web"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.admin": [
|
"label.administrator": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Pengelola"
|
"value": "Pengelola"
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
"value": "Aggiungi sito"
|
"value": "Aggiungi sito"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.admin": [
|
"label.administrator": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Amministratore"
|
"value": "Amministratore"
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
"value": "Webサイトの追加"
|
"value": "Webサイトの追加"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.admin": [
|
"label.administrator": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "管理者"
|
"value": "管理者"
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
"value": "បន្ថែមគេហទំព័រ"
|
"value": "បន្ថែមគេហទំព័រ"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.admin": [
|
"label.administrator": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "អ្នកគ្រប់គ្រង"
|
"value": "អ្នកគ្រប់គ្រង"
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
"value": "웹사이트 추가"
|
"value": "웹사이트 추가"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.admin": [
|
"label.administrator": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "관리자"
|
"value": "관리자"
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
"value": "Pridėti svetainę"
|
"value": "Pridėti svetainę"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.admin": [
|
"label.administrator": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Administratorius"
|
"value": "Administratorius"
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
"value": "Веб нэмэх"
|
"value": "Веб нэмэх"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.admin": [
|
"label.administrator": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Админ"
|
"value": "Админ"
|
||||||
|
|
@ -314,7 +314,7 @@
|
||||||
"label.dismiss": [
|
"label.dismiss": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Үл хэргэсэх"
|
"value": "Үл хэрэгсэх"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.does-not-contain": [
|
"label.does-not-contain": [
|
||||||
|
|
@ -422,7 +422,7 @@
|
||||||
"label.funnel-description": [
|
"label.funnel-description": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Хэрэглэгчдийн шилжилт, уналтын хэмжээг шижнлэх."
|
"value": "Хэрэглэгчдийн шилжилт, уналтын хэмжээг шинжлэх."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.greater-than": [
|
"label.greater-than": [
|
||||||
|
|
@ -446,7 +446,7 @@
|
||||||
"label.insights-description": [
|
"label.insights-description": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Өгөгдлөө хэсэгчлэн хуваах, шүүх байдлаар задлах шинжлэх."
|
"value": "Өгөгдлөө хэсэгчлэн хуваах, шүүх байдлаар задлан шинжлэх."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.is": [
|
"label.is": [
|
||||||
|
|
@ -796,7 +796,7 @@
|
||||||
"label.retention-description": [
|
"label.retention-description": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Хэрэглэгчид таны веб рүү дахин хандах буюу хэрэглэгчидээ хэр тогтоож буйг хэмжих."
|
"value": "Хэрэглэгчид таны веб рүү дахин хандах буюу хэрэглэгчдээ хэр тогтоож буйг хэмжих."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.role": [
|
"label.role": [
|
||||||
|
|
@ -982,7 +982,7 @@
|
||||||
"label.total-records": [
|
"label.total-records": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Нийт мөриийн тоо"
|
"value": "Нийт мөрийн тоо"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.tracking-code": [
|
"label.tracking-code": [
|
||||||
|
|
@ -1006,7 +1006,7 @@
|
||||||
"label.unique": [
|
"label.unique": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Unique"
|
"value": "Давхардаагүй"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.unique-visitors": [
|
"label.unique-visitors": [
|
||||||
|
|
@ -1364,7 +1364,7 @@
|
||||||
"message.reset-website": [
|
"message.reset-website": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Тоон үзүүлэлийг дахин эхлүүлэхийн тулд доорх хэсэгт "
|
"value": "Тоон үзүүлэлтийг дахин эхлүүлэхийн тулд доорх хэсэгт "
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": 1,
|
"type": 1,
|
||||||
|
|
@ -1378,7 +1378,7 @@
|
||||||
"message.reset-website-warning": [
|
"message.reset-website-warning": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Энэ вебийн бүх тоон үзүүлэлтүүдийг устгах болно. Гэхдээ мөрдөх код хэвэндээ үлдэнэ."
|
"value": "Энэ вебийн бүх тоон үзүүлэлтүүдийг устгах болно. Гэхдээ мөрдөх код хэвээрээ үлдэнэ."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"message.saved": [
|
"message.saved": [
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
"value": "Tambah laman web"
|
"value": "Tambah laman web"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.admin": [
|
"label.administrator": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Pentadbir"
|
"value": "Pentadbir"
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
"value": "ဝက်ဘ်ဆိုဒ်ထည့်မည်"
|
"value": "ဝက်ဘ်ဆိုဒ်ထည့်မည်"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.admin": [
|
"label.administrator": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "အက်ဒမင်"
|
"value": "အက်ဒမင်"
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
"value": "Legg til nettsted"
|
"value": "Legg til nettsted"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.admin": [
|
"label.administrator": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Administrator"
|
"value": "Administrator"
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
"value": "Website koppelen"
|
"value": "Website koppelen"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.admin": [
|
"label.administrator": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Beheerder"
|
"value": "Beheerder"
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
"value": "Dodaj witrynę"
|
"value": "Dodaj witrynę"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.admin": [
|
"label.administrator": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Administrator"
|
"value": "Administrator"
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
"value": "Adicionar site"
|
"value": "Adicionar site"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.admin": [
|
"label.administrator": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Administrador"
|
"value": "Administrador"
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
"value": "Adicionar website"
|
"value": "Adicionar website"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.admin": [
|
"label.administrator": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Administrador"
|
"value": "Administrador"
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
"value": "Adăugare site web"
|
"value": "Adăugare site web"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.admin": [
|
"label.administrator": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Administrator"
|
"value": "Administrator"
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
"value": "Добавить сайт"
|
"value": "Добавить сайт"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.admin": [
|
"label.administrator": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Администратор"
|
"value": "Администратор"
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
"value": "වෙබ් අඩවිය එක් කරන්න"
|
"value": "වෙබ් අඩවිය එක් කරන්න"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.admin": [
|
"label.administrator": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Administrator"
|
"value": "Administrator"
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
"value": "Pridať web"
|
"value": "Pridať web"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.admin": [
|
"label.administrator": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Administrátor"
|
"value": "Administrátor"
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
"value": "Dodaj spletno mesto"
|
"value": "Dodaj spletno mesto"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.admin": [
|
"label.administrator": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Administrator"
|
"value": "Administrator"
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
"label.access-code": [
|
"label.access-code": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Access code"
|
"value": "Åtkomstkod"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.actions": [
|
"label.actions": [
|
||||||
|
|
@ -20,22 +20,22 @@
|
||||||
"label.add": [
|
"label.add": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Add"
|
"value": "Lägg till"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.add-description": [
|
"label.add-description": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Add description"
|
"value": "Lägg till beskrivning"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.add-website": [
|
"label.add-website": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Lägg till webbsajt"
|
"value": "Lägg till webbplats"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.admin": [
|
"label.administrator": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Administratör"
|
"value": "Administratör"
|
||||||
|
|
@ -44,7 +44,7 @@
|
||||||
"label.after": [
|
"label.after": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "After"
|
"value": "Efter"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.all": [
|
"label.all": [
|
||||||
|
|
@ -62,19 +62,19 @@
|
||||||
"label.analytics": [
|
"label.analytics": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Analys"
|
"value": "Webbplats Analys"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.average": [
|
"label.average": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Average"
|
"value": "Genomsnitt"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.average-visit-time": [
|
"label.average-visit-time": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Medelbesökstid"
|
"value": "Genomsnittlig besökstid"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.back": [
|
"label.back": [
|
||||||
|
|
@ -86,25 +86,25 @@
|
||||||
"label.before": [
|
"label.before": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Before"
|
"value": "Före"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.bounce-rate": [
|
"label.bounce-rate": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Avvisningfrekvens"
|
"value": "Avvisningsfrekvens"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.breakdown": [
|
"label.breakdown": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Breakdown"
|
"value": "Analys"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.browser": [
|
"label.browser": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Browser"
|
"value": "Webbläsare"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.browsers": [
|
"label.browsers": [
|
||||||
|
|
@ -134,7 +134,7 @@
|
||||||
"label.city": [
|
"label.city": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "City"
|
"value": "Stad"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.clear-all": [
|
"label.clear-all": [
|
||||||
|
|
@ -158,7 +158,7 @@
|
||||||
"label.contains": [
|
"label.contains": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Contains"
|
"value": "Innehåller"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.continue": [
|
"label.continue": [
|
||||||
|
|
@ -176,19 +176,19 @@
|
||||||
"label.country": [
|
"label.country": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Country"
|
"value": "Land"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.create": [
|
"label.create": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Create"
|
"value": "Skapa"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.create-report": [
|
"label.create-report": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Create report"
|
"value": "Skapa rapport"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.create-team": [
|
"label.create-team": [
|
||||||
|
|
@ -236,19 +236,19 @@
|
||||||
"label.date": [
|
"label.date": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Date"
|
"value": "Datum"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.date-range": [
|
"label.date-range": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Datumomfång"
|
"value": "Tidsperiod"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.day": [
|
"label.day": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Day"
|
"value": "Dag"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.default-date-range": [
|
"label.default-date-range": [
|
||||||
|
|
@ -278,13 +278,13 @@
|
||||||
"label.delete-website": [
|
"label.delete-website": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Radera webbsajt"
|
"value": "Radera webbplats"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.description": [
|
"label.description": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Description"
|
"value": "Beskrivning"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.desktop": [
|
"label.desktop": [
|
||||||
|
|
@ -296,13 +296,13 @@
|
||||||
"label.details": [
|
"label.details": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Detailjer"
|
"value": "Detaljer"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.device": [
|
"label.device": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Device"
|
"value": "Enhet"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.devices": [
|
"label.devices": [
|
||||||
|
|
@ -320,7 +320,7 @@
|
||||||
"label.does-not-contain": [
|
"label.does-not-contain": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Does not contain"
|
"value": "Innehåller inte"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.domain": [
|
"label.domain": [
|
||||||
|
|
@ -332,7 +332,7 @@
|
||||||
"label.dropoff": [
|
"label.dropoff": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Dropoff"
|
"value": "Bortfall"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.edit": [
|
"label.edit": [
|
||||||
|
|
@ -350,19 +350,19 @@
|
||||||
"label.enable-share-url": [
|
"label.enable-share-url": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Aktivera delnings-URL"
|
"value": "Aktivera delningslänk"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.event": [
|
"label.event": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Event"
|
"value": "Händelse"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.event-data": [
|
"label.event-data": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Event data"
|
"value": "Händelsedata"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.events": [
|
"label.events": [
|
||||||
|
|
@ -374,19 +374,19 @@
|
||||||
"label.false": [
|
"label.false": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "False"
|
"value": "Falskt"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.field": [
|
"label.field": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Field"
|
"value": "Fält"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.fields": [
|
"label.fields": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Fields"
|
"value": "Fältar"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.filter": [
|
"label.filter": [
|
||||||
|
|
@ -410,7 +410,7 @@
|
||||||
"label.filters": [
|
"label.filters": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Filters"
|
"value": "Filter"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.funnel": [
|
"label.funnel": [
|
||||||
|
|
@ -422,55 +422,55 @@
|
||||||
"label.funnel-description": [
|
"label.funnel-description": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Understand the conversion and drop-off rate of users."
|
"value": "Förstå omvandlingen och bortfallsfrekvensen för användare."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.greater-than": [
|
"label.greater-than": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Greater than"
|
"value": "Större än"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.greater-than-equals": [
|
"label.greater-than-equals": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Greater than or equals"
|
"value": "Större än eller lika med"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.insights": [
|
"label.insights": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Insights"
|
"value": "Insikter"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.insights-description": [
|
"label.insights-description": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Dive deeper into your data by using segments and filters."
|
"value": "Dyk djupare in i din data genom att använda olika segment och filter."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.is": [
|
"label.is": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Is"
|
"value": "Är"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.is-not": [
|
"label.is-not": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Is not"
|
"value": "Är inte"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.is-not-set": [
|
"label.is-not-set": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Is not set"
|
"value": "Är inte inställd"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.is-set": [
|
"label.is-set": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Is set"
|
"value": "Är inställd"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.join": [
|
"label.join": [
|
||||||
|
|
@ -482,7 +482,7 @@
|
||||||
"label.join-team": [
|
"label.join-team": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "gå med i team"
|
"value": "Gå med i team"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.language": [
|
"label.language": [
|
||||||
|
|
@ -546,13 +546,13 @@
|
||||||
"label.less-than": [
|
"label.less-than": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Less than"
|
"value": "Mindre än"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.less-than-equals": [
|
"label.less-than-equals": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Less than or equals"
|
"value": "Mindre än eller lika med"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.login": [
|
"label.login": [
|
||||||
|
|
@ -600,7 +600,7 @@
|
||||||
"label.my-websites": [
|
"label.my-websites": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "My websites"
|
"value": "Mina webbplatser"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.name": [
|
"label.name": [
|
||||||
|
|
@ -624,13 +624,13 @@
|
||||||
"label.os": [
|
"label.os": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "OS"
|
"value": "Operativsystem"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.overview": [
|
"label.overview": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Overview"
|
"value": "Översikt"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.owner": [
|
"label.owner": [
|
||||||
|
|
@ -642,7 +642,7 @@
|
||||||
"label.page-of": [
|
"label.page-of": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Page "
|
"value": "Sida "
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": 1,
|
"type": 1,
|
||||||
|
|
@ -650,7 +650,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": " of "
|
"value": " av "
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": 1,
|
"type": 1,
|
||||||
|
|
@ -666,7 +666,7 @@
|
||||||
"label.pageTitle": [
|
"label.pageTitle": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Page title"
|
"value": "Sidtitel"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.pages": [
|
"label.pages": [
|
||||||
|
|
@ -706,13 +706,13 @@
|
||||||
"label.query": [
|
"label.query": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Frågor"
|
"value": "Fråga"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.query-parameters": [
|
"label.query-parameters": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Fråge-parametrar"
|
"value": "Frågeparametrar"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.realtime": [
|
"label.realtime": [
|
||||||
|
|
@ -724,7 +724,7 @@
|
||||||
"label.referrer": [
|
"label.referrer": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Referrer"
|
"value": "Hänvisare"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.referrers": [
|
"label.referrers": [
|
||||||
|
|
@ -742,7 +742,7 @@
|
||||||
"label.regenerate": [
|
"label.regenerate": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Regenerera"
|
"value": "Förnya"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.region": [
|
"label.region": [
|
||||||
|
|
@ -766,7 +766,7 @@
|
||||||
"label.reports": [
|
"label.reports": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Reports"
|
"value": "Rapporter"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.required": [
|
"label.required": [
|
||||||
|
|
@ -784,7 +784,7 @@
|
||||||
"label.reset-website": [
|
"label.reset-website": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Återställ statistik"
|
"value": "Återställ webbplats"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.retention": [
|
"label.retention": [
|
||||||
|
|
@ -796,7 +796,7 @@
|
||||||
"label.retention-description": [
|
"label.retention-description": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Measure your website stickiness by tracking how often users return."
|
"value": "Mät din webbplats engagemang genom att följa hur ofta användare återvänder."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.role": [
|
"label.role": [
|
||||||
|
|
@ -808,7 +808,7 @@
|
||||||
"label.run-query": [
|
"label.run-query": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Run query"
|
"value": "Kör sökning"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.save": [
|
"label.save": [
|
||||||
|
|
@ -826,25 +826,25 @@
|
||||||
"label.search": [
|
"label.search": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Search"
|
"value": "Sök"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.select-date": [
|
"label.select-date": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Select date"
|
"value": "Välj datum"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.select-website": [
|
"label.select-website": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Välj webbsajt"
|
"value": "Välj webbplats"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.sessions": [
|
"label.sessions": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Sessions"
|
"value": "Sessioner"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.settings": [
|
"label.settings": [
|
||||||
|
|
@ -856,7 +856,7 @@
|
||||||
"label.share-url": [
|
"label.share-url": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Delnings-URL"
|
"value": "Delningslänk"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.single-day": [
|
"label.single-day": [
|
||||||
|
|
@ -868,13 +868,13 @@
|
||||||
"label.sum": [
|
"label.sum": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Sum"
|
"value": "Summa"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.tablet": [
|
"label.tablet": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Platta"
|
"value": "Surfplatta"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.team": [
|
"label.team": [
|
||||||
|
|
@ -904,7 +904,7 @@
|
||||||
"label.team-name": [
|
"label.team-name": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Team name"
|
"value": "Team namn"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.team-owner": [
|
"label.team-owner": [
|
||||||
|
|
@ -916,7 +916,7 @@
|
||||||
"label.team-websites": [
|
"label.team-websites": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Team websites"
|
"value": "Team webbplatser"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.teams": [
|
"label.teams": [
|
||||||
|
|
@ -976,13 +976,13 @@
|
||||||
"label.total": [
|
"label.total": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Total"
|
"value": "Totalt"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.total-records": [
|
"label.total-records": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Total records"
|
"value": "Totala poster"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.tracking-code": [
|
"label.tracking-code": [
|
||||||
|
|
@ -994,19 +994,19 @@
|
||||||
"label.true": [
|
"label.true": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "True"
|
"value": "Sant"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.type": [
|
"label.type": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Type"
|
"value": "Typ"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.unique": [
|
"label.unique": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Unique"
|
"value": "Unikt"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.unique-visitors": [
|
"label.unique-visitors": [
|
||||||
|
|
@ -1018,25 +1018,25 @@
|
||||||
"label.unknown": [
|
"label.unknown": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Okänd"
|
"value": "Okänt"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.untitled": [
|
"label.untitled": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Untitled"
|
"value": "Namnlös"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.url": [
|
"label.url": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "URL"
|
"value": "Länk"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.urls": [
|
"label.urls": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "URLs"
|
"value": "Länkar"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.user": [
|
"label.user": [
|
||||||
|
|
@ -1054,13 +1054,13 @@
|
||||||
"label.users": [
|
"label.users": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Users"
|
"value": "Användare"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.value": [
|
"label.value": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Value"
|
"value": "Värde"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.view": [
|
"label.view": [
|
||||||
|
|
@ -1078,7 +1078,7 @@
|
||||||
"label.view-only": [
|
"label.view-only": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "View only"
|
"value": "Endast visning"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.views": [
|
"label.views": [
|
||||||
|
|
@ -1096,25 +1096,25 @@
|
||||||
"label.website": [
|
"label.website": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Website"
|
"value": "Webbplats"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.website-id": [
|
"label.website-id": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Webbsajt-ID"
|
"value": "Webbplats ID"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.websites": [
|
"label.websites": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Webbsajt"
|
"value": "Webbplatser"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.window": [
|
"label.window": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Window"
|
"value": "Fönster"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.yesterday": [
|
"label.yesterday": [
|
||||||
|
|
@ -1206,7 +1206,7 @@
|
||||||
"message.delete-account": [
|
"message.delete-account": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "To delete this account, type "
|
"value": "För att radera det här kontot, skriv "
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": 1,
|
"type": 1,
|
||||||
|
|
@ -1214,13 +1214,13 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": " in the box below to confirm."
|
"value": " i rutan nedan för att bekräfta."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"message.delete-website": [
|
"message.delete-website": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "To delete this website, type "
|
"value": "För att radera den här webbplatsen, skriv "
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": 1,
|
"type": 1,
|
||||||
|
|
@ -1228,13 +1228,13 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": " in the box below to confirm."
|
"value": " i rutan nedan för att bekräfta."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"message.delete-website-warning": [
|
"message.delete-website-warning": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "All tillhörande data kommer också raderas."
|
"value": "All tillhörande data kommer också att raderas."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"message.error": [
|
"message.error": [
|
||||||
|
|
@ -1292,7 +1292,7 @@
|
||||||
"message.new-version-available": [
|
"message.new-version-available": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "A new version of Umami "
|
"value": "En ny version av Umami "
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": 1,
|
"type": 1,
|
||||||
|
|
@ -1300,7 +1300,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": " is available!"
|
"value": " är tillgänglig!"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"message.no-data-available": [
|
"message.no-data-available": [
|
||||||
|
|
@ -1312,25 +1312,25 @@
|
||||||
"message.no-event-data": [
|
"message.no-event-data": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "No event data is available."
|
"value": "Ingen händelsedata är tillgänglig."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"message.no-match-password": [
|
"message.no-match-password": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Lösenorden är inte samma"
|
"value": "Lösenorden matchar inte"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"message.no-results-found": [
|
"message.no-results-found": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "No results were found."
|
"value": "Inga resultat hittades."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"message.no-team-websites": [
|
"message.no-team-websites": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Det här teamet har inga webbsajter."
|
"value": "Det här teamet har inga webbplatser."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"message.no-teams": [
|
"message.no-teams": [
|
||||||
|
|
@ -1348,19 +1348,19 @@
|
||||||
"message.no-websites-configured": [
|
"message.no-websites-configured": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Du har inga webbsajter."
|
"value": "Du har inte konfigurerat några webbplatser."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"message.page-not-found": [
|
"message.page-not-found": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Sidan kan inte hittas."
|
"value": "Sidan kunde inte hittas."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"message.reset-website": [
|
"message.reset-website": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "För att återställa statistiken skriv "
|
"value": "För att återställa webbplatsen, skriv "
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": 1,
|
"type": 1,
|
||||||
|
|
@ -1374,19 +1374,19 @@
|
||||||
"message.reset-website-warning": [
|
"message.reset-website-warning": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "All statistik för webbsajten tas bort men spårningskoden förblir oförändrad."
|
"value": "All statistik för webbplatsen tas bort, men spårningskoden förblir oförändrad."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"message.saved": [
|
"message.saved": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Sparades!"
|
"value": "Sparat!"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"message.share-url": [
|
"message.share-url": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Det här är den offentliga delnings-URL:en för "
|
"value": "Det här är den offentliga delningslänken för "
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": 1,
|
"type": 1,
|
||||||
|
|
@ -1406,13 +1406,13 @@
|
||||||
"message.team-not-found": [
|
"message.team-not-found": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Team kan inte hittas."
|
"value": "Teamet kunde inte hittas."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"message.team-websites-info": [
|
"message.team-websites-info": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Websajter kan ses av alla i teamet."
|
"value": "Webbplatserna kan ses av alla i teamet."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"message.tracking-code": [
|
"message.tracking-code": [
|
||||||
|
|
@ -1424,7 +1424,7 @@
|
||||||
"message.user-deleted": [
|
"message.user-deleted": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Användare raderad."
|
"value": "Användaren har raderats."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"message.visitor-log": [
|
"message.visitor-log": [
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
"value": "வலைத்தளத்தைச் சேர்க்க"
|
"value": "வலைத்தளத்தைச் சேர்க்க"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.admin": [
|
"label.administrator": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "நிர்வாகியைச் சேர்க்க"
|
"value": "நிர்வாகியைச் சேர்க்க"
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
"value": "เพิ่มเว็บไซต์"
|
"value": "เพิ่มเว็บไซต์"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.admin": [
|
"label.administrator": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "ผู้ดูแลระบบ"
|
"value": "ผู้ดูแลระบบ"
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
"value": "Web sitesi ekle"
|
"value": "Web sitesi ekle"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.admin": [
|
"label.administrator": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Yönetici"
|
"value": "Yönetici"
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
"value": "Додати сайт"
|
"value": "Додати сайт"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.admin": [
|
"label.administrator": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Адміністратор"
|
"value": "Адміністратор"
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
"value": "ویب سائٹ کا اضافہ کریں"
|
"value": "ویب سائٹ کا اضافہ کریں"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.admin": [
|
"label.administrator": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "منتظم"
|
"value": "منتظم"
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
"value": "Thêm website"
|
"value": "Thêm website"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.admin": [
|
"label.administrator": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Quản trị"
|
"value": "Quản trị"
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
"value": "添加网站"
|
"value": "添加网站"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.admin": [
|
"label.administrator": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "管理员"
|
"value": "管理员"
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
"value": "新增網站"
|
"value": "新增網站"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label.admin": [
|
"label.administrator": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "管理員"
|
"value": "管理員"
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import 'dotenv/config';
|
import 'dotenv/config';
|
||||||
import buble from '@rollup/plugin-buble';
|
|
||||||
import replace from '@rollup/plugin-replace';
|
import replace from '@rollup/plugin-replace';
|
||||||
import { terser } from 'rollup-plugin-terser';
|
import { terser } from 'rollup-plugin-terser';
|
||||||
|
|
||||||
|
|
@ -15,7 +14,6 @@ export default {
|
||||||
delimiters: ['', ''],
|
delimiters: ['', ''],
|
||||||
preventAssignment: true,
|
preventAssignment: true,
|
||||||
}),
|
}),
|
||||||
buble({ objectAssign: true }),
|
|
||||||
terser({ compress: { evaluate: false } }),
|
terser({ compress: { evaluate: false } }),
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ export function App({ children }) {
|
||||||
{children}
|
{children}
|
||||||
<UpdateNotice user={user} config={config} />
|
<UpdateNotice user={user} config={config} />
|
||||||
{process.env.NODE_ENV === 'production' && !pathname.includes('/share/') && (
|
{process.env.NODE_ENV === 'production' && !pathname.includes('/share/') && (
|
||||||
<Script src={`/telemetry.js`} />
|
<Script src={`${process.env.basePath}/telemetry.js`} />
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
background: var(--base75);
|
background: var(--base75);
|
||||||
border-bottom: 1px solid var(--base300);
|
border-bottom: 1px solid var(--base300);
|
||||||
padding: 0 20px;
|
padding: 0 20px;
|
||||||
|
z-index: 200;
|
||||||
}
|
}
|
||||||
|
|
||||||
.logo {
|
.logo {
|
||||||
|
|
@ -26,6 +27,7 @@
|
||||||
padding: 0 40px;
|
padding: 0 40px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
max-height: 60px;
|
max-height: 60px;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.links a,
|
.links a,
|
||||||
|
|
|
||||||
|
|
@ -2,35 +2,38 @@
|
||||||
import { Icon, Text } from 'react-basics';
|
import { Icon, Text } from 'react-basics';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import Icons from 'components/icons';
|
import HamburgerButton from 'components/common/HamburgerButton';
|
||||||
import ThemeButton from 'components/input/ThemeButton';
|
import ThemeButton from 'components/input/ThemeButton';
|
||||||
import LanguageButton from 'components/input/LanguageButton';
|
import LanguageButton from 'components/input/LanguageButton';
|
||||||
import ProfileButton from 'components/input/ProfileButton';
|
import ProfileButton from 'components/input/ProfileButton';
|
||||||
import useMessages from 'components/hooks/useMessages';
|
import TeamsButton from 'components/input/TeamsButton';
|
||||||
import HamburgerButton from 'components/common/HamburgerButton';
|
import Icons from 'components/icons';
|
||||||
import { usePathname } from 'next/navigation';
|
import { useLogin, useMessages, useNavigation, useTeamUrl } from 'components/hooks';
|
||||||
import styles from './NavBar.module.css';
|
import styles from './NavBar.module.css';
|
||||||
|
|
||||||
export function NavBar() {
|
export function NavBar() {
|
||||||
const pathname = usePathname();
|
const { user } = useLogin();
|
||||||
const { formatMessage, labels } = useMessages();
|
const { formatMessage, labels } = useMessages();
|
||||||
const cloudMode = Boolean(process.env.cloudMode);
|
const { pathname } = useNavigation();
|
||||||
|
const { teamId, renderTeamUrl } = useTeamUrl();
|
||||||
|
|
||||||
|
const cloudMode = !!process.env.cloudMode;
|
||||||
|
|
||||||
const links = [
|
const links = [
|
||||||
{ label: formatMessage(labels.dashboard), url: '/dashboard' },
|
{ label: formatMessage(labels.dashboard), url: renderTeamUrl('/dashboard') },
|
||||||
{ label: formatMessage(labels.websites), url: '/websites' },
|
{ label: formatMessage(labels.websites), url: renderTeamUrl('/websites') },
|
||||||
{ label: formatMessage(labels.reports), url: '/reports' },
|
{ label: formatMessage(labels.reports), url: renderTeamUrl('/reports') },
|
||||||
{ label: formatMessage(labels.settings), url: '/settings' },
|
{ label: formatMessage(labels.settings), url: renderTeamUrl('/settings') },
|
||||||
].filter(n => n);
|
].filter(n => n);
|
||||||
|
|
||||||
const menuItems = [
|
const menuItems = [
|
||||||
{
|
{
|
||||||
label: formatMessage(labels.dashboard),
|
label: formatMessage(labels.dashboard),
|
||||||
url: '/dashboard',
|
url: renderTeamUrl('/dashboard'),
|
||||||
},
|
},
|
||||||
!cloudMode && {
|
!cloudMode && {
|
||||||
label: formatMessage(labels.settings),
|
label: formatMessage(labels.settings),
|
||||||
url: '/settings',
|
url: renderTeamUrl('/settings'),
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
label: formatMessage(labels.websites),
|
label: formatMessage(labels.websites),
|
||||||
|
|
@ -46,13 +49,13 @@ export function NavBar() {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: formatMessage(labels.profile),
|
label: formatMessage(labels.profile),
|
||||||
url: '/settings/profile',
|
url: '/profile',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
cloudMode && {
|
cloudMode && {
|
||||||
label: formatMessage(labels.profile),
|
label: formatMessage(labels.profile),
|
||||||
url: '/settings/profile',
|
url: '/profile',
|
||||||
},
|
},
|
||||||
!cloudMode && { label: formatMessage(labels.logout), url: '/logout' },
|
!cloudMode && { label: formatMessage(labels.logout), url: '/logout' },
|
||||||
].filter(n => n);
|
].filter(n => n);
|
||||||
|
|
@ -80,6 +83,7 @@ export function NavBar() {
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.actions}>
|
<div className={styles.actions}>
|
||||||
|
{user?.teams?.length && <TeamsButton teamId={teamId} />}
|
||||||
<ThemeButton />
|
<ThemeButton />
|
||||||
<LanguageButton />
|
<LanguageButton />
|
||||||
<ProfileButton />
|
<ProfileButton />
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
'use client';
|
|
||||||
import { useEffect, useCallback, useState } from 'react';
|
import { useEffect, useCallback, useState } from 'react';
|
||||||
import { createPortal } from 'react-dom';
|
import { createPortal } from 'react-dom';
|
||||||
import { Button } from 'react-basics';
|
import { Button } from 'react-basics';
|
||||||
|
|
@ -6,7 +5,7 @@ import { setItem } from 'next-basics';
|
||||||
import useStore, { checkVersion } from 'store/version';
|
import useStore, { checkVersion } from 'store/version';
|
||||||
import { REPO_URL, VERSION_CHECK } from 'lib/constants';
|
import { REPO_URL, VERSION_CHECK } from 'lib/constants';
|
||||||
import styles from './UpdateNotice.module.css';
|
import styles from './UpdateNotice.module.css';
|
||||||
import useMessages from 'components/hooks/useMessages';
|
import { useMessages } from 'components/hooks';
|
||||||
import { usePathname } from 'next/navigation';
|
import { usePathname } from 'next/navigation';
|
||||||
|
|
||||||
export function UpdateNotice({ user, config }) {
|
export function UpdateNotice({ user, config }) {
|
||||||
|
|
|
||||||
6
src/app/(main)/console/ConsolePage.tsx
Normal file
6
src/app/(main)/console/ConsolePage.tsx
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
'use client';
|
||||||
|
import TestConsole from './TestConsole';
|
||||||
|
|
||||||
|
export default function ConsolePage({ websiteId }) {
|
||||||
|
return <TestConsole websiteId={websiteId} />;
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,17 @@
|
||||||
.test {
|
.container {
|
||||||
|
display: grid;
|
||||||
|
gap: 30px;
|
||||||
|
padding-bottom: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
border: 1px solid var(--base400);
|
border: 1px solid var(--base400);
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
padding: 0 20px 20px 20px;
|
padding: 0 20px 20px 20px;
|
||||||
|
display: grid;
|
||||||
|
gap: 40px;
|
||||||
|
grid-template-columns: repeat(3, minmax(300px, 1fr));
|
||||||
|
box-shadow: 0 0 0 10px var(--base100);
|
||||||
}
|
}
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
|
|
@ -9,3 +19,15 @@
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
margin: 20px 0;
|
margin: 20px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.group {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrapped {
|
||||||
|
border: 1px solid var(--blue900);
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 8px 16px;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
'use client';
|
|
||||||
import { Button } from 'react-basics';
|
import { Button } from 'react-basics';
|
||||||
import Head from 'next/head';
|
import Head from 'next/head';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
|
|
@ -7,9 +6,8 @@ import WebsiteSelect from 'components/input/WebsiteSelect';
|
||||||
import Page from 'components/layout/Page';
|
import Page from 'components/layout/Page';
|
||||||
import PageHeader from 'components/layout/PageHeader';
|
import PageHeader from 'components/layout/PageHeader';
|
||||||
import EventsChart from 'components/metrics/EventsChart';
|
import EventsChart from 'components/metrics/EventsChart';
|
||||||
import WebsiteChart from 'app/(main)/websites/[id]/WebsiteChart';
|
import WebsiteChart from '../websites/[websiteId]/WebsiteChart';
|
||||||
import useApi from 'components/hooks/useApi';
|
import { useApi, useNavigation } from 'components/hooks';
|
||||||
import useNavigation from 'components/hooks/useNavigation';
|
|
||||||
import styles from './TestConsole.module.css';
|
import styles from './TestConsole.module.css';
|
||||||
|
|
||||||
export function TestConsole({ websiteId }: { websiteId: string }) {
|
export function TestConsole({ websiteId }: { websiteId: string }) {
|
||||||
|
|
@ -82,15 +80,15 @@ export function TestConsole({ websiteId }: { websiteId: string }) {
|
||||||
<WebsiteSelect websiteId={website?.id} onSelect={handleChange} />
|
<WebsiteSelect websiteId={website?.id} onSelect={handleChange} />
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
{website && (
|
{website && (
|
||||||
<>
|
<div className={styles.container}>
|
||||||
<Script
|
<Script
|
||||||
async
|
async
|
||||||
data-website-id={websiteId}
|
data-website-id={websiteId}
|
||||||
src={`${process.env.basePath}/script.js`}
|
src={`${process.env.basePath}/script.js`}
|
||||||
data-cache="true"
|
data-cache="true"
|
||||||
/>
|
/>
|
||||||
<div className={styles.test}>
|
<div className={styles.actions}>
|
||||||
<div>
|
<div className={styles.group}>
|
||||||
<div className={styles.header}>Page links</div>
|
<div className={styles.header}>Page links</div>
|
||||||
<div>
|
<div>
|
||||||
<Link href={`/console/${websiteId}/page/1/?q=abc`}>page one</Link>
|
<Link href={`/console/${websiteId}/page/1/?q=abc`}>page one</Link>
|
||||||
|
|
@ -114,12 +112,11 @@ export function TestConsole({ websiteId }: { websiteId: string }) {
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className={styles.group}>
|
||||||
<div className={styles.header}>Click events</div>
|
<div className={styles.header}>Click events</div>
|
||||||
<Button id="send-event-button" data-umami-event="button-click" variant="primary">
|
<Button id="send-event-button" data-umami-event="button-click" variant="primary">
|
||||||
Send event
|
Send event
|
||||||
</Button>
|
</Button>
|
||||||
<p />
|
|
||||||
<Button
|
<Button
|
||||||
id="send-event-data-button"
|
id="send-event-data-button"
|
||||||
data-umami-event="button-click"
|
data-umami-event="button-click"
|
||||||
|
|
@ -129,23 +126,39 @@ export function TestConsole({ websiteId }: { websiteId: string }) {
|
||||||
>
|
>
|
||||||
Send event with data
|
Send event with data
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button
|
||||||
|
id="button-with-div-button"
|
||||||
|
data-umami-event="button-click"
|
||||||
|
data-umami-event-name="bob"
|
||||||
|
data-umami-event-id="123"
|
||||||
|
variant="primary"
|
||||||
|
>
|
||||||
|
<div className={styles.wrapped}>Button with div</div>
|
||||||
|
</Button>
|
||||||
|
<div data-umami-event="div-click" className={styles.wrapped}>
|
||||||
|
DIV with attribute
|
||||||
|
</div>
|
||||||
|
<div data-umami-event="div-click-one" className={styles.wrapped}>
|
||||||
|
<div data-umami-event="div-click-two" className={styles.wrapped}>
|
||||||
|
<div data-umami-event="div-click-three" className={styles.wrapped}>
|
||||||
|
Nested DIV
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className={styles.group}>
|
||||||
<div className={styles.header}>Javascript events</div>
|
<div className={styles.header}>Javascript events</div>
|
||||||
<Button id="manual-button" variant="primary" onClick={handleClick}>
|
<Button id="manual-button" variant="primary" onClick={handleClick}>
|
||||||
Run script
|
Run script
|
||||||
</Button>
|
</Button>
|
||||||
<p />
|
|
||||||
<Button id="manual-button" variant="primary" onClick={handleIdentifyClick}>
|
<Button id="manual-button" variant="primary" onClick={handleIdentifyClick}>
|
||||||
Run identify
|
Run identify
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<WebsiteChart websiteId={website.id} />
|
||||||
<WebsiteChart websiteId={website.id} />
|
<EventsChart websiteId={website.id} />
|
||||||
<EventsChart websiteId={website.id} />
|
</div>
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
</Page>
|
</Page>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,20 @@
|
||||||
import TestConsole from '../TestConsole';
|
|
||||||
import { Metadata } from 'next';
|
import { Metadata } from 'next';
|
||||||
|
import ConsolePage from '../ConsolePage';
|
||||||
|
|
||||||
async function getEnabled() {
|
async function getEnabled() {
|
||||||
return !!process.env.ENABLE_TEST_CONSOLE;
|
return !!process.env.ENABLE_TEST_CONSOLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function ({ params: { id } }) {
|
export default async function ({ params: { websiteId } }) {
|
||||||
const enabled = await getEnabled();
|
const enabled = await getEnabled();
|
||||||
|
|
||||||
if (!enabled) {
|
if (!enabled) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <TestConsole websiteId={id?.[0]} />;
|
return <ConsolePage websiteId={websiteId?.[0]} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: 'Test Console | umami',
|
title: 'Test Console | Umami',
|
||||||
};
|
};
|
||||||
|
|
@ -1,81 +0,0 @@
|
||||||
'use client';
|
|
||||||
import { Button, Icon, Icons, Loading, Text } from 'react-basics';
|
|
||||||
import Link from 'next/link';
|
|
||||||
import PageHeader from 'components/layout/PageHeader';
|
|
||||||
import Pager from 'components/common/Pager';
|
|
||||||
import WebsiteChartList from '../../(main)/websites/[id]/WebsiteChartList';
|
|
||||||
import DashboardSettingsButton from 'app/(main)/dashboard/DashboardSettingsButton';
|
|
||||||
import DashboardEdit from 'app/(main)/dashboard/DashboardEdit';
|
|
||||||
import EmptyPlaceholder from 'components/common/EmptyPlaceholder';
|
|
||||||
import useApi from 'components/hooks/useApi';
|
|
||||||
import useDashboard from 'store/dashboard';
|
|
||||||
import useMessages from 'components/hooks/useMessages';
|
|
||||||
import useLocale from 'components/hooks/useLocale';
|
|
||||||
import useFilterQuery from 'components/hooks/useFilterQuery';
|
|
||||||
import { useUser } from 'components/hooks';
|
|
||||||
|
|
||||||
export function Dashboard() {
|
|
||||||
const { formatMessage, labels, messages } = useMessages();
|
|
||||||
const { user } = useUser();
|
|
||||||
const { showCharts, editing } = useDashboard();
|
|
||||||
const { dir } = useLocale();
|
|
||||||
const { get } = useApi();
|
|
||||||
const pageSize = 10;
|
|
||||||
|
|
||||||
const { query, params, setParams, result } = useFilterQuery({
|
|
||||||
queryKey: ['dashboard:websites'],
|
|
||||||
queryFn: (params: any) => {
|
|
||||||
return get(`/users/${user.id}/websites`, { ...params, includeTeams: true, pageSize });
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const handlePageChange = (page: number) => {
|
|
||||||
setParams({ ...params, page });
|
|
||||||
};
|
|
||||||
|
|
||||||
const { data, count } = result || {};
|
|
||||||
const hasData = !!(data as any)?.length;
|
|
||||||
const { page } = params;
|
|
||||||
|
|
||||||
if (query.isLoading) {
|
|
||||||
return <Loading />;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<PageHeader title={formatMessage(labels.dashboard)}>
|
|
||||||
{!editing && hasData && <DashboardSettingsButton />}
|
|
||||||
</PageHeader>
|
|
||||||
{!hasData && (
|
|
||||||
<EmptyPlaceholder message={formatMessage(messages.noWebsitesConfigured)}>
|
|
||||||
<Link href="/settings/websites">
|
|
||||||
<Button>
|
|
||||||
<Icon rotate={dir === 'rtl' ? 180 : 0}>
|
|
||||||
<Icons.ArrowRight />
|
|
||||||
</Icon>
|
|
||||||
<Text>{formatMessage(messages.goToSettings)}</Text>
|
|
||||||
</Button>
|
|
||||||
</Link>
|
|
||||||
</EmptyPlaceholder>
|
|
||||||
)}
|
|
||||||
{hasData && (
|
|
||||||
<>
|
|
||||||
{editing && <DashboardEdit />}
|
|
||||||
{!editing && (
|
|
||||||
<>
|
|
||||||
<WebsiteChartList websites={data} showCharts={showCharts} limit={pageSize} />
|
|
||||||
<Pager
|
|
||||||
page={page}
|
|
||||||
pageSize={pageSize}
|
|
||||||
count={count}
|
|
||||||
onPageChange={handlePageChange}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Dashboard;
|
|
||||||
|
|
@ -1,32 +1,30 @@
|
||||||
'use client';
|
|
||||||
import { useState, useMemo } from 'react';
|
import { useState, useMemo } 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 } from 'react-basics';
|
import { Button, Loading } 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 from 'components/hooks/useMessages';
|
import { useMessages, useWebsites } from 'components/hooks';
|
||||||
import useApi from 'components/hooks/useApi';
|
|
||||||
import styles from './DashboardEdit.module.css';
|
import styles from './DashboardEdit.module.css';
|
||||||
|
|
||||||
const dragId = 'dashboard-website-ordering';
|
const DRAG_ID = 'dashboard-website-ordering';
|
||||||
|
|
||||||
export function DashboardEdit() {
|
export function DashboardEdit({ teamId }: { teamId: string }) {
|
||||||
const settings = useDashboard();
|
const settings = useDashboard();
|
||||||
const { websiteOrder } = settings;
|
const { websiteOrder } = settings;
|
||||||
const { formatMessage, labels } = useMessages();
|
const { formatMessage, labels } = useMessages();
|
||||||
const [order, setOrder] = useState(websiteOrder || []);
|
const [order, setOrder] = useState(websiteOrder || []);
|
||||||
const { get, useQuery } = useApi();
|
const {
|
||||||
const { data: result } = useQuery({
|
result,
|
||||||
queryKey: ['websites'],
|
query: { isLoading },
|
||||||
queryFn: () => get('/websites', { includeTeams: 1 }),
|
} = useWebsites({ teamId });
|
||||||
});
|
|
||||||
const { data: websites } = result || {};
|
const websites = result?.data;
|
||||||
|
|
||||||
const ordered = useMemo(() => {
|
const ordered = useMemo(() => {
|
||||||
if (websites) {
|
if (websites) {
|
||||||
return websites
|
return websites
|
||||||
.map(website => ({ ...website, order: order.indexOf(website.id) }))
|
.map((website: { id: any }) => ({ ...website, order: order.indexOf(website.id) }))
|
||||||
.sort(firstBy('order'));
|
.sort(firstBy('order'));
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
|
|
@ -57,6 +55,10 @@ export function DashboardEdit() {
|
||||||
setOrder([]);
|
setOrder([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return <Loading />;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className={styles.buttons}>
|
<div className={styles.buttons}>
|
||||||
|
|
@ -72,7 +74,7 @@ export function DashboardEdit() {
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.dragActive}>
|
<div className={styles.dragActive}>
|
||||||
<DragDropContext onDragEnd={handleWebsiteDrag}>
|
<DragDropContext onDragEnd={handleWebsiteDrag}>
|
||||||
<Droppable droppableId={dragId}>
|
<Droppable droppableId={DRAG_ID}>
|
||||||
{(provided, snapshot) => (
|
{(provided, snapshot) => (
|
||||||
<div
|
<div
|
||||||
{...provided.droppableProps}
|
{...provided.droppableProps}
|
||||||
|
|
@ -80,7 +82,7 @@ export function DashboardEdit() {
|
||||||
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={`${dragId}-${id}`} index={index}>
|
<Draggable key={id} draggableId={`${DRAG_ID}-${id}`} index={index}>
|
||||||
{(provided, snapshot) => (
|
{(provided, snapshot) => (
|
||||||
<div
|
<div
|
||||||
ref={provided.innerRef}
|
ref={provided.innerRef}
|
||||||
|
|
|
||||||
71
src/app/(main)/dashboard/DashboardPage.tsx
Normal file
71
src/app/(main)/dashboard/DashboardPage.tsx
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
'use client';
|
||||||
|
import { Icon, Icons, Loading, Text } from 'react-basics';
|
||||||
|
import PageHeader from 'components/layout/PageHeader';
|
||||||
|
import Pager from 'components/common/Pager';
|
||||||
|
import WebsiteChartList from '../websites/[websiteId]/WebsiteChartList';
|
||||||
|
import DashboardSettingsButton from 'app/(main)/dashboard/DashboardSettingsButton';
|
||||||
|
import DashboardEdit from 'app/(main)/dashboard/DashboardEdit';
|
||||||
|
import EmptyPlaceholder from 'components/common/EmptyPlaceholder';
|
||||||
|
import { useMessages, useLocale, useTeamUrl, useWebsites } from 'components/hooks';
|
||||||
|
import useDashboard from 'store/dashboard';
|
||||||
|
import LinkButton from 'components/common/LinkButton';
|
||||||
|
|
||||||
|
export function DashboardPage() {
|
||||||
|
const { formatMessage, labels, messages } = useMessages();
|
||||||
|
const { teamId, renderTeamUrl } = useTeamUrl();
|
||||||
|
const { showCharts, editing } = useDashboard();
|
||||||
|
const { dir } = useLocale();
|
||||||
|
const pageSize = 10;
|
||||||
|
|
||||||
|
const { result, query, params, setParams } = useWebsites({ teamId }, { pageSize });
|
||||||
|
const { page } = params;
|
||||||
|
const hasData = !!result?.data?.length;
|
||||||
|
|
||||||
|
const handlePageChange = (page: number) => {
|
||||||
|
setParams({ ...params, page });
|
||||||
|
};
|
||||||
|
|
||||||
|
if (query.isLoading) {
|
||||||
|
return <Loading />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section style={{ marginBottom: 60 }}>
|
||||||
|
<PageHeader title={formatMessage(labels.dashboard)}>
|
||||||
|
{!editing && hasData && <DashboardSettingsButton />}
|
||||||
|
</PageHeader>
|
||||||
|
{!hasData && (
|
||||||
|
<EmptyPlaceholder message={formatMessage(messages.noWebsitesConfigured)}>
|
||||||
|
<LinkButton href={renderTeamUrl('/settings')}>
|
||||||
|
<Icon rotate={dir === 'rtl' ? 180 : 0}>
|
||||||
|
<Icons.ArrowRight />
|
||||||
|
</Icon>
|
||||||
|
<Text>{formatMessage(messages.goToSettings)}</Text>
|
||||||
|
</LinkButton>
|
||||||
|
</EmptyPlaceholder>
|
||||||
|
)}
|
||||||
|
{hasData && (
|
||||||
|
<>
|
||||||
|
{editing && <DashboardEdit teamId={teamId} />}
|
||||||
|
{!editing && (
|
||||||
|
<>
|
||||||
|
<WebsiteChartList
|
||||||
|
websites={result?.data as any}
|
||||||
|
showCharts={showCharts}
|
||||||
|
limit={pageSize}
|
||||||
|
/>
|
||||||
|
<Pager
|
||||||
|
page={page}
|
||||||
|
pageSize={pageSize}
|
||||||
|
count={result?.count}
|
||||||
|
onPageChange={handlePageChange}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DashboardPage;
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { TooltipPopup, Icon, Text, Flexbox, Button } from 'react-basics';
|
import { TooltipPopup, Icon, Text, Flexbox, Button } from 'react-basics';
|
||||||
import Icons from 'components/icons';
|
import Icons from 'components/icons';
|
||||||
import { saveDashboard } from 'store/dashboard';
|
import { saveDashboard } from 'store/dashboard';
|
||||||
import useMessages from 'components/hooks/useMessages';
|
import { useMessages } from 'components/hooks';
|
||||||
|
|
||||||
export function DashboardSettingsButton() {
|
export function DashboardSettingsButton() {
|
||||||
const { formatMessage, labels } = useMessages();
|
const { formatMessage, labels } = useMessages();
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import Dashboard from 'app/(main)/dashboard/Dashboard';
|
import DashboardPage from './DashboardPage';
|
||||||
import { Metadata } from 'next';
|
import { Metadata } from 'next';
|
||||||
|
|
||||||
export default function () {
|
export default function () {
|
||||||
return <Dashboard />;
|
return <DashboardPage />;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: 'Dashboard | umami',
|
title: 'Dashboard | Umami',
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -18,5 +18,16 @@
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
height: calc(100vh - 60px);
|
height: calc(100vh - 60px);
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
padding-bottom: 60px;
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 1320px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 0 20px;
|
||||||
|
min-height: calc(100vh - 60px);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,15 @@
|
||||||
import DateFilter from 'components/input/DateFilter';
|
import DateFilter from 'components/input/DateFilter';
|
||||||
import { Button, Flexbox } from 'react-basics';
|
import { Button, Flexbox } from 'react-basics';
|
||||||
import useDateRange from 'components/hooks/useDateRange';
|
import { useDateRange, useMessages } from 'components/hooks';
|
||||||
import { DEFAULT_DATE_RANGE } from 'lib/constants';
|
import { DEFAULT_DATE_RANGE } from 'lib/constants';
|
||||||
import useMessages from 'components/hooks/useMessages';
|
import { DateRange } from 'lib/types';
|
||||||
|
|
||||||
export function DateRangeSetting() {
|
export function DateRangeSetting() {
|
||||||
const { formatMessage, labels } = useMessages();
|
const { formatMessage, labels } = useMessages();
|
||||||
const [dateRange, setDateRange] = useDateRange();
|
const [dateRange, setDateRange] = useDateRange();
|
||||||
const { value } = dateRange;
|
const { value } = dateRange;
|
||||||
|
|
||||||
const handleChange = value => setDateRange(value);
|
const handleChange = (value: string | DateRange) => setDateRange(value);
|
||||||
const handleReset = () => setDateRange(DEFAULT_DATE_RANGE);
|
const handleReset = () => setDateRange(DEFAULT_DATE_RANGE);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
4
src/app/(main)/profile/LanguageSetting.module.css
Normal file
4
src/app/(main)/profile/LanguageSetting.module.css
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
div.menu {
|
||||||
|
max-height: 300px;
|
||||||
|
width: 300px;
|
||||||
|
}
|
||||||
|
|
@ -1,17 +1,26 @@
|
||||||
|
import { useState } from 'react';
|
||||||
import { Button, Dropdown, Item, Flexbox } from 'react-basics';
|
import { Button, Dropdown, Item, Flexbox } from 'react-basics';
|
||||||
import useLocale from 'components/hooks/useLocale';
|
import { useLocale, useMessages } from 'components/hooks';
|
||||||
import { DEFAULT_LOCALE } from 'lib/constants';
|
import { DEFAULT_LOCALE } from 'lib/constants';
|
||||||
import { languages } from 'lib/lang';
|
import { languages } from 'lib/lang';
|
||||||
import useMessages from 'components/hooks/useMessages';
|
import styles from './LanguageSetting.module.css';
|
||||||
|
|
||||||
export function LanguageSetting() {
|
export function LanguageSetting() {
|
||||||
|
const [search, setSearch] = useState('');
|
||||||
const { formatMessage, labels } = useMessages();
|
const { formatMessage, labels } = useMessages();
|
||||||
const { locale, saveLocale } = useLocale();
|
const { locale, saveLocale } = useLocale();
|
||||||
const options = Object.keys(languages);
|
const options = search
|
||||||
|
? Object.keys(languages).filter(n => {
|
||||||
|
return (
|
||||||
|
n.toLowerCase().includes(search.toLowerCase()) ||
|
||||||
|
languages[n].label.toLowerCase().includes(search.toLowerCase())
|
||||||
|
);
|
||||||
|
})
|
||||||
|
: Object.keys(languages);
|
||||||
|
|
||||||
const handleReset = () => saveLocale(DEFAULT_LOCALE);
|
const handleReset = () => saveLocale(DEFAULT_LOCALE);
|
||||||
|
|
||||||
const renderValue = value => languages[value].label;
|
const renderValue = (value: string | number) => languages[value].label;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flexbox gap={10}>
|
<Flexbox gap={10}>
|
||||||
|
|
@ -19,8 +28,10 @@ export function LanguageSetting() {
|
||||||
items={options}
|
items={options}
|
||||||
value={locale}
|
value={locale}
|
||||||
renderValue={renderValue}
|
renderValue={renderValue}
|
||||||
onChange={saveLocale}
|
onChange={val => saveLocale(val as string)}
|
||||||
menuProps={{ style: { height: 300, width: 300 } }}
|
allowSearch={true}
|
||||||
|
onSearch={setSearch}
|
||||||
|
menuProps={{ className: styles.menu }}
|
||||||
>
|
>
|
||||||
{item => <Item key={item}>{languages[item].label}</Item>}
|
{item => <Item key={item}>{languages[item].label}</Item>}
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { Button, Icon, Text, useToasts, ModalTrigger, Modal } from 'react-basics';
|
import { Button, Icon, Text, useToasts, ModalTrigger, Modal } from 'react-basics';
|
||||||
import PasswordEditForm from 'app/(main)/settings/profile/PasswordEditForm';
|
import PasswordEditForm from 'app/(main)/profile/PasswordEditForm';
|
||||||
import Icons from 'components/icons';
|
import Icons from 'components/icons';
|
||||||
import useMessages from 'components/hooks/useMessages';
|
import { useMessages } from 'components/hooks';
|
||||||
|
|
||||||
export function PasswordChangeButton() {
|
export function PasswordChangeButton() {
|
||||||
const { formatMessage, labels, messages } = useMessages();
|
const { formatMessage, labels, messages } = useMessages();
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import { useRef } from 'react';
|
import { useRef } from 'react';
|
||||||
import { Form, FormRow, FormInput, FormButtons, PasswordField, Button } from 'react-basics';
|
import { Form, FormRow, FormInput, FormButtons, PasswordField, Button } from 'react-basics';
|
||||||
import useApi from 'components/hooks/useApi';
|
import { useApi, useMessages } from 'components/hooks';
|
||||||
import useMessages from 'components/hooks/useMessages';
|
|
||||||
|
|
||||||
export function PasswordEditForm({ onSave, onClose }) {
|
export function PasswordEditForm({ onSave, onClose }) {
|
||||||
const { formatMessage, labels, messages } = useMessages();
|
const { formatMessage, labels, messages } = useMessages();
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
'use client';
|
|
||||||
import PageHeader from 'components/layout/PageHeader';
|
import PageHeader from 'components/layout/PageHeader';
|
||||||
import { useMessages } from 'components/hooks';
|
import { useMessages } from 'components/hooks';
|
||||||
|
|
||||||
9
src/app/(main)/profile/ProfilePage.module.css
Normal file
9
src/app/(main)/profile/ProfilePage.module.css
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
.container {
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 768px) {
|
||||||
|
.container {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,16 +1,13 @@
|
||||||
|
'use client';
|
||||||
import ProfileHeader from './ProfileHeader';
|
import ProfileHeader from './ProfileHeader';
|
||||||
import ProfileSettings from './ProfileSettings';
|
import ProfileSettings from './ProfileSettings';
|
||||||
import { Metadata } from 'next';
|
import styles from './ProfilePage.module.css';
|
||||||
|
|
||||||
export default function () {
|
export default function () {
|
||||||
return (
|
return (
|
||||||
<>
|
<div className={styles.container}>
|
||||||
<ProfileHeader />
|
<ProfileHeader />
|
||||||
<ProfileSettings />
|
<ProfileSettings />
|
||||||
</>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
|
||||||
title: 'Profile Settings | umami',
|
|
||||||
};
|
|
||||||
|
|
@ -1,18 +1,16 @@
|
||||||
'use client';
|
|
||||||
import { Form, FormRow } from 'react-basics';
|
import { Form, FormRow } from 'react-basics';
|
||||||
import TimezoneSetting from 'app/(main)/settings/profile/TimezoneSetting';
|
import TimezoneSetting from 'app/(main)/profile/TimezoneSetting';
|
||||||
import DateRangeSetting from 'app/(main)/settings/profile/DateRangeSetting';
|
import DateRangeSetting from 'app/(main)/profile/DateRangeSetting';
|
||||||
import LanguageSetting from 'app/(main)/settings/profile/LanguageSetting';
|
import LanguageSetting from 'app/(main)/profile/LanguageSetting';
|
||||||
import ThemeSetting from 'app/(main)/settings/profile/ThemeSetting';
|
import ThemeSetting from 'app/(main)/profile/ThemeSetting';
|
||||||
import PasswordChangeButton from './PasswordChangeButton';
|
import PasswordChangeButton from './PasswordChangeButton';
|
||||||
import useUser from 'components/hooks/useUser';
|
import { useLogin, useMessages } from 'components/hooks';
|
||||||
import useMessages from 'components/hooks/useMessages';
|
|
||||||
import { ROLES } from 'lib/constants';
|
import { ROLES } from 'lib/constants';
|
||||||
|
|
||||||
export function ProfileSettings() {
|
export function ProfileSettings() {
|
||||||
const { user } = useUser();
|
const { user } = useLogin();
|
||||||
const { formatMessage, labels } = useMessages();
|
const { formatMessage, labels } = useMessages();
|
||||||
const cloudMode = Boolean(process.env.cloudMode);
|
const cloudMode = !!process.env.cloudMode;
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -20,12 +18,12 @@ export function ProfileSettings() {
|
||||||
|
|
||||||
const { username, role } = user;
|
const { username, role } = user;
|
||||||
|
|
||||||
const renderRole = value => {
|
const renderRole = (value: string) => {
|
||||||
if (value === ROLES.user) {
|
if (value === ROLES.user) {
|
||||||
return formatMessage(labels.user);
|
return formatMessage(labels.user);
|
||||||
}
|
}
|
||||||
if (value === ROLES.admin) {
|
if (value === ROLES.admin) {
|
||||||
return formatMessage(labels.admin);
|
return formatMessage(labels.administrator);
|
||||||
}
|
}
|
||||||
if (value === ROLES.viewOnly) {
|
if (value === ROLES.viewOnly) {
|
||||||
return formatMessage(labels.viewOnly);
|
return formatMessage(labels.viewOnly);
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { Button, Icon } from 'react-basics';
|
import { Button, Icon } from 'react-basics';
|
||||||
import useTheme from 'components/hooks/useTheme';
|
import { useTheme } from 'components/hooks';
|
||||||
import Sun from 'assets/sun.svg';
|
import Sun from 'assets/sun.svg';
|
||||||
import Moon from 'assets/moon.svg';
|
import Moon from 'assets/moon.svg';
|
||||||
import styles from './ThemeSetting.module.css';
|
import styles from './ThemeSetting.module.css';
|
||||||
4
src/app/(main)/profile/TimezoneSetting.module.css
Normal file
4
src/app/(main)/profile/TimezoneSetting.module.css
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
div.menu {
|
||||||
|
max-height: 300px;
|
||||||
|
width: 300px;
|
||||||
|
}
|
||||||
|
|
@ -1,13 +1,17 @@
|
||||||
|
import { useState } from 'react';
|
||||||
import { Dropdown, Item, Button, Flexbox } from 'react-basics';
|
import { Dropdown, Item, Button, Flexbox } from 'react-basics';
|
||||||
import { listTimeZones } from 'timezone-support';
|
import { listTimeZones } from 'timezone-support';
|
||||||
import useTimezone from 'components/hooks/useTimezone';
|
import { useTimezone, useMessages } from 'components/hooks';
|
||||||
import useMessages from 'components/hooks/useMessages';
|
|
||||||
import { getTimezone } from 'lib/date';
|
import { getTimezone } from 'lib/date';
|
||||||
|
import styles from './TimezoneSetting.module.css';
|
||||||
|
|
||||||
export function TimezoneSetting() {
|
export function TimezoneSetting() {
|
||||||
|
const [search, setSearch] = useState('');
|
||||||
const { formatMessage, labels } = useMessages();
|
const { formatMessage, labels } = useMessages();
|
||||||
const [timezone, saveTimezone] = useTimezone();
|
const [timezone, saveTimezone] = useTimezone();
|
||||||
const options = listTimeZones();
|
const options = search
|
||||||
|
? listTimeZones().filter(n => n.toLowerCase().includes(search.toLowerCase()))
|
||||||
|
: listTimeZones();
|
||||||
|
|
||||||
const handleReset = () => saveTimezone(getTimezone());
|
const handleReset = () => saveTimezone(getTimezone());
|
||||||
|
|
||||||
|
|
@ -17,8 +21,9 @@ export function TimezoneSetting() {
|
||||||
items={options}
|
items={options}
|
||||||
value={timezone}
|
value={timezone}
|
||||||
onChange={saveTimezone}
|
onChange={saveTimezone}
|
||||||
style={{ flex: 1 }}
|
menuProps={{ className: styles.menu }}
|
||||||
menuProps={{ style: { height: 300 } }}
|
allowSearch={true}
|
||||||
|
onSearch={setSearch}
|
||||||
>
|
>
|
||||||
{item => <Item key={item}>{item}</Item>}
|
{item => <Item key={item}>{item}</Item>}
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
10
src/app/(main)/profile/page.tsx
Normal file
10
src/app/(main)/profile/page.tsx
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
import { Metadata } from 'next';
|
||||||
|
import ProfilePage from './ProfilePage';
|
||||||
|
|
||||||
|
export default function () {
|
||||||
|
return <ProfilePage />;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: 'Profile | Umami',
|
||||||
|
};
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import { Button, Icon, Icons, Modal, ModalTrigger, Text } from 'react-basics';
|
import { Button, Icon, Icons, Modal, ModalTrigger, Text } from 'react-basics';
|
||||||
import ConfirmDeleteForm from 'components/common/ConfirmDeleteForm';
|
import { useApi, useMessages, useModified } from 'components/hooks';
|
||||||
import { useApi, useMessages } from 'components/hooks';
|
import ConfirmationForm from 'components/common/ConfirmationForm';
|
||||||
import { setValue } from 'store/cache';
|
|
||||||
|
|
||||||
export function ReportDeleteButton({
|
export function ReportDeleteButton({
|
||||||
reportId,
|
reportId,
|
||||||
|
|
@ -12,14 +11,17 @@ export function ReportDeleteButton({
|
||||||
reportName: string;
|
reportName: string;
|
||||||
onDelete?: () => void;
|
onDelete?: () => void;
|
||||||
}) {
|
}) {
|
||||||
const { formatMessage, labels } = useMessages();
|
const { formatMessage, labels, messages, FormattedMessage } = useMessages();
|
||||||
const { del, useMutation } = useApi();
|
const { del, useMutation } = useApi();
|
||||||
const { mutate } = useMutation({ mutationFn: reportId => del(`/reports/${reportId}`) });
|
const { mutate, isPending, error } = useMutation({
|
||||||
|
mutationFn: reportId => del(`/reports/${reportId}`),
|
||||||
|
});
|
||||||
|
const { touch } = useModified();
|
||||||
|
|
||||||
const handleConfirm = (close: () => void) => {
|
const handleConfirm = (close: () => void) => {
|
||||||
mutate(reportId as any, {
|
mutate(reportId as any, {
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
setValue('reports', Date.now());
|
touch('reports');
|
||||||
onDelete?.();
|
onDelete?.();
|
||||||
close();
|
close();
|
||||||
},
|
},
|
||||||
|
|
@ -34,12 +36,20 @@ export function ReportDeleteButton({
|
||||||
</Icon>
|
</Icon>
|
||||||
<Text>{formatMessage(labels.delete)}</Text>
|
<Text>{formatMessage(labels.delete)}</Text>
|
||||||
</Button>
|
</Button>
|
||||||
<Modal>
|
<Modal title={formatMessage(labels.deleteReport)}>
|
||||||
{close => (
|
{(close: () => void) => (
|
||||||
<ConfirmDeleteForm
|
<ConfirmationForm
|
||||||
name={reportName}
|
message={
|
||||||
|
<FormattedMessage
|
||||||
|
{...messages.confirmDelete}
|
||||||
|
values={{ target: <b>{reportName}</b> }}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
isLoading={isPending}
|
||||||
|
error={error}
|
||||||
onConfirm={handleConfirm.bind(null, close)}
|
onConfirm={handleConfirm.bind(null, close)}
|
||||||
onClose={close}
|
onClose={close}
|
||||||
|
buttonLabel={formatMessage(labels.delete)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,15 @@
|
||||||
'use client';
|
|
||||||
import { useReports } from 'components/hooks';
|
import { useReports } from 'components/hooks';
|
||||||
import ReportsTable from './ReportsTable';
|
import ReportsTable from './ReportsTable';
|
||||||
import DataTable from 'components/common/DataTable';
|
import DataTable from 'components/common/DataTable';
|
||||||
|
|
||||||
export default function ReportsDataTable({ websiteId }: { websiteId?: string }) {
|
export default function ReportsDataTable({
|
||||||
const queryResult = useReports(websiteId);
|
websiteId,
|
||||||
|
teamId,
|
||||||
|
}: {
|
||||||
|
websiteId?: string;
|
||||||
|
teamId?: string;
|
||||||
|
}) {
|
||||||
|
const queryResult = useReports({ websiteId, teamId });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DataTable queryResult={queryResult}>
|
<DataTable queryResult={queryResult}>
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,20 @@
|
||||||
'use client';
|
|
||||||
import PageHeader from 'components/layout/PageHeader';
|
import PageHeader from 'components/layout/PageHeader';
|
||||||
import { Button, Icon, Icons, Text } from 'react-basics';
|
import { Icon, Icons, Text } from 'react-basics';
|
||||||
import { useMessages } from 'components/hooks';
|
import { useMessages, useTeamUrl } from 'components/hooks';
|
||||||
import { useRouter } from 'next/navigation';
|
import LinkButton from 'components/common/LinkButton';
|
||||||
|
|
||||||
export function ReportsHeader() {
|
export function ReportsHeader() {
|
||||||
const { formatMessage, labels } = useMessages();
|
const { formatMessage, labels } = useMessages();
|
||||||
const router = useRouter();
|
const { renderTeamUrl } = useTeamUrl();
|
||||||
|
|
||||||
const handleClick = () => router.push('/reports/create');
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageHeader title={formatMessage(labels.reports)}>
|
<PageHeader title={formatMessage(labels.reports)}>
|
||||||
<Button variant="primary" onClick={handleClick}>
|
<LinkButton href={renderTeamUrl('/reports/create')} variant="primary">
|
||||||
<Icon>
|
<Icon>
|
||||||
<Icons.Plus />
|
<Icons.Plus />
|
||||||
</Icon>
|
</Icon>
|
||||||
<Text>{formatMessage(labels.createReport)}</Text>
|
<Text>{formatMessage(labels.createReport)}</Text>
|
||||||
</Button>
|
</LinkButton>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
15
src/app/(main)/reports/ReportsPage.tsx
Normal file
15
src/app/(main)/reports/ReportsPage.tsx
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
'use client';
|
||||||
|
import ReportsHeader from './ReportsHeader';
|
||||||
|
import ReportsDataTable from './ReportsDataTable';
|
||||||
|
|
||||||
|
export default function ReportsPage({ teamId }: { teamId: string }) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ReportsHeader />
|
||||||
|
<ReportsDataTable teamId={teamId} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
export const metadata = {
|
||||||
|
title: 'Reports | Umami',
|
||||||
|
};
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
import { GridColumn, GridTable, Icon, Icons, Text, useBreakpoint } from 'react-basics';
|
import { GridColumn, GridTable, Icon, Icons, Text, useBreakpoint } from 'react-basics';
|
||||||
import LinkButton from 'components/common/LinkButton';
|
import LinkButton from 'components/common/LinkButton';
|
||||||
import { useMessages } from 'components/hooks';
|
import { useMessages, useLogin, useTeamUrl } from 'components/hooks';
|
||||||
import useUser from 'components/hooks/useUser';
|
|
||||||
import { REPORT_TYPES } from 'lib/constants';
|
import { REPORT_TYPES } from 'lib/constants';
|
||||||
import ReportDeleteButton from './ReportDeleteButton';
|
import ReportDeleteButton from './ReportDeleteButton';
|
||||||
|
|
||||||
export function ReportsTable({ data = [], showDomain }: { data: any[]; showDomain?: boolean }) {
|
export function ReportsTable({ data = [], showDomain }: { data: any[]; showDomain?: boolean }) {
|
||||||
const { formatMessage, labels } = useMessages();
|
const { formatMessage, labels } = useMessages();
|
||||||
const { user } = useUser();
|
const { user } = useLogin();
|
||||||
const breakpoint = useBreakpoint();
|
const breakpoint = useBreakpoint();
|
||||||
|
const { renderTeamUrl } = useTeamUrl();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<GridTable data={data} cardMode={['xs', 'sm', 'md'].includes(breakpoint)}>
|
<GridTable data={data} cardMode={['xs', 'sm', 'md'].includes(breakpoint)}>
|
||||||
|
|
@ -34,7 +34,7 @@ export function ReportsTable({ data = [], showDomain }: { data: any[]; showDomai
|
||||||
{(user.id === userId || user.id === website?.userId) && (
|
{(user.id === userId || user.id === website?.userId) && (
|
||||||
<ReportDeleteButton reportId={id} reportName={name} />
|
<ReportDeleteButton reportId={id} reportName={name} />
|
||||||
)}
|
)}
|
||||||
<LinkButton href={`/reports/${id}`}>
|
<LinkButton href={renderTeamUrl(`/reports/${id}`)}>
|
||||||
<Icon>
|
<Icon>
|
||||||
<Icons.ArrowRight />
|
<Icons.ArrowRight />
|
||||||
</Icon>
|
</Icon>
|
||||||
|
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
import ReportDetails from './ReportDetails';
|
|
||||||
import { Metadata } from 'next';
|
|
||||||
|
|
||||||
export default function ReportDetailsPage({ params: { id } }) {
|
|
||||||
if (!id) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return <ReportDetails reportId={id} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
|
||||||
title: 'Reports | umami',
|
|
||||||
};
|
|
||||||
|
|
@ -3,7 +3,7 @@ import { FormRow } from 'react-basics';
|
||||||
import { parseDateRange } from 'lib/date';
|
import { parseDateRange } from 'lib/date';
|
||||||
import DateFilter from 'components/input/DateFilter';
|
import DateFilter from 'components/input/DateFilter';
|
||||||
import WebsiteSelect from 'components/input/WebsiteSelect';
|
import WebsiteSelect from 'components/input/WebsiteSelect';
|
||||||
import { useMessages } from 'components/hooks';
|
import { useMessages, useTeamUrl, useWebsite } from 'components/hooks';
|
||||||
import { ReportContext } from './Report';
|
import { ReportContext } from './Report';
|
||||||
|
|
||||||
export interface BaseParametersProps {
|
export interface BaseParametersProps {
|
||||||
|
|
@ -21,10 +21,12 @@ export function BaseParameters({
|
||||||
}: BaseParametersProps) {
|
}: BaseParametersProps) {
|
||||||
const { report, updateReport } = useContext(ReportContext);
|
const { report, updateReport } = useContext(ReportContext);
|
||||||
const { formatMessage, labels } = useMessages();
|
const { formatMessage, labels } = useMessages();
|
||||||
|
const { teamId } = useTeamUrl();
|
||||||
const { parameters } = report || {};
|
const { parameters } = report || {};
|
||||||
const { websiteId, dateRange } = parameters || {};
|
const { websiteId, dateRange } = parameters || {};
|
||||||
const { value, startDate, endDate } = dateRange || {};
|
const { value, startDate, endDate } = dateRange || {};
|
||||||
|
const { data: website } = useWebsite(websiteId);
|
||||||
|
const { name } = website || {};
|
||||||
|
|
||||||
const handleWebsiteSelect = (websiteId: string) => {
|
const handleWebsiteSelect = (websiteId: string) => {
|
||||||
updateReport({ websiteId, parameters: { websiteId } });
|
updateReport({ websiteId, parameters: { websiteId } });
|
||||||
|
|
@ -38,8 +40,10 @@ export function BaseParameters({
|
||||||
<>
|
<>
|
||||||
{showWebsiteSelect && (
|
{showWebsiteSelect && (
|
||||||
<FormRow label={formatMessage(labels.website)}>
|
<FormRow label={formatMessage(labels.website)}>
|
||||||
{allowWebsiteSelect && (
|
{allowWebsiteSelect ? (
|
||||||
<WebsiteSelect websiteId={websiteId} onSelect={handleWebsiteSelect} />
|
<WebsiteSelect teamId={teamId} websiteId={websiteId} onSelect={handleWebsiteSelect} />
|
||||||
|
) : (
|
||||||
|
name
|
||||||
)}
|
)}
|
||||||
</FormRow>
|
</FormRow>
|
||||||
)}
|
)}
|
||||||
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