@mdguggenbichler/slugbase-core 0.0.31 → 0.0.32

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.
Files changed (30) hide show
  1. package/frontend/src/components/FilterChips.tsx +5 -3
  2. package/frontend/src/components/StatCard.tsx +82 -5
  3. package/frontend/src/components/bookmarks/BookmarkCard.tsx +317 -210
  4. package/frontend/src/components/bookmarks/BookmarkTableView.tsx +47 -23
  5. package/frontend/src/components/collections/CollectionToolbar.tsx +294 -0
  6. package/frontend/src/components/collections/README.md +44 -0
  7. package/frontend/src/components/collections/index.ts +2 -0
  8. package/frontend/src/components/dashboard/DashboardHeader.tsx +16 -0
  9. package/frontend/src/components/dashboard/MostUsedTagsSection.tsx +49 -0
  10. package/frontend/src/components/dashboard/PinnedSection.tsx +110 -0
  11. package/frontend/src/components/dashboard/QuickAccessSection.tsx +120 -0
  12. package/frontend/src/components/dashboard/README.md +35 -0
  13. package/frontend/src/components/dashboard/StatsCardsRow.tsx +78 -0
  14. package/frontend/src/components/dashboard/index.ts +17 -0
  15. package/frontend/src/locales/de.json +2 -0
  16. package/frontend/src/locales/en.json +1 -0
  17. package/frontend/src/locales/es.json +2 -0
  18. package/frontend/src/locales/fr.json +2 -0
  19. package/frontend/src/locales/it.json +2 -0
  20. package/frontend/src/locales/ja.json +2 -0
  21. package/frontend/src/locales/nl.json +2 -0
  22. package/frontend/src/locales/pl.json +2 -0
  23. package/frontend/src/locales/pt.json +2 -0
  24. package/frontend/src/locales/ru.json +2 -0
  25. package/frontend/src/locales/zh.json +2 -0
  26. package/frontend/src/pages/Bookmarks.tsx +97 -214
  27. package/frontend/src/pages/Dashboard.tsx +99 -216
  28. package/frontend/src/pages/Folders.tsx +181 -251
  29. package/frontend/src/pages/Tags.tsx +87 -145
  30. package/package.json +1 -1
@@ -1,4 +1,5 @@
1
1
  import { X } from 'lucide-react';
2
+ import { Badge } from './ui/badge';
2
3
 
