improve overview and other things
This commit is contained in:
@@ -160,7 +160,7 @@ export function EventListItem(props: EventListItemProps) {
|
||||
|
||||
return (
|
||||
<ExpandableListItem
|
||||
className={cn(meta?.conversion && 'ring-2 ring-primary-500')}
|
||||
className={cn(meta?.conversion && `bg-${meta.color}-50`)}
|
||||
title={
|
||||
<button onClick={() => setEvents((p) => uniq([...p, name]))}>
|
||||
{name.split('_').join(' ')}
|
||||
@@ -192,42 +192,40 @@ export function EventListItem(props: EventListItemProps) {
|
||||
}
|
||||
image={<EventIcon name={name} meta={meta} projectId={projectId} />}
|
||||
>
|
||||
<div className="p-2">
|
||||
<div className="bg-gradient-to-tr from-slate-100 to-white rounded-md">
|
||||
{propertiesList.length > 0 && (
|
||||
<div className="p-4 flex flex-col gap-4">
|
||||
<div className="font-medium">Your properties</div>
|
||||
<div className="flex flex-wrap gap-x-4 gap-y-2">
|
||||
{propertiesList.map((item) => (
|
||||
<KeyValue
|
||||
key={item.name}
|
||||
name={item.name}
|
||||
value={item.value}
|
||||
onClick={() => {
|
||||
setFilter(
|
||||
`properties.${item.name}`,
|
||||
item.value ? String(item.value) : '',
|
||||
'is'
|
||||
);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="p-4 flex flex-col gap-4">
|
||||
<div className="font-medium">Properties</div>
|
||||
<div className="bg-white p-4">
|
||||
{propertiesList.length > 0 && (
|
||||
<div className="flex flex-col gap-4 mb-6">
|
||||
<div className="font-medium">Your properties</div>
|
||||
<div className="flex flex-wrap gap-x-4 gap-y-2">
|
||||
{keyValueList.map((item) => (
|
||||
{propertiesList.map((item) => (
|
||||
<KeyValue
|
||||
onClick={() => item.onClick?.()}
|
||||
key={item.name}
|
||||
name={item.name}
|
||||
value={item.value}
|
||||
onClick={() => {
|
||||
setFilter(
|
||||
`properties.${item.name}`,
|
||||
item.value ? String(item.value) : '',
|
||||
'is'
|
||||
);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="font-medium">Properties</div>
|
||||
<div className="flex flex-wrap gap-x-4 gap-y-2">
|
||||
{keyValueList.map((item) => (
|
||||
<KeyValue
|
||||
onClick={() => item.onClick?.()}
|
||||
key={item.name}
|
||||
name={item.name}
|
||||
value={item.value}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ExpandableListItem>
|
||||
|
||||
@@ -1,17 +1,23 @@
|
||||
'use client';
|
||||
|
||||
import { Suspense } from 'react';
|
||||
import { Fragment, Suspense } from 'react';
|
||||
import { FullPageEmptyState } from '@/components/FullPageEmptyState';
|
||||
import { Pagination } from '@/components/Pagination';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { useCursor } from '@/hooks/useCursor';
|
||||
import { useEventQueryFilters } from '@/hooks/useEventQueryFilters';
|
||||
import { isSameDay } from 'date-fns';
|
||||
import { GanttChartIcon } from 'lucide-react';
|
||||
|
||||
import type { IServiceCreateEventPayload } from '@mixan/db';
|
||||
|
||||
import { EventListItem } from './event-list-item';
|
||||
|
||||
function showDateHeader(a: Date, b?: Date) {
|
||||
if (!b) return true;
|
||||
return !isSameDay(a, b);
|
||||
}
|
||||
|
||||
interface EventListProps {
|
||||
data: IServiceCreateEventPayload[];
|
||||
count: number;
|
||||
@@ -55,8 +61,18 @@ export function EventList({ data, count }: EventListProps) {
|
||||
take={50}
|
||||
/>
|
||||
<div className="flex flex-col gap-4 my-4">
|
||||
{data.map((item) => (
|
||||
<EventListItem key={item.id} {...item} />
|
||||
{data.map((item, index, list) => (
|
||||
<Fragment key={item.id}>
|
||||
{showDateHeader(
|
||||
item.createdAt,
|
||||
list[index - 1]?.createdAt
|
||||
) && (
|
||||
<div className="font-medium text-xs [&:not(:first-child)]:mt-12">
|
||||
{item.createdAt.toLocaleDateString()}
|
||||
</div>
|
||||
)}
|
||||
<EventListItem {...item} />
|
||||
</Fragment>
|
||||
))}
|
||||
</div>
|
||||
<Pagination
|
||||
|
||||
@@ -37,23 +37,19 @@ export function ProfileListItem(props: ProfileListItemProps) {
|
||||
>
|
||||
<>
|
||||
{properties && (
|
||||
<div className="p-2">
|
||||
<div className="bg-gradient-to-tr from-slate-100 to-white rounded-md">
|
||||
<div className="p-4 flex flex-col gap-4">
|
||||
<div className="font-medium">Properties</div>
|
||||
<div className="flex flex-wrap gap-x-4 gap-y-2">
|
||||
{Object.entries(properties)
|
||||
.filter(([, value]) => !!value)
|
||||
.map(([key, value]) => (
|
||||
<KeyValue
|
||||
onClick={() => setFilter(`properties.${key}`, value)}
|
||||
key={key}
|
||||
name={key}
|
||||
value={value}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-white p-4 flex flex-col gap-4">
|
||||
<div className="font-medium">Properties</div>
|
||||
<div className="flex flex-wrap gap-x-4 gap-y-2">
|
||||
{Object.entries(properties)
|
||||
.filter(([, value]) => !!value)
|
||||
.map(([key, value]) => (
|
||||
<KeyValue
|
||||
onClick={() => setFilter(`properties.${key}`, value)}
|
||||
key={key}
|
||||
name={key}
|
||||
value={value}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -31,7 +31,7 @@ export default async function Page({ params: { id } }: PageProps) {
|
||||
|
||||
return (
|
||||
<div className="p-4 md:p-16 bg-gradient-to-tl from-blue-950 to-blue-600">
|
||||
<div className="max-w-4xl mx-auto">
|
||||
<div className="max-w-6xl mx-auto">
|
||||
<div className="flex justify-between items-end mb-4">
|
||||
<div className="leading-none">
|
||||
<span className="text-white mb-4">{organization?.name}</span>
|
||||
|
||||
@@ -38,6 +38,7 @@ export function ExpandableListItem({
|
||||
variant="secondary"
|
||||
size="icon"
|
||||
onClick={() => setOpen((p) => !p)}
|
||||
className="bg-black/5 hover:bg-black/10"
|
||||
>
|
||||
<ChevronUp
|
||||
size={20}
|
||||
|
||||
@@ -146,7 +146,7 @@ function Wrapper({ open, children, count }: WrapperProps) {
|
||||
</div>
|
||||
</div>
|
||||
<div className="max-h-[150px] aspect-[5/1] flex flex-1 gap-0.5 md:gap-2 items-end w-full relative">
|
||||
<div className="absolute top-0 right-0 text-xs text-muted-foreground">
|
||||
<div className="absolute -top-3 right-0 text-xs text-muted-foreground">
|
||||
NOW
|
||||
</div>
|
||||
{/* <div className="md:absolute top-0 left-0 md:card md:p-4 mr-2 md:bg-white/90 z-50"> */}
|
||||
|
||||
@@ -196,7 +196,10 @@ export default function OverviewMetrics({ projectId }: OverviewMetricsProps) {
|
||||
{reports.map((report, index) => (
|
||||
<button
|
||||
key={index}
|
||||
className="relative col-span-3 md:col-span-2 lg:col-span-1 group"
|
||||
className={cn(
|
||||
'relative col-span-3 md:col-span-2 lg:col-span-1 group transition-all scale-95',
|
||||
index === metric && 'shadow-md rounded-xl scale-105 z-10'
|
||||
)}
|
||||
onClick={() => {
|
||||
setMetric(index);
|
||||
}}
|
||||
|
||||
@@ -21,7 +21,7 @@ export function KeyValue({ href, onClick, name, value }: KeyValueProps) {
|
||||
)}
|
||||
{...{ href, onClick }}
|
||||
>
|
||||
<div className="p-1 px-2 bg-slate-50">{name}</div>
|
||||
<div className="p-1 px-2 bg-black/5">{name}</div>
|
||||
<div
|
||||
className={cn(
|
||||
'p-1 px-2 font-mono text-blue-700 bg-white whitespace-nowrap overflow-hidden text-ellipsis shadow-[inset_0_0_0_1px_#fff]',
|
||||
@@ -45,7 +45,7 @@ export function KeyValueSubtle({ href, onClick, name, value }: KeyValueProps) {
|
||||
<div className="text-gray-400">{name}</div>
|
||||
<div
|
||||
className={cn(
|
||||
'bg-slate-100 rounded p-0.5 px-1 sm:p-1 sm:px-2 text-gray-600 whitespace-nowrap overflow-hidden text-ellipsis',
|
||||
'bg-black/5 rounded p-0.5 px-1 sm:p-1 sm:px-2 text-gray-600 whitespace-nowrap overflow-hidden text-ellipsis',
|
||||
clickable && 'group-hover:underline'
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -14,56 +14,47 @@ const colors = [
|
||||
'#7856ff',
|
||||
];
|
||||
|
||||
const twColors = [
|
||||
'rose',
|
||||
'pink',
|
||||
'fuchsia',
|
||||
'purple',
|
||||
'violet',
|
||||
'indigo',
|
||||
'blue',
|
||||
'sky',
|
||||
'cyan',
|
||||
'teal',
|
||||
'emerald',
|
||||
'green',
|
||||
'lime',
|
||||
'yellow',
|
||||
'amber',
|
||||
'orange',
|
||||
'red',
|
||||
'stone',
|
||||
'neutral',
|
||||
'zinc',
|
||||
'grey',
|
||||
'slate',
|
||||
];
|
||||
const twColorVariants = ['50', '100', '200', '700'];
|
||||
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
const config = {
|
||||
safelist: [
|
||||
...colors.flatMap((color) =>
|
||||
['text', 'bg'].map((prefix) => `${prefix}-chart-${color}`)
|
||||
),
|
||||
'bg-rose-200',
|
||||
'text-rose-700',
|
||||
'bg-pink-200',
|
||||
'text-pink-700',
|
||||
'bg-fuchsia-200',
|
||||
'text-fuchsia-700',
|
||||
'bg-purple-200',
|
||||
'text-purple-700',
|
||||
'bg-violet-200',
|
||||
'text-violet-700',
|
||||
'bg-indigo-200',
|
||||
'text-indigo-700',
|
||||
'bg-blue-200',
|
||||
'text-blue-700',
|
||||
'bg-sky-200',
|
||||
'text-sky-700',
|
||||
'bg-cyan-200',
|
||||
'text-cyan-700',
|
||||
'bg-teal-200',
|
||||
'text-teal-700',
|
||||
'bg-emerald-200',
|
||||
'text-emerald-700',
|
||||
'bg-green-200',
|
||||
'text-green-700',
|
||||
'bg-lime-200',
|
||||
'text-lime-700',
|
||||
'bg-yellow-200',
|
||||
'text-yellow-700',
|
||||
'bg-amber-200',
|
||||
'text-amber-700',
|
||||
'bg-orange-200',
|
||||
'text-orange-700',
|
||||
'bg-red-200',
|
||||
'text-red-700',
|
||||
'bg-stone-200',
|
||||
'text-stone-700',
|
||||
'bg-neutral-200',
|
||||
'text-neutral-700',
|
||||
'bg-zinc-200',
|
||||
'text-zinc-700',
|
||||
'bg-grey-200',
|
||||
'text-grey-700',
|
||||
'bg-slate-200',
|
||||
'text-slate-700',
|
||||
...twColors.flatMap((color) => {
|
||||
return twColorVariants.flatMap((variant) => {
|
||||
return [
|
||||
`text-${color}-${variant}`,
|
||||
`bg-${color}-${variant}`,
|
||||
`border-${color}-${variant}`,
|
||||
];
|
||||
});
|
||||
}),
|
||||
],
|
||||
content: [
|
||||
'./pages/**/*.{ts,tsx}',
|
||||
|
||||
Reference in New Issue
Block a user