mirror of
https://github.com/umami-software/umami.git
synced 2026-02-24 14:35:35 +01:00
Compare commits
13 commits
8c703eff93
...
27c342811e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
27c342811e | ||
|
|
8115709a8b | ||
|
|
83a014e884 | ||
|
|
3afe843461 | ||
|
|
c51dd7e606 | ||
|
|
050df528a6 | ||
|
|
cb209eee81 | ||
|
|
6497cd0cd4 | ||
|
|
3a4f4c1e27 | ||
|
|
7d9fe30626 | ||
|
|
9c36f76e1b | ||
|
|
b2c829a077 | ||
|
|
bf4e6ea96f |
27 changed files with 349 additions and 118 deletions
2
.github/workflows/cd-cloud.yml
vendored
2
.github/workflows/cd-cloud.yml
vendored
|
|
@ -1,4 +1,4 @@
|
||||||
name: Create docker images
|
name: Create docker images (cloud)
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
|
|
|
||||||
111
.github/workflows/cd.yml
vendored
111
.github/workflows/cd.yml
vendored
|
|
@ -1,50 +1,101 @@
|
||||||
name: Create docker images
|
name: Create docker images
|
||||||
|
|
||||||
on: [create]
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
- main
|
||||||
|
- dev
|
||||||
|
# Publish semver tags as releases.
|
||||||
|
tags: [ 'v*.*.*' ]
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
- main
|
||||||
|
- dev
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: Build, push, and deploy
|
name: Build, push, and deploy
|
||||||
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
# This is used to complete the identity challenge
|
||||||
|
# with sigstore/fulcio when running outside of PRs.
|
||||||
|
id-token: write
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
db-type: [postgresql]
|
db-type: [postgresql]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v5
|
||||||
|
|
||||||
- name: Set env
|
# Install the cosign tool except on PR
|
||||||
run: |
|
# https://github.com/sigstore/cosign-installer
|
||||||
echo "NOW=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_ENV
|
- name: Install cosign
|
||||||
|
if: github.event_name != 'pull_request'
|
||||||
|
uses: sigstore/cosign-installer@v3
|
||||||
|
|
||||||
- name: Generate tags
|
- name: Set up Docker Buildx
|
||||||
id: generate_tags
|
uses: docker/setup-buildx-action@v3
|
||||||
run: |
|
|
||||||
echo "tag_patch=$(echo ${{ matrix.db-type }})-${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
|
|
||||||
echo "tag_minor=$(echo ${{ matrix.db-type }})-$(echo ${GITHUB_REF#refs/tags/} | cut -d. -f1,2)" >> $GITHUB_ENV
|
|
||||||
echo "tag_major=$(echo ${{ matrix.db-type }})-$(echo ${GITHUB_REF#refs/tags/} | cut -d. -f1)" >> $GITHUB_ENV
|
|
||||||
echo "tag_latest=$(echo ${{ matrix.db-type }})-latest" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- uses: mr-smithers-excellent/docker-build-push@v6
|
- name: Log into registry docker.io
|
||||||
name: Build & push Docker image to ghcr.io for ${{ matrix.db-type }}
|
if: github.event_name != 'pull_request' && github.repository == 'umami-software/umami'
|
||||||
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
image: umami
|
|
||||||
tags: ${{ env.tag_major }}, ${{ env.tag_minor }}, ${{ env.tag_patch }}, ${{ env.tag_latest }}
|
|
||||||
buildArgs: DATABASE_TYPE=${{ matrix.db-type }}
|
|
||||||
registry: ghcr.io
|
|
||||||
multiPlatform: true
|
|
||||||
platform: linux/amd64,linux/arm64
|
|
||||||
username: ${{ github.actor }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- uses: mr-smithers-excellent/docker-build-push@v6
|
|
||||||
name: Build & push Docker image to docker.io for ${{ matrix.db-type }}
|
|
||||||
with:
|
|
||||||
image: umamisoftware/umami
|
|
||||||
tags: ${{ env.tag_major }}, ${{ env.tag_minor }}, ${{ env.tag_patch }}, ${{ env.tag_latest }}
|
|
||||||
buildArgs: DATABASE_TYPE=${{ matrix.db-type }}
|
|
||||||
registry: docker.io
|
registry: docker.io
|
||||||
username: ${{ secrets.DOCKER_USERNAME }}
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
|
|
||||||
|
- name: Log into ghcr registry
|
||||||
|
if: github.event_name != 'pull_request'
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Extract Docker metadata
|
||||||
|
id: meta
|
||||||
|
uses: docker/metadata-action@v5
|
||||||
|
with:
|
||||||
|
images: |
|
||||||
|
umamisoftware/umami,enable=${{ github.repository == 'umami-software/umami' }}
|
||||||
|
ghcr.io/${{ github.repository }}
|
||||||
|
flavor: |
|
||||||
|
latest=auto
|
||||||
|
prefix=${{ matrix.db-type }}-
|
||||||
|
tags: |
|
||||||
|
type=ref,event=branch
|
||||||
|
type=ref,event=pr
|
||||||
|
|
||||||
|
# output 1.1.2
|
||||||
|
type=semver,pattern={{version}}
|
||||||
|
# output 1.1
|
||||||
|
type=semver,pattern={{major}}.{{minor}}
|
||||||
|
# output 1
|
||||||
|
type=semver,pattern={{major}}
|
||||||
|
|
||||||
|
- name: Build and push Docker image
|
||||||
|
id: build-and-push
|
||||||
|
uses: docker/build-push-action@v6
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
platforms: linux/amd64,linux/arm64
|
||||||
|
build-args: DATABASE_TYPE=${{ matrix.db-type }}
|
||||||
|
push: ${{ github.event_name != 'pull_request' }}
|
||||||
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
cache-from: type=gha
|
||||||
|
cache-to: type=gha,mode=max
|
||||||
|
|
||||||
|
# Sign the resulting Docker image digest except on PRs.
|
||||||
|
- name: Sign the published Docker image
|
||||||
|
if: ${{ github.event_name != 'pull_request' }}
|
||||||
|
env:
|
||||||
|
TAGS: ${{ steps.meta.outputs.tags }}
|
||||||
|
DIGEST: ${{ steps.build-and-push.outputs.digest }}
|
||||||
|
run: echo "${TAGS}" | xargs -I {} cosign sign --yes "{}@${DIGEST}"
|
||||||
|
|
|
||||||
9
.github/workflows/ci.yml
vendored
9
.github/workflows/ci.yml
vendored
|
|
@ -14,11 +14,16 @@ jobs:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- node-version: 18.18
|
- node-version: 18.18
|
||||||
db-type: postgresql
|
pnpm-version: 10
|
||||||
|
db-type: postgresql
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
- uses: pnpm/action-setup@v4 # required so that setup-node will work
|
||||||
|
with:
|
||||||
|
version: ${{ matrix.pnpm-version }}
|
||||||
|
run_install: false
|
||||||
- name: Use Node.js ${{ matrix.node-version }}
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@umami/components",
|
"name": "@umami/components",
|
||||||
"version": "0.125.0",
|
"version": "0.127.0",
|
||||||
"description": "Umami React components.",
|
"description": "Umami React components.",
|
||||||
"author": "Mike Cao <mike@mikecao.com>",
|
"author": "Mike Cao <mike@mikecao.com>",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,7 @@ export function SideNav(props: SidebarProps) {
|
||||||
<SidebarItem
|
<SidebarItem
|
||||||
label={label}
|
label={label}
|
||||||
icon={icon}
|
icon={icon}
|
||||||
isSelected={pathname.endsWith(path)}
|
isSelected={pathname.includes(path)}
|
||||||
role="button"
|
role="button"
|
||||||
/>
|
/>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ export function LinkDeleteButton({
|
||||||
name: string;
|
name: string;
|
||||||
onSave?: () => void;
|
onSave?: () => void;
|
||||||
}) {
|
}) {
|
||||||
const { formatMessage, labels, getErrorMessage } = useMessages();
|
const { formatMessage, labels, getErrorMessage, FormattedMessage } = useMessages();
|
||||||
const { mutateAsync, isPending, error, touch } = useDeleteQuery(`/links/${linkId}`);
|
const { mutateAsync, isPending, error, touch } = useDeleteQuery(`/links/${linkId}`);
|
||||||
|
|
||||||
const handleConfirm = async (close: () => void) => {
|
const handleConfirm = async (close: () => void) => {
|
||||||
|
|
@ -33,9 +33,14 @@ export function LinkDeleteButton({
|
||||||
<Dialog title={formatMessage(labels.confirm)} style={{ width: 400 }}>
|
<Dialog title={formatMessage(labels.confirm)} style={{ width: 400 }}>
|
||||||
{({ close }) => (
|
{({ close }) => (
|
||||||
<ConfirmationForm
|
<ConfirmationForm
|
||||||
message={formatMessage(messages.confirmRemove, {
|
message={
|
||||||
target: name,
|
<FormattedMessage
|
||||||
})}
|
{...messages.confirmRemove}
|
||||||
|
values={{
|
||||||
|
target: <b>{name}</b>,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
isLoading={isPending}
|
isLoading={isPending}
|
||||||
error={getErrorMessage(error)}
|
error={getErrorMessage(error)}
|
||||||
onConfirm={handleConfirm.bind(null, close)}
|
onConfirm={handleConfirm.bind(null, close)}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ export function LinkHeader() {
|
||||||
const link = useLink();
|
const link = useLink();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageHeader title={link.name} description={link.url} icon={<Link />}>
|
<PageHeader title={link.name} description={link.url} icon={<Link />} marginBottom="3">
|
||||||
<LinkButton href={getSlugUrl(link.slug)} target="_blank">
|
<LinkButton href={getSlugUrl(link.slug)} target="_blank">
|
||||||
<Icon>
|
<Icon>
|
||||||
<ExternalLink />
|
<ExternalLink />
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ export function PixelDeleteButton({
|
||||||
name: string;
|
name: string;
|
||||||
onSave?: () => void;
|
onSave?: () => void;
|
||||||
}) {
|
}) {
|
||||||
const { formatMessage, labels, getErrorMessage } = useMessages();
|
const { formatMessage, labels, getErrorMessage, FormattedMessage } = useMessages();
|
||||||
const { mutateAsync, isPending, error } = useDeleteQuery(`/pixels/${pixelId}`);
|
const { mutateAsync, isPending, error } = useDeleteQuery(`/pixels/${pixelId}`);
|
||||||
const { touch } = useModified();
|
const { touch } = useModified();
|
||||||
|
|
||||||
|
|
@ -32,9 +32,14 @@ export function PixelDeleteButton({
|
||||||
<Dialog title={formatMessage(labels.confirm)} style={{ width: 400 }}>
|
<Dialog title={formatMessage(labels.confirm)} style={{ width: 400 }}>
|
||||||
{({ close }) => (
|
{({ close }) => (
|
||||||
<ConfirmationForm
|
<ConfirmationForm
|
||||||
message={formatMessage(messages.confirmRemove, {
|
message={
|
||||||
target: name,
|
<FormattedMessage
|
||||||
})}
|
{...messages.confirmRemove}
|
||||||
|
values={{
|
||||||
|
target: <b>{name}</b>,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
isLoading={isPending}
|
isLoading={isPending}
|
||||||
error={getErrorMessage(error)}
|
error={getErrorMessage(error)}
|
||||||
onConfirm={handleConfirm.bind(null, close)}
|
onConfirm={handleConfirm.bind(null, close)}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ export function PixelHeader() {
|
||||||
const pixel = usePixel();
|
const pixel = usePixel();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageHeader title={pixel.name} description={pixel.slug} icon={<Grid2x2 />}>
|
<PageHeader title={pixel.name} icon={<Grid2x2 />} marginBottom="3">
|
||||||
<LinkButton href={getSlugUrl(pixel.slug)} target="_blank">
|
<LinkButton href={getSlugUrl(pixel.slug)} target="_blank">
|
||||||
<Icon>
|
<Icon>
|
||||||
<ExternalLink />
|
<ExternalLink />
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ export function TeamLeaveForm({
|
||||||
onSave: () => void;
|
onSave: () => void;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
}) {
|
}) {
|
||||||
const { formatMessage, labels, messages, getErrorMessage } = useMessages();
|
const { formatMessage, labels, messages, getErrorMessage, FormattedMessage } = useMessages();
|
||||||
const { mutateAsync, error, isPending } = useDeleteQuery(`/teams/${teamId}/users/${userId}`);
|
const { mutateAsync, error, isPending } = useDeleteQuery(`/teams/${teamId}/users/${userId}`);
|
||||||
const { touch } = useModified();
|
const { touch } = useModified();
|
||||||
|
|
||||||
|
|
@ -31,9 +31,14 @@ export function TeamLeaveForm({
|
||||||
return (
|
return (
|
||||||
<ConfirmationForm
|
<ConfirmationForm
|
||||||
buttonLabel={formatMessage(labels.leave)}
|
buttonLabel={formatMessage(labels.leave)}
|
||||||
message={formatMessage(messages.confirmLeave, {
|
message={
|
||||||
target: teamName,
|
<FormattedMessage
|
||||||
})}
|
{...messages.confirmLeave}
|
||||||
|
values={{
|
||||||
|
target: <b>{teamName}</b>,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
onConfirm={handleConfirm}
|
onConfirm={handleConfirm}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
isLoading={isPending}
|
isLoading={isPending}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { Button, Icon, Modal, DialogTrigger, Dialog, Text, useToast } from '@umami/react-zen';
|
import { Button, Icon, Modal, DialogTrigger, Dialog, Text, useToast } from '@umami/react-zen';
|
||||||
import { AddUserSvg } from '@/components/icons';
|
import { UserPlus } from '@/components/icons';
|
||||||
import { useMessages, useModified } from '@/components/hooks';
|
import { useMessages, useModified } from '@/components/hooks';
|
||||||
import { TeamJoinForm } from './TeamJoinForm';
|
import { TeamJoinForm } from './TeamJoinForm';
|
||||||
|
|
||||||
|
|
@ -17,7 +17,7 @@ export function TeamsJoinButton() {
|
||||||
<DialogTrigger>
|
<DialogTrigger>
|
||||||
<Button>
|
<Button>
|
||||||
<Icon>
|
<Icon>
|
||||||
<AddUserSvg />
|
<UserPlus />
|
||||||
</Icon>
|
</Icon>
|
||||||
<Text>{formatMessage(labels.joinTeam)}</Text>
|
<Text>{formatMessage(labels.joinTeam)}</Text>
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ export function TeamMemberRemoveButton({
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
onSave?: () => void;
|
onSave?: () => void;
|
||||||
}) {
|
}) {
|
||||||
const { formatMessage, labels } = useMessages();
|
const { formatMessage, labels, FormattedMessage } = useMessages();
|
||||||
const { mutateAsync, isPending, error } = useDeleteQuery(`/teams/${teamId}/users/${userId}`);
|
const { mutateAsync, isPending, error } = useDeleteQuery(`/teams/${teamId}/users/${userId}`);
|
||||||
const { touch } = useModified();
|
const { touch } = useModified();
|
||||||
|
|
||||||
|
|
@ -36,9 +36,14 @@ export function TeamMemberRemoveButton({
|
||||||
<Dialog title={formatMessage(labels.removeMember)} style={{ width: 400 }}>
|
<Dialog title={formatMessage(labels.removeMember)} style={{ width: 400 }}>
|
||||||
{({ close }) => (
|
{({ close }) => (
|
||||||
<ConfirmationForm
|
<ConfirmationForm
|
||||||
message={formatMessage(messages.confirmRemove, {
|
message={
|
||||||
target: userName,
|
<FormattedMessage
|
||||||
})}
|
{...messages.confirmRemove}
|
||||||
|
values={{
|
||||||
|
target: <b>{userName}</b>,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
isLoading={isPending}
|
isLoading={isPending}
|
||||||
error={error}
|
error={error}
|
||||||
onConfirm={handleConfirm.bind(null, close)}
|
onConfirm={handleConfirm.bind(null, close)}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import Link from 'next/link';
|
||||||
import { Column, Icon, Text, Row } from '@umami/react-zen';
|
import { Column, Icon, Text, Row } from '@umami/react-zen';
|
||||||
import { useLoginQuery, useMessages, useNavigation, useTeam } from '@/components/hooks';
|
import { useLoginQuery, useMessages, useNavigation, useTeam } from '@/components/hooks';
|
||||||
import { ROLES } from '@/lib/constants';
|
import { ROLES } from '@/lib/constants';
|
||||||
import { Users, ArrowLeft } from '@/components/icons';
|
import { Users, ArrowRight } from '@/components/icons';
|
||||||
import { TeamLeaveButton } from '@/app/(main)/teams/TeamLeaveButton';
|
import { TeamLeaveButton } from '@/app/(main)/teams/TeamLeaveButton';
|
||||||
import { TeamManage } from './TeamManage';
|
import { TeamManage } from './TeamManage';
|
||||||
import { TeamEditForm } from './TeamEditForm';
|
import { TeamEditForm } from './TeamEditForm';
|
||||||
|
|
@ -34,8 +34,8 @@ export function TeamSettings({ teamId }: { teamId: string }) {
|
||||||
<>
|
<>
|
||||||
<Link href="/settings/teams">
|
<Link href="/settings/teams">
|
||||||
<Row marginTop="2" alignItems="center" gap>
|
<Row marginTop="2" alignItems="center" gap>
|
||||||
<Icon>
|
<Icon rotate={180}>
|
||||||
<ArrowLeft />
|
<ArrowRight />
|
||||||
</Icon>
|
</Icon>
|
||||||
<Text>{formatMessage(labels.teams)}</Text>
|
<Text>{formatMessage(labels.teams)}</Text>
|
||||||
</Row>
|
</Row>
|
||||||
|
|
|
||||||
|
|
@ -14,12 +14,12 @@ export function WebsiteFilterButton({
|
||||||
showText?: boolean;
|
showText?: boolean;
|
||||||
}) {
|
}) {
|
||||||
const { formatMessage, labels } = useMessages();
|
const { formatMessage, labels } = useMessages();
|
||||||
const { replaceParams, router } = useNavigation();
|
const { updateParams, router } = useNavigation();
|
||||||
|
|
||||||
const handleChange = ({ filters, segment, cohort }: any) => {
|
const handleChange = ({ filters, segment, cohort }: any) => {
|
||||||
const params = filtersArrayToObject(filters);
|
const params = filtersArrayToObject(filters);
|
||||||
|
|
||||||
const url = replaceParams({ ...params, segment, cohort });
|
const url = updateParams({ ...params, segment, cohort });
|
||||||
|
|
||||||
router.push(url);
|
router.push(url);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ export function CohortDeleteButton({
|
||||||
name: string;
|
name: string;
|
||||||
onSave?: () => void;
|
onSave?: () => void;
|
||||||
}) {
|
}) {
|
||||||
const { formatMessage, labels } = useMessages();
|
const { formatMessage, labels, FormattedMessage } = useMessages();
|
||||||
const { mutateAsync, isPending, error, touch } = useDeleteQuery(
|
const { mutateAsync, isPending, error, touch } = useDeleteQuery(
|
||||||
`/websites/${websiteId}/segments/${cohortId}`,
|
`/websites/${websiteId}/segments/${cohortId}`,
|
||||||
);
|
);
|
||||||
|
|
@ -36,9 +36,14 @@ export function CohortDeleteButton({
|
||||||
<Dialog title={formatMessage(labels.confirm)} style={{ width: 400 }}>
|
<Dialog title={formatMessage(labels.confirm)} style={{ width: 400 }}>
|
||||||
{({ close }) => (
|
{({ close }) => (
|
||||||
<ConfirmationForm
|
<ConfirmationForm
|
||||||
message={formatMessage(messages.confirmRemove, {
|
message={
|
||||||
target: name,
|
<FormattedMessage
|
||||||
})}
|
{...messages.confirmRemove}
|
||||||
|
values={{
|
||||||
|
target: <b>{name}</b>,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
isLoading={isPending}
|
isLoading={isPending}
|
||||||
error={error}
|
error={error}
|
||||||
onConfirm={handleConfirm.bind(null, close)}
|
onConfirm={handleConfirm.bind(null, close)}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import { useFormat, useMessages, useNavigation } from '@/components/hooks';
|
||||||
import { Empty } from '@/components/common/Empty';
|
import { Empty } from '@/components/common/Empty';
|
||||||
import { Avatar } from '@/components/common/Avatar';
|
import { Avatar } from '@/components/common/Avatar';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { Bolt, Eye } from '@/components/icons';
|
import { LightningSvg, Eye } from '@/components/icons';
|
||||||
import { DateDistance } from '@/components/common/DateDistance';
|
import { DateDistance } from '@/components/common/DateDistance';
|
||||||
import { TypeIcon } from '@/components/common/TypeIcon';
|
import { TypeIcon } from '@/components/common/TypeIcon';
|
||||||
|
|
||||||
|
|
@ -25,7 +25,7 @@ export function EventsTable({ data = [] }) {
|
||||||
<Link href={renderUrl(`/websites/${row.websiteId}/sessions/${row.sessionId}`)}>
|
<Link href={renderUrl(`/websites/${row.websiteId}/sessions/${row.sessionId}`)}>
|
||||||
<Avatar seed={row.sessionId} size={32} />
|
<Avatar seed={row.sessionId} size={32} />
|
||||||
</Link>
|
</Link>
|
||||||
<Icon>{row.eventName ? <Bolt /> : <Eye />}</Icon>
|
<Icon>{row.eventName ? <LightningSvg /> : <Eye />}</Icon>
|
||||||
<Text>
|
<Text>
|
||||||
{formatMessage(row.eventName ? labels.triggeredEvent : labels.viewedPage)}
|
{formatMessage(row.eventName ? labels.triggeredEvent : labels.viewedPage)}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import {
|
||||||
useTimezone,
|
useTimezone,
|
||||||
useWebsite,
|
useWebsite,
|
||||||
} from '@/components/hooks';
|
} from '@/components/hooks';
|
||||||
import { Eye, Visitor, Bolt } from '@/components/icons';
|
import { Eye, User, LightningSvg } from '@/components/icons';
|
||||||
import { BROWSERS, OS_NAMES } from '@/lib/constants';
|
import { BROWSERS, OS_NAMES } from '@/lib/constants';
|
||||||
import { stringToColor } from '@/lib/format';
|
import { stringToColor } from '@/lib/format';
|
||||||
import { useMemo, useState } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
|
|
@ -23,14 +23,14 @@ const TYPE_EVENT = 'event';
|
||||||
|
|
||||||
const icons = {
|
const icons = {
|
||||||
[TYPE_PAGEVIEW]: <Eye />,
|
[TYPE_PAGEVIEW]: <Eye />,
|
||||||
[TYPE_SESSION]: <Visitor />,
|
[TYPE_SESSION]: <User />,
|
||||||
[TYPE_EVENT]: <Bolt />,
|
[TYPE_EVENT]: <LightningSvg />,
|
||||||
};
|
};
|
||||||
|
|
||||||
export function RealtimeLog({ data }: { data: any }) {
|
export function RealtimeLog({ data }: { data: any }) {
|
||||||
const website = useWebsite();
|
const website = useWebsite();
|
||||||
const [search, setSearch] = useState('');
|
const [search, setSearch] = useState('');
|
||||||
const { formatMessage, labels, messages } = useMessages();
|
const { formatMessage, labels, messages, FormattedMessage } = useMessages();
|
||||||
const { formatValue } = useFormat();
|
const { formatValue } = useFormat();
|
||||||
const { locale } = useLocale();
|
const { locale } = useLocale();
|
||||||
const { formatTimezoneDate } = useTimezone();
|
const { formatTimezoneDate } = useTimezone();
|
||||||
|
|
@ -74,20 +74,25 @@ export function RealtimeLog({ data }: { data: any }) {
|
||||||
const { __type, eventName, urlPath, browser, os, country, device } = log;
|
const { __type, eventName, urlPath, browser, os, country, device } = log;
|
||||||
|
|
||||||
if (__type === TYPE_EVENT) {
|
if (__type === TYPE_EVENT) {
|
||||||
return formatMessage(messages.eventLog, {
|
return (
|
||||||
event: <b key="b">{eventName || formatMessage(labels.unknown)}</b>,
|
<FormattedMessage
|
||||||
url: (
|
{...messages.eventLog}
|
||||||
<a
|
values={{
|
||||||
key="a"
|
event: <b key="b">{eventName || formatMessage(labels.unknown)}</b>,
|
||||||
href={`//${website?.domain}${urlPath}`}
|
url: (
|
||||||
className={styles.link}
|
<a
|
||||||
target="_blank"
|
key="a"
|
||||||
rel="noreferrer noopener"
|
href={`//${website?.domain}${urlPath}`}
|
||||||
>
|
className={styles.link}
|
||||||
{urlPath}
|
target="_blank"
|
||||||
</a>
|
rel="noreferrer noopener"
|
||||||
),
|
>
|
||||||
});
|
{urlPath}
|
||||||
|
</a>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (__type === TYPE_PAGEVIEW) {
|
if (__type === TYPE_PAGEVIEW) {
|
||||||
|
|
@ -104,12 +109,17 @@ export function RealtimeLog({ data }: { data: any }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (__type === TYPE_SESSION) {
|
if (__type === TYPE_SESSION) {
|
||||||
return formatMessage(messages.visitorLog, {
|
return (
|
||||||
country: <b key="country">{countryNames[country] || formatMessage(labels.unknown)}</b>,
|
<FormattedMessage
|
||||||
browser: <b key="browser">{BROWSERS[browser]}</b>,
|
{...messages.visitorLog}
|
||||||
os: <b key="os">{OS_NAMES[os] || os}</b>,
|
values={{
|
||||||
device: <b key="device">{formatMessage(labels[device] || labels.unknown)}</b>,
|
country: <b key="country">{countryNames[country] || formatMessage(labels.unknown)}</b>,
|
||||||
});
|
browser: <b key="browser">{BROWSERS[browser]}</b>,
|
||||||
|
os: <b key="os">{OS_NAMES[os] || os}</b>,
|
||||||
|
device: <b key="device">{formatMessage(labels[device] || labels.unknown)}</b>,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ export function SegmentDeleteButton({
|
||||||
name: string;
|
name: string;
|
||||||
onSave?: () => void;
|
onSave?: () => void;
|
||||||
}) {
|
}) {
|
||||||
const { formatMessage, labels } = useMessages();
|
const { formatMessage, labels, FormattedMessage } = useMessages();
|
||||||
const { mutateAsync, isPending, error, touch } = useDeleteQuery(
|
const { mutateAsync, isPending, error, touch } = useDeleteQuery(
|
||||||
`/websites/${websiteId}/segments/${segmentId}`,
|
`/websites/${websiteId}/segments/${segmentId}`,
|
||||||
);
|
);
|
||||||
|
|
@ -36,9 +36,14 @@ export function SegmentDeleteButton({
|
||||||
<Dialog title={formatMessage(labels.confirm)} style={{ width: 400 }}>
|
<Dialog title={formatMessage(labels.confirm)} style={{ width: 400 }}>
|
||||||
{({ close }) => (
|
{({ close }) => (
|
||||||
<ConfirmationForm
|
<ConfirmationForm
|
||||||
message={formatMessage(messages.confirmRemove, {
|
message={
|
||||||
target: name,
|
<FormattedMessage
|
||||||
})}
|
{...messages.confirmRemove}
|
||||||
|
values={{
|
||||||
|
target: <b>{name}</b>,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
isLoading={isPending}
|
isLoading={isPending}
|
||||||
error={error}
|
error={error}
|
||||||
onConfirm={handleConfirm.bind(null, close)}
|
onConfirm={handleConfirm.bind(null, close)}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ import {
|
||||||
Dialog,
|
Dialog,
|
||||||
} from '@umami/react-zen';
|
} from '@umami/react-zen';
|
||||||
import { LoadingPanel } from '@/components/common/LoadingPanel';
|
import { LoadingPanel } from '@/components/common/LoadingPanel';
|
||||||
import { Bolt, Eye, FileText } from '@/components/icons';
|
import { LightningSvg, Eye, FileText } from '@/components/icons';
|
||||||
import { useMessages, useSessionActivityQuery, useTimezone } from '@/components/hooks';
|
import { useMessages, useSessionActivityQuery, useTimezone } from '@/components/hooks';
|
||||||
import { EventData } from '@/components/metrics/EventData';
|
import { EventData } from '@/components/metrics/EventData';
|
||||||
|
|
||||||
|
|
@ -52,7 +52,7 @@ export function SessionActivity({
|
||||||
{formatTimezoneDate(createdAt, 'pp')}
|
{formatTimezoneDate(createdAt, 'pp')}
|
||||||
</StatusLight>
|
</StatusLight>
|
||||||
<Row alignItems="center" gap="2">
|
<Row alignItems="center" gap="2">
|
||||||
<Icon>{eventName ? <Bolt /> : <Eye />}</Icon>
|
<Icon>{eventName ? <LightningSvg /> : <Eye />}</Icon>
|
||||||
<Text>
|
<Text>
|
||||||
{eventName
|
{eventName
|
||||||
? formatMessage(labels.triggeredEvent)
|
? formatMessage(labels.triggeredEvent)
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import { Heading, Icon, Row, RowProps, Text, Column } from '@umami/react-zen';
|
||||||
export function PageHeader({
|
export function PageHeader({
|
||||||
title,
|
title,
|
||||||
description,
|
description,
|
||||||
|
label,
|
||||||
icon,
|
icon,
|
||||||
showBorder = true,
|
showBorder = true,
|
||||||
children,
|
children,
|
||||||
|
|
@ -11,6 +12,7 @@ export function PageHeader({
|
||||||
}: {
|
}: {
|
||||||
title: string;
|
title: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
|
label?: ReactNode;
|
||||||
icon?: ReactNode;
|
icon?: ReactNode;
|
||||||
showBorder?: boolean;
|
showBorder?: boolean;
|
||||||
allowEdit?: boolean;
|
allowEdit?: boolean;
|
||||||
|
|
@ -26,7 +28,8 @@ export function PageHeader({
|
||||||
width="100%"
|
width="100%"
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<Column>
|
<Column gap="2">
|
||||||
|
{label}
|
||||||
<Row alignItems="center" gap="3">
|
<Row alignItems="center" gap="3">
|
||||||
{icon && (
|
{icon && (
|
||||||
<Icon size="md" color="muted">
|
<Icon size="md" color="muted">
|
||||||
|
|
@ -35,7 +38,11 @@ export function PageHeader({
|
||||||
)}
|
)}
|
||||||
{title && <Heading size="4">{title}</Heading>}
|
{title && <Heading size="4">{title}</Heading>}
|
||||||
</Row>
|
</Row>
|
||||||
{description && <Text color="muted">{description}</Text>}
|
{description && (
|
||||||
|
<Text color="muted" truncate style={{ maxWidth: 600 }} title={description}>
|
||||||
|
{description}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
</Column>
|
</Column>
|
||||||
<Row justifyContent="flex-end">{children}</Row>
|
<Row justifyContent="flex-end">{children}</Row>
|
||||||
</Row>
|
</Row>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,22 @@
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl, FormattedMessage, type MessageDescriptor } from 'react-intl';
|
||||||
import { messages, labels } from '@/components/messages';
|
import { messages, labels } from '@/components/messages';
|
||||||
|
|
||||||
export function useMessages() {
|
type FormatMessage = (
|
||||||
|
descriptor: MessageDescriptor,
|
||||||
|
values?: Record<string, string | number | boolean | null | undefined>,
|
||||||
|
opts?: any,
|
||||||
|
) => string | null;
|
||||||
|
|
||||||
|
interface UseMessages {
|
||||||
|
formatMessage: FormatMessage;
|
||||||
|
messages: typeof messages;
|
||||||
|
labels: typeof labels;
|
||||||
|
getMessage: (id: string) => string;
|
||||||
|
getErrorMessage: (error: unknown) => string | undefined;
|
||||||
|
FormattedMessage: typeof FormattedMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useMessages(): UseMessages {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
const getMessage = (id: string) => {
|
const getMessage = (id: string) => {
|
||||||
|
|
@ -21,15 +36,12 @@ export function useMessages() {
|
||||||
};
|
};
|
||||||
|
|
||||||
const formatMessage = (
|
const formatMessage = (
|
||||||
descriptor: {
|
descriptor: MessageDescriptor,
|
||||||
id: string;
|
values?: Record<string, string | number | boolean | null | undefined>,
|
||||||
defaultMessage: string;
|
|
||||||
},
|
|
||||||
values?: Record<string, string>,
|
|
||||||
opts?: any,
|
opts?: any,
|
||||||
) => {
|
) => {
|
||||||
return descriptor ? intl.formatMessage(descriptor, values, opts) : null;
|
return descriptor ? intl.formatMessage(descriptor, values, opts) : null;
|
||||||
};
|
};
|
||||||
|
|
||||||
return { formatMessage, messages, labels, getMessage, getErrorMessage };
|
return { formatMessage, messages, labels, getMessage, getErrorMessage, FormattedMessage };
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,14 @@
|
||||||
export * from 'lucide-react';
|
export * from 'lucide-react';
|
||||||
export {
|
export {
|
||||||
Logo as LogoSvg,
|
Logo as LogoSvg,
|
||||||
Bolt as BoltSvg,
|
|
||||||
Change as ChangeSvg,
|
|
||||||
Compare as CompareSvg,
|
Compare as CompareSvg,
|
||||||
Funnel as FunnelSvg,
|
Funnel as FunnelSvg,
|
||||||
Lightbulb as LightbulbSvg,
|
|
||||||
Lightning as LightningSvg,
|
Lightning as LightningSvg,
|
||||||
Location as LocationSvg,
|
Location as LocationSvg,
|
||||||
Magnet as MagnetSvg,
|
Magnet as MagnetSvg,
|
||||||
Money as MoneySvg,
|
Money as MoneySvg,
|
||||||
Network as NetworkSvg,
|
Network as NetworkSvg,
|
||||||
Path as PathSvg,
|
Path as PathSvg,
|
||||||
Tag as TagSvg,
|
|
||||||
Target as TargetSvg,
|
Target as TargetSvg,
|
||||||
AddUser as AddUserSvg,
|
AddUser as AddUserSvg,
|
||||||
} from '@/components/svg';
|
} from '@/components/svg';
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,15 @@ import {
|
||||||
MenuSection,
|
MenuSection,
|
||||||
} from '@umami/react-zen';
|
} from '@umami/react-zen';
|
||||||
import { useMessages, useLoginQuery, useNavigation, useConfig } from '@/components/hooks';
|
import { useMessages, useLoginQuery, useNavigation, useConfig } from '@/components/hooks';
|
||||||
import { LogOut, LockKeyhole, Settings, UserCircle, LifeBuoy, BookText } from '@/components/icons';
|
import {
|
||||||
|
LogOut,
|
||||||
|
LockKeyhole,
|
||||||
|
Settings,
|
||||||
|
UserCircle,
|
||||||
|
LifeBuoy,
|
||||||
|
BookText,
|
||||||
|
ExternalLink,
|
||||||
|
} from '@/components/icons';
|
||||||
import { DOCS_URL } from '@/lib/constants';
|
import { DOCS_URL } from '@/lib/constants';
|
||||||
|
|
||||||
export function SettingsButton() {
|
export function SettingsButton() {
|
||||||
|
|
@ -54,7 +62,11 @@ export function SettingsButton() {
|
||||||
id="/docs"
|
id="/docs"
|
||||||
icon={<BookText />}
|
icon={<BookText />}
|
||||||
label={formatMessage(labels.documentation)}
|
label={formatMessage(labels.documentation)}
|
||||||
/>
|
>
|
||||||
|
<Icon color="muted">
|
||||||
|
<ExternalLink />
|
||||||
|
</Icon>
|
||||||
|
</MenuItem>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
id="/settings/support"
|
id="/settings/support"
|
||||||
icon={<LifeBuoy />}
|
icon={<LifeBuoy />}
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ export function WebsiteDateFilter({
|
||||||
showButtons = true,
|
showButtons = true,
|
||||||
allowCompare,
|
allowCompare,
|
||||||
}: WebsiteDateFilterProps) {
|
}: WebsiteDateFilterProps) {
|
||||||
const { dateRange } = useDateRange(websiteId);
|
const { dateRange, saveDateRange } = useDateRange(websiteId);
|
||||||
const { value, endDate } = dateRange;
|
const { value, endDate } = dateRange;
|
||||||
const { formatMessage, labels } = useMessages();
|
const { formatMessage, labels } = useMessages();
|
||||||
const {
|
const {
|
||||||
|
|
@ -32,6 +32,7 @@ export function WebsiteDateFilter({
|
||||||
const disableForward = value === 'all' || isAfter(endDate, new Date());
|
const disableForward = value === 'all' || isAfter(endDate, new Date());
|
||||||
|
|
||||||
const handleChange = (date: string) => {
|
const handleChange = (date: string) => {
|
||||||
|
saveDateRange(date);
|
||||||
router.push(updateParams({ date, offset: undefined }));
|
router.push(updateParams({ date, offset: undefined }));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,7 @@ export function WeeklyTraffic({ websiteId }: { websiteId: string }) {
|
||||||
height="16px"
|
height="16px"
|
||||||
borderRadius="full"
|
borderRadius="full"
|
||||||
style={{ margin: '0 auto' }}
|
style={{ margin: '0 auto' }}
|
||||||
role="cell"
|
role="button"
|
||||||
>
|
>
|
||||||
<Row
|
<Row
|
||||||
backgroundColor="primary"
|
backgroundColor="primary"
|
||||||
|
|
|
||||||
|
|
@ -359,14 +359,116 @@ export const GROUPED_DOMAINS = [
|
||||||
export const MAP_FILE = '/datamaps.world.json';
|
export const MAP_FILE = '/datamaps.world.json';
|
||||||
|
|
||||||
export const ISO_COUNTRIES = {
|
export const ISO_COUNTRIES = {
|
||||||
|
ABW: 'AW',
|
||||||
|
AFG: 'AF',
|
||||||
|
AGO: 'AO',
|
||||||
|
AIA: 'AI',
|
||||||
|
ALA: 'AX',
|
||||||
|
ALB: 'AL',
|
||||||
|
AND: 'AD',
|
||||||
ANT: 'AN',
|
ANT: 'AN',
|
||||||
ARE: 'AE',
|
ARE: 'AE',
|
||||||
|
ARG: 'AR',
|
||||||
|
ARM: 'AM',
|
||||||
|
ASM: 'AS',
|
||||||
|
ATF: 'TF',
|
||||||
|
ATG: 'AG',
|
||||||
|
AUS: 'AU',
|
||||||
|
AUT: 'AT',
|
||||||
|
AZE: 'AZ',
|
||||||
|
BDI: 'BI',
|
||||||
|
BEL: 'BE',
|
||||||
|
BEN: 'BJ',
|
||||||
|
BFA: 'BF',
|
||||||
|
BGD: 'BD',
|
||||||
|
BGR: 'BG',
|
||||||
|
BHR: 'BH',
|
||||||
|
BHS: 'BS',
|
||||||
|
BIH: 'BA',
|
||||||
|
BLR: 'BY',
|
||||||
|
BLZ: 'BZ',
|
||||||
BLM: 'BL',
|
BLM: 'BL',
|
||||||
|
BMU: 'BM',
|
||||||
|
BOL: 'BO',
|
||||||
|
BRA: 'BR',
|
||||||
|
BRB: 'BB',
|
||||||
|
BRN: 'BN',
|
||||||
|
BTN: 'BT',
|
||||||
|
BVT: 'BV',
|
||||||
|
BWA: 'BW',
|
||||||
|
CAF: 'CF',
|
||||||
|
CAN: 'CA',
|
||||||
|
CCK: 'CC',
|
||||||
CHE: 'CH',
|
CHE: 'CH',
|
||||||
|
CHL: 'CL',
|
||||||
|
CHN: 'CN',
|
||||||
|
CIV: 'CI',
|
||||||
|
CMR: 'CM',
|
||||||
|
COD: 'CD',
|
||||||
|
COG: 'CG',
|
||||||
|
COK: 'CK',
|
||||||
|
COL: 'CO',
|
||||||
|
COM: 'KM',
|
||||||
|
CPV: 'CV',
|
||||||
|
CRI: 'CR',
|
||||||
|
CUB: 'CU',
|
||||||
|
CXR: 'CX',
|
||||||
|
CYM: 'KY',
|
||||||
|
CYP: 'CY',
|
||||||
|
CZE: 'CZ',
|
||||||
|
DEU: 'DE',
|
||||||
|
DJI: 'DJ',
|
||||||
|
DMA: 'DM',
|
||||||
|
DNK: 'DK',
|
||||||
|
DOM: 'DO',
|
||||||
|
DZA: 'DZ',
|
||||||
|
ECU: 'EC',
|
||||||
|
EGY: 'EG',
|
||||||
|
ERI: 'ER',
|
||||||
ESH: 'EH',
|
ESH: 'EH',
|
||||||
ESP: 'ES',
|
ESP: 'ES',
|
||||||
|
EST: 'EE',
|
||||||
|
ETH: 'ET',
|
||||||
|
FIN: 'FI',
|
||||||
|
FJI: 'FJ',
|
||||||
|
FLK: 'FK',
|
||||||
|
FRA: 'FR',
|
||||||
|
FRO: 'FO',
|
||||||
FSM: 'FM',
|
FSM: 'FM',
|
||||||
|
GAB: 'GA',
|
||||||
GBR: 'GB',
|
GBR: 'GB',
|
||||||
|
GEO: 'GE',
|
||||||
|
GGY: 'GG',
|
||||||
|
GHA: 'GH',
|
||||||
|
GIB: 'GI',
|
||||||
|
GIN: 'GN',
|
||||||
|
GLP: 'GP',
|
||||||
|
GMB: 'GM',
|
||||||
|
GNB: 'GW',
|
||||||
|
GNQ: 'GQ',
|
||||||
|
GRC: 'GR',
|
||||||
|
GRD: 'GD',
|
||||||
|
GRL: 'GL',
|
||||||
|
GTM: 'GT',
|
||||||
|
GUF: 'GF',
|
||||||
|
GUM: 'GU',
|
||||||
|
GUY: 'GY',
|
||||||
|
HKG: 'HK',
|
||||||
|
HMD: 'HM',
|
||||||
|
HND: 'HN',
|
||||||
|
HRV: 'HR',
|
||||||
|
HTI: 'HT',
|
||||||
|
HUN: 'HU',
|
||||||
|
IDN: 'ID',
|
||||||
|
IMN: 'IM',
|
||||||
|
IND: 'IN',
|
||||||
|
IOT: 'IO',
|
||||||
|
IRL: 'IE',
|
||||||
|
IRN: 'IR',
|
||||||
|
IRQ: 'IQ',
|
||||||
|
ISL: 'IS',
|
||||||
|
ISR: 'IL',
|
||||||
|
ITA: 'IT',
|
||||||
JAM: 'JM',
|
JAM: 'JM',
|
||||||
JEY: 'JE',
|
JEY: 'JE',
|
||||||
JOR: 'JO',
|
JOR: 'JO',
|
||||||
|
|
@ -374,6 +476,7 @@ export const ISO_COUNTRIES = {
|
||||||
KAZ: 'KZ',
|
KAZ: 'KZ',
|
||||||
KEN: 'KE',
|
KEN: 'KE',
|
||||||
KGZ: 'KG',
|
KGZ: 'KG',
|
||||||
|
KHM: 'KH',
|
||||||
KIR: 'KI',
|
KIR: 'KI',
|
||||||
KNA: 'KN',
|
KNA: 'KN',
|
||||||
KOR: 'KR',
|
KOR: 'KR',
|
||||||
|
|
@ -438,6 +541,7 @@ export const ISO_COUNTRIES = {
|
||||||
PRT: 'PT',
|
PRT: 'PT',
|
||||||
PRY: 'PY',
|
PRY: 'PY',
|
||||||
PSE: 'PS',
|
PSE: 'PS',
|
||||||
|
PYF: 'PF',
|
||||||
QAT: 'QA',
|
QAT: 'QA',
|
||||||
REU: 'RE',
|
REU: 'RE',
|
||||||
ROU: 'RO',
|
ROU: 'RO',
|
||||||
|
|
@ -452,13 +556,13 @@ export const ISO_COUNTRIES = {
|
||||||
SJM: 'SJ',
|
SJM: 'SJ',
|
||||||
SLB: 'SB',
|
SLB: 'SB',
|
||||||
SLE: 'SL',
|
SLE: 'SL',
|
||||||
|
SLV: 'SV',
|
||||||
SMR: 'SM',
|
SMR: 'SM',
|
||||||
SOM: 'SO',
|
SOM: 'SO',
|
||||||
SPM: 'PM',
|
SPM: 'PM',
|
||||||
SRB: 'RS',
|
SRB: 'RS',
|
||||||
SSD: 'SS',
|
|
||||||
STP: 'ST',
|
|
||||||
SUR: 'SR',
|
SUR: 'SR',
|
||||||
|
STP: 'ST',
|
||||||
SVK: 'SK',
|
SVK: 'SK',
|
||||||
SVN: 'SI',
|
SVN: 'SI',
|
||||||
SWE: 'SE',
|
SWE: 'SE',
|
||||||
|
|
@ -466,6 +570,7 @@ export const ISO_COUNTRIES = {
|
||||||
SYC: 'SC',
|
SYC: 'SC',
|
||||||
SYR: 'SY',
|
SYR: 'SY',
|
||||||
TCA: 'TC',
|
TCA: 'TC',
|
||||||
|
TCD: 'TD',
|
||||||
TGO: 'TG',
|
TGO: 'TG',
|
||||||
THA: 'TH',
|
THA: 'TH',
|
||||||
TJK: 'TJ',
|
TJK: 'TJ',
|
||||||
|
|
@ -485,8 +590,10 @@ export const ISO_COUNTRIES = {
|
||||||
URY: 'UY',
|
URY: 'UY',
|
||||||
USA: 'US',
|
USA: 'US',
|
||||||
UZB: 'UZ',
|
UZB: 'UZ',
|
||||||
|
VAT: 'VA',
|
||||||
VCT: 'VC',
|
VCT: 'VC',
|
||||||
VEN: 'VE',
|
VEN: 'VE',
|
||||||
|
VGB: 'VG',
|
||||||
VIR: 'VI',
|
VIR: 'VI',
|
||||||
VNM: 'VN',
|
VNM: 'VN',
|
||||||
VUT: 'VU',
|
VUT: 'VU',
|
||||||
|
|
|
||||||
|
|
@ -136,7 +136,7 @@ export async function getQueryFilters(
|
||||||
...dateRange,
|
...dateRange,
|
||||||
...filters,
|
...filters,
|
||||||
page: params?.page,
|
page: params?.page,
|
||||||
pageSize: params?.page ? params?.pageSize || DEFAULT_PAGE_SIZE : undefined,
|
pageSize: params?.pageSize ? params?.pageSize || DEFAULT_PAGE_SIZE : undefined,
|
||||||
orderBy: params?.orderBy,
|
orderBy: params?.orderBy,
|
||||||
sortDescending: params?.sortDescending,
|
sortDescending: params?.sortDescending,
|
||||||
search: params?.search,
|
search: params?.search,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue