@apptimate/ui 3.1.0 → 3.2.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/package.json CHANGED
@@ -1,19 +1,19 @@
1
1
  {
2
2
  "name": "@apptimate/ui",
3
- "version": "3.1.0",
3
+ "version": "3.2.0",
4
4
  "main": "src/index.tsx",
5
5
  "types": "src/index.tsx",
6
6
  "dependencies": {
7
7
  "@apptimate/core-lib": "*",
8
+ "apexcharts": "^5.15.2",
8
9
  "class-variance-authority": "^0.7.1",
9
10
  "clsx": "^2.1.1",
10
11
  "framer-motion": "^12.38.0",
11
12
  "lucide-react": "^1.14.0",
12
13
  "react": "^19.2.5",
14
+ "react-apexcharts": "^2.1.1",
13
15
  "recharts": "^2.15.4",
14
- "tailwind-merge": "^3.5.0",
15
- "apexcharts": "^5.11.0",
16
- "react-apexcharts": "^2.1.0"
16
+ "tailwind-merge": "^3.5.0"
17
17
  },
18
18
  "publishConfig": {
19
19
  "access": "public"
@@ -21,4 +21,4 @@
21
21
  "peerDependencies": {
22
22
  "next": "^16.0.0"
23
23
  }
24
- }
24
+ }
@@ -19,6 +19,8 @@ export type DashboardMenuConfig = {
19
19
  id: string;
20
20
  label: string;
21
21
  path: string;
22
+ icon?: React.ReactNode;
23
+ badge?: React.ReactNode;
22
24
  }[]
23
25
  }[]
24
26
  };
