mirror of
https://github.com/umami-software/umami.git
synced 2026-02-08 06:37:18 +01:00
Updated styling of goals report.
This commit is contained in:
parent
f259130202
commit
9f43ae67ef
12 changed files with 93 additions and 85 deletions
|
|
@ -6,11 +6,24 @@
|
|||
|
||||
.item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
width: 100%;
|
||||
flex-wrap: nowrap;
|
||||
padding: 12px;
|
||||
border: 1px solid var(--base400);
|
||||
border-radius: var(--border-radius);
|
||||
box-shadow: 1px 1px 1px var(--base400);
|
||||
}
|
||||
|
||||
.value {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.icon,
|
||||
.close {
|
||||
height: 1.5rem;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,18 +24,21 @@ export function ParameterList({ children }: ParameterListProps) {
|
|||
const Item = ({
|
||||
children,
|
||||
className,
|
||||
icon,
|
||||
onClick,
|
||||
onRemove,
|
||||
}: {
|
||||
children?: ReactNode;
|
||||
className?: string;
|
||||
icon?: ReactNode;
|
||||
onClick?: () => void;
|
||||
onRemove?: () => void;
|
||||
}) => {
|
||||
return (
|
||||
<div className={classNames(styles.item, className)} onClick={onClick}>
|
||||
{children}
|
||||
<Icon onClick={onRemove}>
|
||||
{icon && <Icon className={styles.icon}>{icon}</Icon>}
|
||||
<div className={styles.value}>{children}</div>
|
||||
<Icon className={styles.close} onClick={onRemove}>
|
||||
<Icons.Close />
|
||||
</Icon>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import Funnel from 'assets/funnel.svg';
|
|||
import Lightbulb from 'assets/lightbulb.svg';
|
||||
import Magnet from 'assets/magnet.svg';
|
||||
import Tag from 'assets/tag.svg';
|
||||
import Target from 'assets/target.svg';
|
||||
import styles from './ReportTemplates.module.css';
|
||||
import { useMessages, useTeamUrl } from 'components/hooks';
|
||||
|
||||
|
|
@ -41,7 +42,7 @@ export function ReportTemplates({ showHeader = true }: { showHeader?: boolean })
|
|||
title: formatMessage(labels.goals),
|
||||
description: formatMessage(labels.goalsDescription),
|
||||
url: renderTeamUrl('/reports/goals'),
|
||||
icon: <Tag />,
|
||||
icon: <Target />,
|
||||
},
|
||||
];
|
||||
|
||||
|
|
|
|||
|
|
@ -5,10 +5,6 @@
|
|||
width: 100%;
|
||||
}
|
||||
|
||||
.type {
|
||||
color: var(--base700);
|
||||
}
|
||||
|
||||
.value {
|
||||
display: flex;
|
||||
align-self: center;
|
||||
|
|
|
|||
|
|
@ -93,12 +93,10 @@ export function FunnelParameters() {
|
|||
<PopupTrigger key={index}>
|
||||
<ParameterList.Item
|
||||
className={styles.item}
|
||||
icon={step.type === 'url' ? <Icons.Eye /> : <Icons.Bolt />}
|
||||
onRemove={() => handleRemoveStep(index)}
|
||||
>
|
||||
<div className={styles.value}>
|
||||
<div className={styles.type}>
|
||||
<Icon>{step.type === 'url' ? <Icons.Eye /> : <Icons.Bolt />}</Icon>
|
||||
</div>
|
||||
<div>{step.value}</div>
|
||||
</div>
|
||||
</ParameterList.Item>
|
||||
|
|
|
|||
|
|
@ -76,11 +76,9 @@ export function GoalsAddForm({
|
|||
className={styles.input}
|
||||
value={goal?.toString()}
|
||||
onChange={e => handleChange(e, setGoal)}
|
||||
autoFocus={true}
|
||||
autoComplete="off"
|
||||
onKeyDown={handleKeyDown}
|
||||
/>
|
||||
.
|
||||
</Flexbox>
|
||||
</FormRow>
|
||||
<FormRow>
|
||||
|
|
|
|||
|
|
@ -1,27 +1,15 @@
|
|||
.chart {
|
||||
display: grid;
|
||||
gap: 30px;
|
||||
}
|
||||
|
||||
.num {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 100%;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
color: var(--base800);
|
||||
background: var(--base100);
|
||||
z-index: 1;
|
||||
.goal {
|
||||
padding-bottom: 40px;
|
||||
border-bottom: 1px solid var(--base400);
|
||||
}
|
||||
|
||||
.step {
|
||||
display: grid;
|
||||
grid-template-columns: max-content 1fr;
|
||||
column-gap: 30px;
|
||||
position: relative;
|
||||
padding-bottom: 60px;
|
||||
.goal:last-child {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.card {
|
||||
|
|
@ -36,28 +24,12 @@
|
|||
gap: 20px;
|
||||
}
|
||||
|
||||
.bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
background: var(--base900);
|
||||
height: 30px;
|
||||
border-radius: 5px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.label {
|
||||
color: var(--base600);
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.track {
|
||||
background-color: var(--base100);
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.item {
|
||||
font-size: 20px;
|
||||
color: var(--base900);
|
||||
|
|
@ -73,7 +45,7 @@
|
|||
text-transform: lowercase;
|
||||
}
|
||||
|
||||
.visitors {
|
||||
.value {
|
||||
color: var(--base900);
|
||||
font-size: 24px;
|
||||
font-weight: 900;
|
||||
|
|
@ -85,3 +57,39 @@
|
|||
font-weight: 700;
|
||||
align-self: flex-end;
|
||||
}
|
||||
|
||||
.total {
|
||||
color: var(--base700);
|
||||
}
|
||||
|
||||
.bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
background: var(--base900);
|
||||
height: 10px;
|
||||
border-radius: 5px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.bar.level1 {
|
||||
background: var(--red800);
|
||||
}
|
||||
.bar.level2 {
|
||||
background: var(--orange200);
|
||||
}
|
||||
.bar.level3 {
|
||||
background: var(--orange400);
|
||||
}
|
||||
.bar.level4 {
|
||||
background: var(--orange600);
|
||||
}
|
||||
.bar.level5 {
|
||||
background: var(--green600);
|
||||
}
|
||||
|
||||
.track {
|
||||
background-color: var(--base100);
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,9 +14,10 @@ export function GoalsChart({ className }: { className?: string; isLoading?: bool
|
|||
return (
|
||||
<div className={classNames(styles.chart, className)}>
|
||||
{data?.map(({ type, value, goal, result }, index: number) => {
|
||||
const percent = result > goal ? 100 : (result / goal) * 100;
|
||||
|
||||
return (
|
||||
<div key={index} className={styles.step}>
|
||||
<div className={styles.num}>{index + 1}</div>
|
||||
<div key={index} className={styles.goal}>
|
||||
<div className={styles.card}>
|
||||
<div className={styles.header}>
|
||||
<span className={styles.label}>
|
||||
|
|
@ -25,20 +26,24 @@ export function GoalsChart({ className }: { className?: string; isLoading?: bool
|
|||
<span className={styles.item}>{value}</span>
|
||||
</div>
|
||||
<div className={styles.metric}>
|
||||
<div>
|
||||
<span className={styles.visitors}>{formatLongNumber(result)}</span>
|
||||
{formatMessage(labels.visitors)}
|
||||
</div>
|
||||
<div>
|
||||
<span className={styles.visitors}>{formatLongNumber(goal)}</span>
|
||||
{formatMessage(labels.goal)}
|
||||
<div className={styles.value}>
|
||||
{formatLongNumber(result)}
|
||||
<span className={styles.total}> / {formatLongNumber(goal)}</span>
|
||||
</div>
|
||||
<div className={styles.percent}>{((result / goal) * 100).toFixed(2)}%</div>
|
||||
</div>
|
||||
<div className={styles.track}>
|
||||
<div
|
||||
className={styles.bar}
|
||||
style={{ width: `${result > goal ? 100 : (result / goal) * 100}%` }}
|
||||
className={classNames(
|
||||
classNames(styles.bar, {
|
||||
[styles.level1]: percent <= 20,
|
||||
[styles.level2]: percent > 20 && percent <= 40,
|
||||
[styles.level3]: percent > 40 && percent <= 60,
|
||||
[styles.level4]: percent > 60 && percent <= 80,
|
||||
[styles.level5]: percent > 80,
|
||||
}),
|
||||
)}
|
||||
style={{ width: `${percent}%` }}
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,18 +1,7 @@
|
|||
.item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.type {
|
||||
color: var(--base700);
|
||||
}
|
||||
|
||||
.value {
|
||||
display: flex;
|
||||
align-self: center;
|
||||
gap: 20px;
|
||||
width: 100%;
|
||||
margin-bottom: 8px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.goal {
|
||||
|
|
@ -22,5 +11,4 @@
|
|||
font-weight: 900;
|
||||
padding: 2px 8px;
|
||||
border-radius: 5px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -83,15 +83,12 @@ export function GoalsParameters() {
|
|||
return (
|
||||
<PopupTrigger key={index}>
|
||||
<ParameterList.Item
|
||||
className={styles.item}
|
||||
icon={goal.type === 'url' ? <Icons.Eye /> : <Icons.Bolt />}
|
||||
onRemove={() => handleRemoveGoals(index)}
|
||||
>
|
||||
<div className={styles.value}>
|
||||
<div className={styles.type}>
|
||||
<Icon>{goal.type === 'url' ? <Icons.Eye /> : <Icons.Bolt />}</Icon>
|
||||
</div>
|
||||
<div>{goal.value}</div>
|
||||
<div className={styles.goal}>{formatNumber(goal.goal)}</div>
|
||||
<div className={styles.value}>{goal.value}</div>
|
||||
<div className={styles.goal}>
|
||||
{formatMessage(labels.goal)}: {formatNumber(goal.goal)}
|
||||
</div>
|
||||
</ParameterList.Item>
|
||||
<Popup alignment="start">
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import Report from '../[reportId]/Report';
|
|||
import ReportHeader from '../[reportId]/ReportHeader';
|
||||
import ReportMenu from '../[reportId]/ReportMenu';
|
||||
import ReportBody from '../[reportId]/ReportBody';
|
||||
import Goals from 'assets/funnel.svg';
|
||||
import Target from 'assets/target.svg';
|
||||
import { REPORT_TYPES } from 'lib/constants';
|
||||
|
||||
const defaultParameters = {
|
||||
|
|
@ -15,7 +15,7 @@ const defaultParameters = {
|
|||
export default function GoalsReport({ reportId }: { reportId?: string }) {
|
||||
return (
|
||||
<Report reportId={reportId} defaultParameters={defaultParameters}>
|
||||
<ReportHeader icon={<Goals />} />
|
||||
<ReportHeader icon={<Target />} />
|
||||
<ReportMenu>
|
||||
<GoalsParameters />
|
||||
</ReportMenu>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue