mirror of
https://github.com/kremalicious/umami.git
synced 2025-02-06 01:15:42 +01:00
Updated avatar colors and events table.
This commit is contained in:
parent
9c32057841
commit
3262ea0285
@ -2,7 +2,7 @@ import { GridTable, GridColumn } from 'react-basics';
|
|||||||
import { useLocale, useMessages } from 'components/hooks';
|
import { useLocale, useMessages } from 'components/hooks';
|
||||||
import Empty from 'components/common/Empty';
|
import Empty from 'components/common/Empty';
|
||||||
import { formatDistance } from 'date-fns';
|
import { formatDistance } from 'date-fns';
|
||||||
import Profile from 'components/common/Profile';
|
import Avatar from 'components/common/Avatar';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
|
|
||||||
export function EventsTable({ data = [] }) {
|
export function EventsTable({ data = [] }) {
|
||||||
@ -15,18 +15,23 @@ export function EventsTable({ data = [] }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<GridTable data={data}>
|
<GridTable data={data}>
|
||||||
<GridColumn name="id" label="ID" />
|
<GridColumn name="session" label={formatMessage(labels.session)} width={'100px'}>
|
||||||
<GridColumn name="session" label={formatMessage(labels.session)}>
|
|
||||||
{row => (
|
{row => (
|
||||||
<Link href={`/sessions/`}>
|
<Link href={`/sessions/`}>
|
||||||
<Profile seed={row.sessionId} size={64} />
|
<Avatar seed={row.sessionId} size={64} />
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
</GridColumn>
|
</GridColumn>
|
||||||
<GridColumn name="eventName" label={formatMessage(labels.event)}>
|
<GridColumn name="event" label={formatMessage(labels.event)}>
|
||||||
{row => formatMessage(row.eventName ? labels.triggeredEvent : labels.viewedPage)}
|
{row => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{formatMessage(row.eventName ? labels.triggeredEvent : labels.viewedPage)}
|
||||||
|
<strong>{row.eventName}</strong>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}}
|
||||||
</GridColumn>
|
</GridColumn>
|
||||||
<GridColumn name="eventName" label={formatMessage(labels.name)} />
|
|
||||||
<GridColumn name="urlPath" label={formatMessage(labels.path)} />
|
<GridColumn name="urlPath" label={formatMessage(labels.path)} />
|
||||||
<GridColumn name="created" label={formatMessage(labels.created)}>
|
<GridColumn name="created" label={formatMessage(labels.created)}>
|
||||||
{row =>
|
{row =>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { GridColumn, GridTable, useBreakpoint } from 'react-basics';
|
import { GridColumn, GridTable, useBreakpoint } from 'react-basics';
|
||||||
import { useFormat, useLocale, useMessages } from 'components/hooks';
|
import { useFormat, useLocale, useMessages } from 'components/hooks';
|
||||||
import Profile from 'components/common/Profile';
|
import Avatar from 'components/common/Avatar';
|
||||||
import styles from './SessionsTable.module.css';
|
import styles from './SessionsTable.module.css';
|
||||||
import { formatDate } from 'lib/date';
|
import { formatDate } from 'lib/date';
|
||||||
|
|
||||||
@ -16,7 +16,7 @@ export function SessionsTable({ data = [] }: { data: any[]; showDomain?: boolean
|
|||||||
<GridColumn name="id" label="ID" width="300px">
|
<GridColumn name="id" label="ID" width="300px">
|
||||||
{row => (
|
{row => (
|
||||||
<Link href={`sessions/${row.id}`} className={styles.link}>
|
<Link href={`sessions/${row.id}`} className={styles.link}>
|
||||||
<Profile key={row.id} seed={row.id} size={64} />
|
<Avatar key={row.id} seed={row.id} size={64} />
|
||||||
{row.id}
|
{row.id}
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
|
@ -3,7 +3,7 @@ import WebsiteHeader from '../../WebsiteHeader';
|
|||||||
import SessionInfo from './SessionInfo';
|
import SessionInfo from './SessionInfo';
|
||||||
import { useWebsiteSession } from 'components/hooks';
|
import { useWebsiteSession } from 'components/hooks';
|
||||||
import { Loading } from 'react-basics';
|
import { Loading } from 'react-basics';
|
||||||
import Profile from 'components/common/Profile';
|
import Avatar from 'components/common/Avatar';
|
||||||
import { SessionActivity } from './SessionActivity';
|
import { SessionActivity } from './SessionActivity';
|
||||||
import { SessionStats } from './SessionStats';
|
import { SessionStats } from './SessionStats';
|
||||||
import { SessionData } from './SessionData';
|
import { SessionData } from './SessionData';
|
||||||
@ -27,7 +27,7 @@ export default function SessionDetailsPage({
|
|||||||
<WebsiteHeader websiteId={websiteId} />
|
<WebsiteHeader websiteId={websiteId} />
|
||||||
<div className={styles.page}>
|
<div className={styles.page}>
|
||||||
<div className={styles.sidebar}>
|
<div className={styles.sidebar}>
|
||||||
<Profile seed={data?.id} />
|
<Avatar seed={data?.id} />
|
||||||
<SessionInfo data={data} />
|
<SessionInfo data={data} />
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.content}>
|
<div className={styles.content}>
|
||||||
|
@ -1,71 +1,23 @@
|
|||||||
import md5 from 'md5';
|
import { useMemo } from 'react';
|
||||||
import { colord, extend } from 'colord';
|
import { createAvatar } from '@dicebear/core';
|
||||||
import harmoniesPlugin from 'colord/plugins/harmonies';
|
import { lorelei } from '@dicebear/collection';
|
||||||
import mixPlugin from 'colord/plugins/mix';
|
import { getColor, getPastel } from 'lib/colors';
|
||||||
|
|
||||||
extend([harmoniesPlugin, mixPlugin]);
|
const lib = lorelei;
|
||||||
|
|
||||||
const harmonies = [
|
function Avatar({ seed, size = 128, ...props }: { seed: string; size?: number }) {
|
||||||
//'analogous',
|
const backgroundColor = getPastel(getColor(seed), 4);
|
||||||
//'complementary',
|
|
||||||
'double-split-complementary',
|
|
||||||
//'rectangle',
|
|
||||||
'split-complementary',
|
|
||||||
'tetradic',
|
|
||||||
//'triadic',
|
|
||||||
];
|
|
||||||
|
|
||||||
const color = (value: string, invert: boolean = false) => {
|
const avatar = useMemo(() => {
|
||||||
const c = colord(value.startsWith('#') ? value : `#${value}`);
|
return createAvatar(lib, {
|
||||||
|
...props,
|
||||||
|
seed,
|
||||||
|
size,
|
||||||
|
backgroundColor: [backgroundColor],
|
||||||
|
}).toDataUri();
|
||||||
|
}, []);
|
||||||
|
|
||||||
if (invert && c.isDark()) {
|
return <img src={avatar} alt="Avatar" style={{ borderRadius: '100%' }} />;
|
||||||
return c.invert();
|
|
||||||
}
|
|
||||||
|
|
||||||
return c;
|
|
||||||
};
|
|
||||||
|
|
||||||
const remix = (hash: string) => {
|
|
||||||
const a = hash.substring(0, 6);
|
|
||||||
const b = hash.substring(6, 12);
|
|
||||||
const c = hash.substring(12, 18);
|
|
||||||
const d = hash.substring(18, 24);
|
|
||||||
const e = hash.substring(24, 30);
|
|
||||||
const f = hash.substring(30, 32);
|
|
||||||
|
|
||||||
const base = [b, c, d, e]
|
|
||||||
.reduce((acc, val) => {
|
|
||||||
return acc.mix(color(val), 0.05);
|
|
||||||
}, color(a))
|
|
||||||
.saturate(0.1)
|
|
||||||
.toHex();
|
|
||||||
|
|
||||||
const harmony = pick(parseInt(f, 16), harmonies);
|
|
||||||
|
|
||||||
return color(base, true)
|
|
||||||
.harmonies(harmony)
|
|
||||||
.map(c => c.toHex());
|
|
||||||
};
|
|
||||||
|
|
||||||
const pick = (num: number, arr: any[]) => {
|
|
||||||
return arr[num % arr.length];
|
|
||||||
};
|
|
||||||
|
|
||||||
export function Avatar({ value }: { value: string }) {
|
|
||||||
const hash = md5(value);
|
|
||||||
const colors = remix(hash);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<svg viewBox="0 0 100 100">
|
|
||||||
<defs>
|
|
||||||
<linearGradient id={`color-${hash}`} gradientTransform="rotate(90)">
|
|
||||||
<stop offset="0%" stopColor={colors[1]} />
|
|
||||||
<stop offset="100%" stopColor={colors[2]} />
|
|
||||||
</linearGradient>
|
|
||||||
</defs>
|
|
||||||
<circle cx="50" cy="50" r="50" fill={`url(#color-${hash})`} />
|
|
||||||
</svg>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Avatar;
|
export default Avatar;
|
||||||
|
@ -1,41 +0,0 @@
|
|||||||
import { useMemo } from 'react';
|
|
||||||
import { createAvatar } from '@dicebear/core';
|
|
||||||
import { lorelei } from '@dicebear/collection';
|
|
||||||
import md5 from 'md5';
|
|
||||||
|
|
||||||
const lib = lorelei;
|
|
||||||
|
|
||||||
function convertToPastel(hexColor: string, pastelFactor: number = 0.5) {
|
|
||||||
// Remove the # if present
|
|
||||||
hexColor = hexColor.replace(/^#/, '');
|
|
||||||
|
|
||||||
// Convert hex to RGB
|
|
||||||
let r = parseInt(hexColor.substring(0, 2), 16);
|
|
||||||
let g = parseInt(hexColor.substring(2, 4), 16);
|
|
||||||
let b = parseInt(hexColor.substring(4, 6), 16);
|
|
||||||
|
|
||||||
// Calculate pastel version (mix with white)
|
|
||||||
//const pastelFactor = 0.5; // Adjust this value to control pastel intensity
|
|
||||||
|
|
||||||
r = Math.floor((r + 255 * pastelFactor) / (1 + pastelFactor));
|
|
||||||
g = Math.floor((g + 255 * pastelFactor) / (1 + pastelFactor));
|
|
||||||
b = Math.floor((b + 255 * pastelFactor) / (1 + pastelFactor));
|
|
||||||
|
|
||||||
// Convert back to hex
|
|
||||||
return `#${((1 << 24) | (r << 16) | (g << 8) | b).toString(16).slice(1)}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function Profile({ seed, size = 128, ...props }: { seed: string; size?: number }) {
|
|
||||||
const avatar = useMemo(() => {
|
|
||||||
return createAvatar(lib, {
|
|
||||||
...props,
|
|
||||||
seed,
|
|
||||||
size,
|
|
||||||
backgroundColor: [convertToPastel(md5(seed).substring(0, 6), 2).replace(/^#/, '')],
|
|
||||||
}).toDataUri();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return <img src={avatar} alt="Avatar" style={{ borderRadius: '100%' }} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Profile;
|
|
45
src/lib/colors.ts
Normal file
45
src/lib/colors.ts
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import md5 from 'md5';
|
||||||
|
|
||||||
|
export const pick = (num: number, arr: any[]) => {
|
||||||
|
return arr[num % arr.length];
|
||||||
|
};
|
||||||
|
|
||||||
|
export function clamp(num: number, min: number, max: number) {
|
||||||
|
return num < min ? min : num > max ? max : num;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function hex2RGB(color: string, min: number = 0, max: number = 255) {
|
||||||
|
const c = color.replace(/^#/, '');
|
||||||
|
const diff = max - min;
|
||||||
|
|
||||||
|
const normalize = (num: number) => {
|
||||||
|
return Math.floor((num / 255) * diff + min);
|
||||||
|
};
|
||||||
|
|
||||||
|
const r = normalize(parseInt(c.substring(0, 2), 16));
|
||||||
|
const g = normalize(parseInt(c.substring(2, 4), 16));
|
||||||
|
const b = normalize(parseInt(c.substring(4, 6), 16));
|
||||||
|
|
||||||
|
return { r, g, b };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function rgb2Hex(r: number, g: number, b: number, prefix = '') {
|
||||||
|
return `${prefix}${r.toString(16)}${g.toString(16)}${b.toString(16)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getPastel(color: string, factor: number = 0.5, prefix = '') {
|
||||||
|
let { r, g, b } = hex2RGB(color);
|
||||||
|
|
||||||
|
r = Math.floor((r + 255 * factor) / (1 + factor));
|
||||||
|
g = Math.floor((g + 255 * factor) / (1 + factor));
|
||||||
|
b = Math.floor((b + 255 * factor) / (1 + factor));
|
||||||
|
|
||||||
|
return rgb2Hex(r, g, b, prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getColor(seed: string, min: number = 0, max: number = 255) {
|
||||||
|
const color = md5(seed).substring(0, 6);
|
||||||
|
const { r, g, b } = hex2RGB(color, min, max);
|
||||||
|
|
||||||
|
return rgb2Hex(r, g, b);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user