@@ -47,7 +49,8 @@ export function DashboardLayout({
47
49
  onProfile,
48
50
  organizations = [],
49
51
  selectedOrganization,
50
- onOrganizationChange
52
+ onOrganizationChange,
53
+ extraHeaderContent
51
54
  }: {
52
55
  children: React.ReactNode;
53
56
  logo?: React.ReactNode;
@@ -60,6 +63,7 @@ export function DashboardLayout({
60
63
  organizations?: OrganizationConfig[];
61
64
  selectedOrganization?: OrganizationConfig | null;
62
65
  onOrganizationChange?: (org: OrganizationConfig) => void;
66
+ extraHeaderContent?: React.ReactNode;
63
67
  }) {
64
68
  const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
65
69
  const [isOrgModalOpen, setIsOrgModalOpen] = useState(false);
@@ -178,7 +182,9 @@ export function DashboardLayout({
178
182
  <div className="flex-1 overflow-y-auto px-4 space-y-6">
179
183
  {activeMenu.groups.map((group) => (
180
184
  <div key={group.id}>
181
- <h3 className="px-2 text-xs font-bold text-gray-400 uppercase tracking-wider mb-2">{group.label}</h3>
185
+ {group.label && (
186
+ <h3 className="px-2 text-xs font-bold text-gray-400 uppercase tracking-wider mb-2 empty:hidden">{group.label}</h3>
187
+ )}
182
188
  <nav className="flex flex-col gap-1">
183
189
  {group.items.map((item) => {
184
190
  // Find the longest matching path in this group to avoid parent paths being active
@@ -191,18 +197,28 @@ export function DashboardLayout({
191
197
  : !externalPaths.some(ext => item.path.startsWith(ext));
192
198
  const href = basePath && isInternal ? item.path.replace(basePath, "") || "/" : item.path;
193
199
 
194
- const className = `px-3 py-2 rounded-lg text-sm transition-colors block ${isItemActive
200
+ const className = `px-3 py-2 rounded-lg text-sm transition-colors flex items-center justify-between ${isItemActive
195
201
  ? "bg-[#F4F5F7] text-[#2D3142] font-semibold"
196
202
  : "text-gray-500 font-medium hover:bg-gray-50"
197
203
  }`;
198
204
 
205
+ const content = (
206
+ <>
207
+ <div className="flex items-center gap-3">
208
+ {item.icon && <span className={`${isItemActive ? 'text-[#2D3142]' : 'text-gray-400'}`}>{item.icon}</span>}
209
+ <span>{item.label}</span>
210
+ </div>
211
+ {item.badge && <div>{item.badge}</div>}
212
+ </>
213
+ );
214
+
199
215
  return isInternal ? (
200
216
  <Link key={item.id} href={href} className={className}>
201
- {item.label}
217
+ {content}
202
218
  </Link>
203
219
  ) : (
204
220
  <a key={item.id} href={href} className={className}>
205
- {item.label}
221
+ {content}
206
222
  </a>
207
223
  );
208
224
  })}
@@ -469,7 +485,8 @@ export function DashboardLayout({
469
485
  </div>
470
486
  </div>
471
487
 
472
- <style dangerouslySetInnerHTML={{ __html: `
488
+ <style dangerouslySetInnerHTML={{
489
+ __html: `
473
490
  @keyframes orgModalIn {
474
491
  from {
475
492
  opacity: 0;
@@ -48,7 +48,8 @@ export interface BarChartProps {
48
48
  colors?: string[];
49
49
  horizontal?: boolean;
50
50
  stacked?: boolean;
51
- yFormatter?: (val: number) => string;
51
+ xFormatter?: (val: number) => string;
52
+ yFormatter?: (val: number | string) => string;
52
53
  tooltipYFormatter?: (val: number) => string;
53
54
  showToolbar?: boolean;
54
55
  showLegend?: boolean;
@@ -99,7 +100,7 @@ export function AreaChart({
99
100
  grid: defaultGrid,
100
101
  tooltip: {
101
102
  theme: "dark", shared: true, intersect: false,
102
- x: tooltipXFormatter ? { formatter: tooltipXFormatter } : undefined,
103
+ ...(tooltipXFormatter ? { x: { formatter: tooltipXFormatter } } : {}),
103
104
  y: { formatter: tooltipYFormatter || yFormatter },
104
105
  style: { fontSize: "12px" },
105
106
  },
@@ -162,7 +163,7 @@ export function LineChart({
162
163
  export function BarChart({
163
164
  series, categories, height = 350, colors,
164
165
  horizontal = false, stacked = false,
165
- yFormatter = (v) => String(v),
166
+ xFormatter, yFormatter = (v) => String(v),
166
167
  tooltipYFormatter,
167
168
  showToolbar = false, showLegend = true,
168
169
  borderRadius = 6, columnWidth = "50%", className = "",
@@ -179,7 +180,7 @@ export function BarChart({
179
180
  dataLabels: { enabled: false },
180
181
  xaxis: {
181
182
  categories,
182
- labels: { style: defaultAxisStyle },
183
+ labels: { style: defaultAxisStyle, formatter: xFormatter },
183
184
  axisBorder: { show: false }, axisTicks: { show: false },
184
185
  },
185
186
  yaxis: { labels: { style: defaultAxisStyle, formatter: yFormatter } },
@@ -220,3 +221,55 @@ export function DonutChart({
220
221
  </div>
221
222
  );
222
223
  }
224
+
225
+ // ── Heatmap Chart ────────────────────────────────────────────────────
226
+ export interface HeatmapChartProps {
227
+ series: { name: string; data: { x: string; y: number }[] }[];
228
+ height?: number;
229
+ colors?: string[];
230
+ className?: string;
231
+ tooltipYFormatter?: (val: number) => string;
232
+ }
233
+
234
+ export function HeatmapChart({
235
+ series, height = 350, colors = ["#3D5A80"], className = "", tooltipYFormatter
236
+ }: HeatmapChartProps) {
237
+ const options = useMemo<ApexCharts.ApexOptions>(() => ({
238
+ chart: { type: "heatmap", height, fontFamily: defaultFont, ...defaultAnimations, toolbar: { show: false } },
239
+ colors,
240
+ plotOptions: {
241
+ heatmap: {
242
+ shadeIntensity: 0.5,
243
+ radius: 4,
244
+ useFillColorAsStroke: false,
245
+ colorScale: {
246
+ ranges: [
247
+ { from: 0, to: 0, color: "#f1f5f9", name: "0" },
248
+ { from: 1, to: 5, color: "#cbd5e1", name: "1-5" },
249
+ { from: 6, to: 15, color: "#94a3b8", name: "6-15" },
250
+ { from: 16, to: 50, color: "#64748b", name: "16-50" },
251
+ { from: 51, to: 1000, color: colors[0], name: "50+" }
252
+ ]
253
+ }
254
+ }
255
+ },
256
+ dataLabels: { enabled: false },
257
+ xaxis: {
258
+ labels: { style: defaultAxisStyle },
259
+ axisBorder: { show: false }, axisTicks: { show: false }
260
+ },
261
+ yaxis: { labels: { style: defaultAxisStyle } },
262
+ grid: defaultGrid,
263
+ tooltip: {
264
+ theme: "dark",
265
+ y: { formatter: tooltipYFormatter || ((val) => String(val)) },
266
+ style: { fontSize: "12px" },
267
+ },
268
+ }), [colors, height, tooltipYFormatter]);
269
+
270
+ return (
271
+ <div className={`w-full ${className}`}>
272
+ <ApexChart options={options} series={series} type="heatmap" height={height} width="100%" />
273
+ </div>
274
+ );
275
+ }