@checkstack/ui 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +35 -0
- package/package.json +1 -1
- package/src/components/Card.tsx +19 -2
- package/src/components/DateRangeFilter.tsx +33 -21
- package/src/components/Page.tsx +17 -8
- package/src/components/PageLayout.tsx +24 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,40 @@
|
|
|
1
1
|
# @checkstack/ui
|
|
2
2
|
|
|
3
|
+
## 0.5.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 223081d: Add icon support to PageLayout and improve mobile responsiveness
|
|
8
|
+
|
|
9
|
+
**PageLayout Icons:**
|
|
10
|
+
|
|
11
|
+
- Added required `icon` prop to `PageLayout` and `PageHeader` components that accepts a Lucide icon component reference
|
|
12
|
+
- Icons are rendered with consistent `h-6 w-6 text-primary` styling
|
|
13
|
+
- Updated all page components to include appropriate icons in their headers
|
|
14
|
+
|
|
15
|
+
**Mobile Layout Improvements:**
|
|
16
|
+
|
|
17
|
+
- Standardized responsive padding in main app shell (`p-3` on mobile, `p-6` on desktop)
|
|
18
|
+
- Added `CardHeaderRow` component for mobile-safe card headers with proper wrapping
|
|
19
|
+
- Improved `DateRangeFilter` responsive behavior with vertical stacking on mobile
|
|
20
|
+
- Migrated pages to use `PageLayout` for consistent responsive behavior
|
|
21
|
+
|
|
22
|
+
## 0.4.1
|
|
23
|
+
|
|
24
|
+
### Patch Changes
|
|
25
|
+
|
|
26
|
+
- 538e45d: Fixed 24-hour date range not returning correct data and improved chart display
|
|
27
|
+
|
|
28
|
+
- Fixed missing `endDate` parameter in raw data queries causing data to extend beyond selected time range
|
|
29
|
+
- Fixed incorrect 24-hour date calculation using `setHours()` - now uses `date-fns` `subHours()` for correct date math
|
|
30
|
+
- Refactored `DateRangePreset` from string union to enum for improved type safety and IDE support
|
|
31
|
+
- Exported `getPresetRange` function for reuse across components
|
|
32
|
+
- Changed chart x-axis domain from `["auto", "auto"]` to `["dataMin", "dataMax"]` to remove padding gaps
|
|
33
|
+
|
|
34
|
+
- Updated dependencies [db1f56f]
|
|
35
|
+
- @checkstack/common@0.6.0
|
|
36
|
+
- @checkstack/frontend-api@0.3.3
|
|
37
|
+
|
|
3
38
|
## 0.4.0
|
|
4
39
|
|
|
5
40
|
### Minor Changes
|
package/package.json
CHANGED
package/src/components/Card.tsx
CHANGED
|
@@ -8,7 +8,7 @@ export const Card = ({
|
|
|
8
8
|
<div
|
|
9
9
|
className={cn(
|
|
10
10
|
"rounded-lg border border-border bg-card text-card-foreground shadow-sm",
|
|
11
|
-
className
|
|
11
|
+
className,
|
|
12
12
|
)}
|
|
13
13
|
{...props}
|
|
14
14
|
/>
|
|
@@ -21,6 +21,23 @@ export const CardHeader = ({
|
|
|
21
21
|
<div className={cn("flex flex-col space-y-1.5 p-6", className)} {...props} />
|
|
22
22
|
);
|
|
23
23
|
|
|
24
|
+
/**
|
|
25
|
+
* A row layout for card headers with title and actions.
|
|
26
|
+
* Provides mobile-friendly defaults with flex-wrap and gap.
|
|
27
|
+
*/
|
|
28
|
+
export const CardHeaderRow = ({
|
|
29
|
+
className,
|
|
30
|
+
...props
|
|
31
|
+
}: React.HTMLAttributes<HTMLDivElement>) => (
|
|
32
|
+
<div
|
|
33
|
+
className={cn(
|
|
34
|
+
"flex flex-wrap items-center justify-between gap-3",
|
|
35
|
+
className,
|
|
36
|
+
)}
|
|
37
|
+
{...props}
|
|
38
|
+
/>
|
|
39
|
+
);
|
|
40
|
+
|
|
24
41
|
export const CardTitle = ({
|
|
25
42
|
className,
|
|
26
43
|
...props
|
|
@@ -28,7 +45,7 @@ export const CardTitle = ({
|
|
|
28
45
|
<h3
|
|
29
46
|
className={cn(
|
|
30
47
|
"text-2xl font-semibold leading-none tracking-tight",
|
|
31
|
-
className
|
|
48
|
+
className,
|
|
32
49
|
)}
|
|
33
50
|
{...props}
|
|
34
51
|
/>
|
|
@@ -9,7 +9,12 @@ export interface DateRange {
|
|
|
9
9
|
endDate: Date;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
export
|
|
12
|
+
export enum DateRangePreset {
|
|
13
|
+
Last24Hours = "24h",
|
|
14
|
+
Last7Days = "7d",
|
|
15
|
+
Last30Days = "30d",
|
|
16
|
+
Custom = "custom",
|
|
17
|
+
}
|
|
13
18
|
|
|
14
19
|
export interface DateRangeFilterProps {
|
|
15
20
|
value: DateRange;
|
|
@@ -17,26 +22,30 @@ export interface DateRangeFilterProps {
|
|
|
17
22
|
className?: string;
|
|
18
23
|
}
|
|
19
24
|
|
|
20
|
-
const PRESETS: Array<{
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
+
const PRESETS: Array<{
|
|
26
|
+
id: DateRangePreset;
|
|
27
|
+
label: string;
|
|
28
|
+
shortLabel: string;
|
|
29
|
+
}> = [
|
|
30
|
+
{ id: DateRangePreset.Last24Hours, label: "Last 24h", shortLabel: "24h" },
|
|
31
|
+
{ id: DateRangePreset.Last7Days, label: "Last 7 days", shortLabel: "7d" },
|
|
32
|
+
{ id: DateRangePreset.Last30Days, label: "Last 30 days", shortLabel: "30d" },
|
|
33
|
+
{ id: DateRangePreset.Custom, label: "Custom", shortLabel: "Custom" },
|
|
25
34
|
];
|
|
26
35
|
|
|
27
|
-
function getPresetRange(preset: DateRangePreset): DateRange {
|
|
36
|
+
export function getPresetRange(preset: DateRangePreset): DateRange {
|
|
28
37
|
const now = new Date();
|
|
29
38
|
switch (preset) {
|
|
30
|
-
case
|
|
39
|
+
case DateRangePreset.Last24Hours: {
|
|
31
40
|
return { startDate: subHours(now, 24), endDate: now };
|
|
32
41
|
}
|
|
33
|
-
case
|
|
42
|
+
case DateRangePreset.Last7Days: {
|
|
34
43
|
return { startDate: startOfDay(subDays(now, 7)), endDate: now };
|
|
35
44
|
}
|
|
36
|
-
case
|
|
45
|
+
case DateRangePreset.Last30Days: {
|
|
37
46
|
return { startDate: startOfDay(subDays(now, 30)), endDate: now };
|
|
38
47
|
}
|
|
39
|
-
case
|
|
48
|
+
case DateRangePreset.Custom: {
|
|
40
49
|
return { startDate: startOfDay(subDays(now, 7)), endDate: now };
|
|
41
50
|
}
|
|
42
51
|
}
|
|
@@ -48,10 +57,10 @@ function detectPreset(range: DateRange): DateRangePreset {
|
|
|
48
57
|
const diffHours = diffMs / (1000 * 60 * 60);
|
|
49
58
|
const diffDays = diffHours / 24;
|
|
50
59
|
|
|
51
|
-
if (diffHours <= 25 && diffHours >= 23) return
|
|
52
|
-
if (diffDays <= 8 && diffDays >= 6) return
|
|
53
|
-
if (diffDays <= 31 && diffDays >= 29) return
|
|
54
|
-
return
|
|
60
|
+
if (diffHours <= 25 && diffHours >= 23) return DateRangePreset.Last24Hours;
|
|
61
|
+
if (diffDays <= 8 && diffDays >= 6) return DateRangePreset.Last7Days;
|
|
62
|
+
if (diffDays <= 31 && diffDays >= 29) return DateRangePreset.Last30Days;
|
|
63
|
+
return DateRangePreset.Custom;
|
|
55
64
|
}
|
|
56
65
|
|
|
57
66
|
/**
|
|
@@ -63,10 +72,12 @@ export const DateRangeFilter: React.FC<DateRangeFilterProps> = ({
|
|
|
63
72
|
className,
|
|
64
73
|
}) => {
|
|
65
74
|
const activePreset = useMemo(() => detectPreset(value), [value]);
|
|
66
|
-
const [showCustom, setShowCustom] = useState(
|
|
75
|
+
const [showCustom, setShowCustom] = useState(
|
|
76
|
+
activePreset === DateRangePreset.Custom,
|
|
77
|
+
);
|
|
67
78
|
|
|
68
79
|
const handlePresetClick = (preset: DateRangePreset) => {
|
|
69
|
-
if (preset ===
|
|
80
|
+
if (preset === DateRangePreset.Custom) {
|
|
70
81
|
setShowCustom(true);
|
|
71
82
|
} else {
|
|
72
83
|
setShowCustom(false);
|
|
@@ -81,21 +92,22 @@ export const DateRangeFilter: React.FC<DateRangeFilterProps> = ({
|
|
|
81
92
|
<span className="text-sm font-medium text-muted-foreground">
|
|
82
93
|
Time range:
|
|
83
94
|
</span>
|
|
84
|
-
<div className="flex gap-1">
|
|
95
|
+
<div className="flex gap-1 flex-wrap">
|
|
85
96
|
{PRESETS.map((preset) => (
|
|
86
97
|
<Button
|
|
87
98
|
key={preset.id}
|
|
88
99
|
variant={
|
|
89
100
|
activePreset === preset.id && !showCustom
|
|
90
101
|
? "primary"
|
|
91
|
-
: preset.id ===
|
|
102
|
+
: preset.id === DateRangePreset.Custom && showCustom
|
|
92
103
|
? "primary"
|
|
93
104
|
: "outline"
|
|
94
105
|
}
|
|
95
106
|
size="sm"
|
|
96
107
|
onClick={() => handlePresetClick(preset.id)}
|
|
97
108
|
>
|
|
98
|
-
{preset.
|
|
109
|
+
<span className="sm:hidden">{preset.shortLabel}</span>
|
|
110
|
+
<span className="hidden sm:inline">{preset.label}</span>
|
|
99
111
|
</Button>
|
|
100
112
|
))}
|
|
101
113
|
</div>
|
|
@@ -132,5 +144,5 @@ export const DateRangeFilter: React.FC<DateRangeFilterProps> = ({
|
|
|
132
144
|
|
|
133
145
|
/** Create a default date range (last 7 days) */
|
|
134
146
|
export function getDefaultDateRange(): DateRange {
|
|
135
|
-
return getPresetRange(
|
|
147
|
+
return getPresetRange(DateRangePreset.Last7Days);
|
|
136
148
|
}
|
package/src/components/Page.tsx
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
+
import type { LucideIcon } from "lucide-react";
|
|
2
3
|
import { cn } from "../utils";
|
|
3
4
|
|
|
4
5
|
interface PageProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
@@ -14,33 +15,37 @@ export const Page = React.forwardRef<HTMLDivElement, PageProps>(
|
|
|
14
15
|
>
|
|
15
16
|
{children}
|
|
16
17
|
</div>
|
|
17
|
-
)
|
|
18
|
+
),
|
|
18
19
|
);
|
|
19
20
|
Page.displayName = "Page";
|
|
20
21
|
|
|
21
22
|
interface PageHeaderProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
22
23
|
title: string;
|
|
23
24
|
subtitle?: string;
|
|
25
|
+
icon: LucideIcon;
|
|
24
26
|
actions?: React.ReactNode;
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
export const PageHeader = React.forwardRef<HTMLDivElement, PageHeaderProps>(
|
|
28
|
-
({ className, title, subtitle, actions, ...props }, ref) => (
|
|
30
|
+
({ className, title, subtitle, icon: Icon, actions, ...props }, ref) => (
|
|
29
31
|
<div
|
|
30
32
|
ref={ref}
|
|
31
33
|
className={cn(
|
|
32
|
-
"flex flex-col md:flex-row items-center justify-between
|
|
33
|
-
className
|
|
34
|
+
"flex flex-col md:flex-row items-center justify-between py-3 pb-2 md:py-6 md:pb-2",
|
|
35
|
+
className,
|
|
34
36
|
)}
|
|
35
37
|
{...props}
|
|
36
38
|
>
|
|
37
39
|
<div className="space-y-1">
|
|
38
|
-
<
|
|
40
|
+
<div className="flex items-center gap-3">
|
|
41
|
+
<Icon className="h-6 w-6 text-primary" />
|
|
42
|
+
<h2 className="text-2xl font-bold tracking-tight">{title}</h2>
|
|
43
|
+
</div>
|
|
39
44
|
{subtitle && <p className="text-muted-foreground">{subtitle}</p>}
|
|
40
45
|
</div>
|
|
41
46
|
{actions && <div className="flex items-center space-x-2">{actions}</div>}
|
|
42
47
|
</div>
|
|
43
|
-
)
|
|
48
|
+
),
|
|
44
49
|
);
|
|
45
50
|
PageHeader.displayName = "PageHeader";
|
|
46
51
|
|
|
@@ -50,9 +55,13 @@ interface PageContentProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
|
50
55
|
|
|
51
56
|
export const PageContent = React.forwardRef<HTMLDivElement, PageContentProps>(
|
|
52
57
|
({ className, children, ...props }, ref) => (
|
|
53
|
-
<div
|
|
58
|
+
<div
|
|
59
|
+
ref={ref}
|
|
60
|
+
className={cn("flex-1 py-3 pt-2 md:py-6 md:pt-2", className)}
|
|
61
|
+
{...props}
|
|
62
|
+
>
|
|
54
63
|
{children}
|
|
55
64
|
</div>
|
|
56
|
-
)
|
|
65
|
+
),
|
|
57
66
|
);
|
|
58
67
|
PageContent.displayName = "PageContent";
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
+
import type { LucideIcon } from "lucide-react";
|
|
2
3
|
import {
|
|
3
4
|
Page,
|
|
4
5
|
PageHeader,
|
|
@@ -10,6 +11,7 @@ import {
|
|
|
10
11
|
interface PageLayoutProps {
|
|
11
12
|
title: string;
|
|
12
13
|
subtitle?: string;
|
|
14
|
+
icon: LucideIcon;
|
|
13
15
|
actions?: React.ReactNode;
|
|
14
16
|
loading?: boolean;
|
|
15
17
|
allowed?: boolean;
|
|
@@ -31,6 +33,7 @@ interface PageLayoutProps {
|
|
|
31
33
|
export const PageLayout: React.FC<PageLayoutProps> = ({
|
|
32
34
|
title,
|
|
33
35
|
subtitle,
|
|
36
|
+
icon,
|
|
34
37
|
actions,
|
|
35
38
|
loading,
|
|
36
39
|
allowed,
|
|
@@ -46,7 +49,12 @@ export const PageLayout: React.FC<PageLayoutProps> = ({
|
|
|
46
49
|
if (isLoading) {
|
|
47
50
|
return (
|
|
48
51
|
<Page>
|
|
49
|
-
<PageHeader
|
|
52
|
+
<PageHeader
|
|
53
|
+
title={title}
|
|
54
|
+
subtitle={subtitle}
|
|
55
|
+
icon={icon}
|
|
56
|
+
actions={actions}
|
|
57
|
+
/>
|
|
50
58
|
<PageContent>
|
|
51
59
|
<div className="flex justify-center py-12">
|
|
52
60
|
<LoadingSpinner />
|
|
@@ -60,7 +68,12 @@ export const PageLayout: React.FC<PageLayoutProps> = ({
|
|
|
60
68
|
if (allowed === false) {
|
|
61
69
|
return (
|
|
62
70
|
<Page>
|
|
63
|
-
<PageHeader
|
|
71
|
+
<PageHeader
|
|
72
|
+
title={title}
|
|
73
|
+
subtitle={subtitle}
|
|
74
|
+
icon={icon}
|
|
75
|
+
actions={actions}
|
|
76
|
+
/>
|
|
64
77
|
<PageContent>
|
|
65
78
|
<AccessDenied />
|
|
66
79
|
</PageContent>
|
|
@@ -70,10 +83,17 @@ export const PageLayout: React.FC<PageLayoutProps> = ({
|
|
|
70
83
|
|
|
71
84
|
return (
|
|
72
85
|
<Page>
|
|
73
|
-
<PageHeader
|
|
86
|
+
<PageHeader
|
|
87
|
+
title={title}
|
|
88
|
+
subtitle={subtitle}
|
|
89
|
+
icon={icon}
|
|
90
|
+
actions={actions}
|
|
91
|
+
/>
|
|
74
92
|
<PageContent>
|
|
75
93
|
<div
|
|
76
|
-
className={
|
|
94
|
+
className={
|
|
95
|
+
maxWidth === "full" ? "space-y-6" : `max-w-${maxWidth} space-y-6`
|
|
96
|
+
}
|
|
77
97
|
>
|
|
78
98
|
{children}
|
|
79
99
|
</div>
|