@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 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@checkstack/ui",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "dependencies": {
@@ -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 type DateRangePreset = "24h" | "7d" | "30d" | "custom";
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<{ id: DateRangePreset; label: string }> = [
21
- { id: "24h", label: "Last 24h" },
22
- { id: "7d", label: "Last 7 days" },
23
- { id: "30d", label: "Last 30 days" },
24
- { id: "custom", label: "Custom" },
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 "24h": {
39
+ case DateRangePreset.Last24Hours: {
31
40
  return { startDate: subHours(now, 24), endDate: now };
32
41
  }
33
- case "7d": {
42
+ case DateRangePreset.Last7Days: {
34
43
  return { startDate: startOfDay(subDays(now, 7)), endDate: now };
35
44
  }
36
- case "30d": {
45
+ case DateRangePreset.Last30Days: {
37
46
  return { startDate: startOfDay(subDays(now, 30)), endDate: now };
38
47
  }
39
- case "custom": {
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 "24h";
52
- if (diffDays <= 8 && diffDays >= 6) return "7d";
53
- if (diffDays <= 31 && diffDays >= 29) return "30d";
54
- return "custom";
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(activePreset === "custom");
75
+ const [showCustom, setShowCustom] = useState(
76
+ activePreset === DateRangePreset.Custom,
77
+ );
67
78
 
68
79
  const handlePresetClick = (preset: DateRangePreset) => {
69
- if (preset === "custom") {
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 === "custom" && showCustom
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.label}
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("7d");
147
+ return getPresetRange(DateRangePreset.Last7Days);
136
148
  }
@@ -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 p-6 pb-2",
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
- <h2 className="text-2xl font-bold tracking-tight">{title}</h2>
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 ref={ref} className={cn("flex-1 p-6 pt-2", className)} {...props}>
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 title={title} subtitle={subtitle} actions={actions} />
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 title={title} subtitle={subtitle} actions={actions} />
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 title={title} subtitle={subtitle} actions={actions} />
86
+ <PageHeader
87
+ title={title}
88
+ subtitle={subtitle}
89
+ icon={icon}
90
+ actions={actions}
91
+ />
74
92
  <PageContent>
75
93
  <div
76
- className={maxWidth === "full" ? "" : `max-w-${maxWidth} space-y-6`}
94
+ className={
95
+ maxWidth === "full" ? "space-y-6" : `max-w-${maxWidth} space-y-6`
96
+ }
77
97
  >
78
98
  {children}
79
99
  </div>