3
4
  export interface FilterChipItem {
4
5
  key: string;
@@ -20,9 +21,10 @@ export function FilterChips({ chips, onRemove, onClearAll, clearAllLabel, clearA
20
21
  return (
21
22
  <div className="flex flex-wrap items-center gap-2">
22
23
  {chips.map(({ key, label, ariaLabel }) => (
23
- <span
24
+ <Badge
24
25
  key={key}
25
- className="inline-flex items-center gap-1.5 pl-2.5 pr-1.5 py-1 rounded-md bg-muted text-muted-foreground text-sm border border-border"
26
+ variant="secondary"
27
+ className="inline-flex items-center gap-1.5 pl-2.5 pr-1 py-1 text-sm font-normal"
26
28
  >
27
29
  <span>{label}</span>
28
30
  <button
@@ -39,7 +41,7 @@ export function FilterChips({ chips, onRemove, onClearAll, clearAllLabel, clearA
39
41
  >
40
42
  <X className="h-3.5 w-3.5" />
41
43
  </button>
42
- </span>
44
+ </Badge>
43
45
  ))}
44
46
  <button
45
47
  type="button"
@@ -3,6 +3,18 @@ import { LucideIcon } from 'lucide-react';
3
3
  import { Card, CardContent } from './ui/card';
4
4
  import { cn } from '@/lib/utils';
5
5
 
6
+ /** Optional usage display for plan/limit (e.g. slugbase-cloud). Only rendered when used + limit are both set. */
7
+ export interface StatCardUsageProps {
8
+ used: number;
9
+ limit: number;
10
+ /** e.g. "Bookmarks used" */
11
+ labelOverride?: string;
12
+ showProgress?: boolean;
13
+ progressVariant?: 'normal' | 'warning' | 'danger';
14
+ /** Upgrade CTA; only pass in cloud when plan limits apply */
15
+ cta?: { label: string; onClick: () => void };
16
+ }
17
+
6
18
  interface StatCardProps {
7
19
  label: string;
8
20
  value: string | number;
@@ -15,21 +27,86 @@ interface StatCardProps {
15
27
  /** Tailwind classes for the icon color (e.g. text-blue-600 dark:text-blue-400) */
16
28
  iconColorClassName?: string;
17
29
  className?: string;
30
+ /** Optional secondary line below value (e.g. "+12 this week") */
31
+ secondaryLine?: string;
32
+ /** Optional usage/limit display for cloud plan; only shown when both used and limit are provided */
33
+ used?: number;
34
+ limit?: number;
35
+ labelOverride?: string;
36
+ showProgress?: boolean;
37
+ progressVariant?: 'normal' | 'warning' | 'danger';
38
+ cta?: { label: string; onClick: () => void };
18
39
  }
19
40
 
20
- export function StatCard({ label, value, icon: Icon, href, dense, iconContainerClassName, iconColorClassName, className }: StatCardProps) {
41
+ export function StatCard({
42
+ label,
43
+ value,
44
+ icon: Icon,
45
+ href,
46
+ dense,
47
+ iconContainerClassName,
48
+ iconColorClassName,
49
+ className,
50
+ secondaryLine,
51
+ used: usedProp,
52
+ limit: limitProp,
53
+ labelOverride,
54
+ showProgress = true,
55
+ progressVariant = 'normal',
56
+ cta,
57
+ }: StatCardProps) {
58
+ const hasUsage = usedProp != null && limitProp != null;
59
+ const used = hasUsage ? usedProp : 0;
60
+ const limit = hasUsage ? limitProp : 0;
61
+ const showProgressBar = hasUsage && showProgress;
62
+ const progressPercent = limit > 0 ? Math.min(100, (used / limit) * 100) : 0;
63
+
21
64
  const content = (
22
65
  <>
23
66
  <div className="flex items-center justify-between">
24
- <div>
25
- <p className={cn('font-medium text-muted-foreground', dense ? 'text-xs' : 'text-sm')}>
67
+ <div className="min-w-0 flex-1">
68
+ <p className={cn('text-muted-foreground', dense ? 'text-xs' : 'text-sm')}>
26
69
  {label}
27
70
  </p>
28
- <p className={cn('font-semibold mt-2', dense ? 'text-xl' : 'text-2xl')}>
71
+ <p className={cn('font-semibold mt-1 text-foreground', dense ? 'text-xl' : 'text-2xl')}>
29
72
  {value}
30
73
  </p>
74
+ {secondaryLine && (
75
+ <p className="mt-0.5 text-xs text-muted-foreground">{secondaryLine}</p>
76
+ )}
77
+ {hasUsage && (
78
+ <p className="mt-1 text-xs text-muted-foreground">
79
+ {labelOverride ?? label} {used} / {limit}
80
+ </p>
81
+ )}
82
+ {showProgressBar && (
83
+ <div className="mt-2 w-full overflow-hidden rounded-full bg-primary/20">
84
+ <div
85
+ className={cn(
86
+ 'h-2 transition-all',
87
+ progressVariant === 'warning' && 'bg-amber-500',
88
+ progressVariant === 'danger' && 'bg-destructive',
89
+ (progressVariant === 'normal' || !progressVariant) && 'bg-primary'
90
+ )}
91
+ style={{ width: `${progressPercent}%` }}
92
+ />
93
+ </div>
94
+ )}
95
+ {cta && (
96
+ <button
97
+ type="button"
98
+ onClick={(e) => {
99
+ e.preventDefault();
100
+ e.stopPropagation();
101
+ cta.onClick();
102
+ }}
103
+ className="mt-2 text-xs font-medium text-primary hover:underline focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 rounded"
104
+ >
105
+ {cta.label}
106
+ </button>
107
+ )}
31
108
  </div>
32
- <div className={cn('rounded-lg', iconContainerClassName ?? 'bg-muted', dense ? 'p-2' : 'p-3')}>
109
+ <div className={cn('shrink-0 rounded-lg', iconContainerClassName ?? 'bg-muted', dense ? 'p-2' : 'p-3')}>
33
110
  <Icon className={cn(iconColorClassName ?? 'text-muted-foreground', dense ? 'h-5 w-5' : 'h-6 w-6')} />
34
111
  </div>
35
112
  </div>