@licklist/design 0.78.21 → 0.78.25

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 (186) hide show
  1. package/dist/assets/Trend-Down.svg +3 -0
  2. package/dist/assets/Trend-Up.svg +3 -0
  3. package/dist/auth/Authorizer.d.ts.map +1 -1
  4. package/dist/auth/Authorizer.js +47 -12
  5. package/dist/index.d.ts +1 -0
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +5 -0
  8. package/dist/v2/components/EntityHeader/EntityHeader.d.ts +13 -0
  9. package/dist/v2/components/EntityHeader/EntityHeader.d.ts.map +1 -0
  10. package/dist/v2/components/EntityHeader/EntityHeader.js +85 -0
  11. package/dist/v2/components/EntityHeader/EntityHeader.scss.js +6 -0
  12. package/dist/v2/components/EntityHeader/index.d.ts +2 -0
  13. package/dist/v2/components/EntityHeader/index.d.ts.map +1 -0
  14. package/dist/v2/components/NPSScore/NPSScore.d.ts +18 -0
  15. package/dist/v2/components/NPSScore/NPSScore.d.ts.map +1 -0
  16. package/dist/v2/components/NPSScore/index.d.ts +3 -0
  17. package/dist/v2/components/NPSScore/index.d.ts.map +1 -0
  18. package/dist/v2/components/Select/Select.d.ts +10 -0
  19. package/dist/v2/components/Select/Select.d.ts.map +1 -0
  20. package/dist/v2/components/Select/index.d.ts +3 -0
  21. package/dist/v2/components/Select/index.d.ts.map +1 -0
  22. package/dist/v2/components/Tooltip/Tooltip.d.ts +21 -0
  23. package/dist/v2/components/Tooltip/Tooltip.d.ts.map +1 -0
  24. package/dist/v2/components/Tooltip/Tooltip.js +103 -0
  25. package/dist/v2/components/Tooltip/Tooltip.scss.js +6 -0
  26. package/dist/v2/components/Tooltip/index.d.ts +2 -0
  27. package/dist/v2/components/Tooltip/index.d.ts.map +1 -0
  28. package/dist/v2/components/UserAvatar/UserAvatar.d.ts +12 -0
  29. package/dist/v2/components/UserAvatar/UserAvatar.d.ts.map +1 -0
  30. package/dist/v2/components/UserAvatar/UserAvatar.js +77 -0
  31. package/dist/v2/components/UserAvatar/UserAvatar.scss.js +6 -0
  32. package/dist/v2/components/UserAvatar/index.d.ts +2 -0
  33. package/dist/v2/components/UserAvatar/index.d.ts.map +1 -0
  34. package/dist/v2/components/UserPanel/UserPanel.d.ts +17 -0
  35. package/dist/v2/components/UserPanel/UserPanel.d.ts.map +1 -0
  36. package/dist/v2/components/UserPanel/UserPanel.js +168 -0
  37. package/dist/v2/components/UserPanel/UserPanel.scss.js +6 -0
  38. package/dist/v2/components/UserPanel/index.d.ts +3 -0
  39. package/dist/v2/components/UserPanel/index.d.ts.map +1 -0
  40. package/dist/v2/dashboard-analytics/blog-posts/Blog.d.ts +15 -0
  41. package/dist/v2/dashboard-analytics/blog-posts/Blog.d.ts.map +1 -0
  42. package/dist/v2/dashboard-analytics/blog-posts/index.d.ts +3 -0
  43. package/dist/v2/dashboard-analytics/blog-posts/index.d.ts.map +1 -0
  44. package/dist/v2/dashboard-analytics/chart/Chart.d.ts +21 -0
  45. package/dist/v2/dashboard-analytics/chart/Chart.d.ts.map +1 -0
  46. package/dist/v2/dashboard-analytics/chart/index.d.ts +3 -0
  47. package/dist/v2/dashboard-analytics/chart/index.d.ts.map +1 -0
  48. package/dist/v2/dashboard-analytics/dashboard/Dashboard.d.ts +57 -0
  49. package/dist/v2/dashboard-analytics/dashboard/Dashboard.d.ts.map +1 -0
  50. package/dist/v2/dashboard-analytics/dashboard/index.d.ts +3 -0
  51. package/dist/v2/dashboard-analytics/dashboard/index.d.ts.map +1 -0
  52. package/dist/v2/dashboard-analytics/index.d.ts +13 -0
  53. package/dist/v2/dashboard-analytics/index.d.ts.map +1 -0
  54. package/dist/v2/dashboard-analytics/metric-card/MetricCard.d.ts +17 -0
  55. package/dist/v2/dashboard-analytics/metric-card/MetricCard.d.ts.map +1 -0
  56. package/dist/v2/dashboard-analytics/metric-card/index.d.ts +3 -0
  57. package/dist/v2/dashboard-analytics/metric-card/index.d.ts.map +1 -0
  58. package/dist/v2/dashboard-analytics/venue-card/VenueCard.d.ts +12 -0
  59. package/dist/v2/dashboard-analytics/venue-card/VenueCard.d.ts.map +1 -0
  60. package/dist/v2/dashboard-analytics/venue-card/index.d.ts +3 -0
  61. package/dist/v2/dashboard-analytics/venue-card/index.d.ts.map +1 -0
  62. package/dist/v2/dashboard-analytics/venue-closed-card/VenueClosedCard.d.ts +25 -0
  63. package/dist/v2/dashboard-analytics/venue-closed-card/VenueClosedCard.d.ts.map +1 -0
  64. package/dist/v2/dashboard-analytics/venue-closed-card/index.d.ts +3 -0
  65. package/dist/v2/dashboard-analytics/venue-closed-card/index.d.ts.map +1 -0
  66. package/dist/v2/index.d.ts +11 -5
  67. package/dist/v2/index.d.ts.map +1 -1
  68. package/dist/v2/navigation/DashboardLayout/AdminSidebar.d.ts +10 -0
  69. package/dist/v2/navigation/DashboardLayout/AdminSidebar.d.ts.map +1 -0
  70. package/dist/v2/navigation/DashboardLayout/AdminSidebar.js +296 -0
  71. package/dist/v2/navigation/DashboardLayout/AdminSidebar.scss.js +6 -0
  72. package/dist/v2/navigation/DashboardLayout/DashboardFooter.d.ts +7 -0
  73. package/dist/v2/navigation/DashboardLayout/DashboardFooter.d.ts.map +1 -0
  74. package/dist/v2/navigation/DashboardLayout/DashboardFooter.js +34 -0
  75. package/dist/v2/navigation/DashboardLayout/DashboardFooter.scss.js +6 -0
  76. package/dist/v2/navigation/DashboardLayout/DashboardLayout.d.ts +42 -0
  77. package/dist/v2/navigation/DashboardLayout/DashboardLayout.d.ts.map +1 -0
  78. package/dist/v2/navigation/DashboardLayout/DashboardLayout.js +154 -0
  79. package/dist/v2/navigation/DashboardLayout/DashboardLayout.scss.js +6 -0
  80. package/dist/v2/navigation/DashboardLayout/ProviderSidebar.d.ts +35 -0
  81. package/dist/v2/navigation/DashboardLayout/ProviderSidebar.d.ts.map +1 -0
  82. package/dist/v2/navigation/DashboardLayout/ProviderSidebar.js +344 -0
  83. package/dist/v2/navigation/DashboardLayout/ProviderSidebar.scss.js +6 -0
  84. package/dist/v2/navigation/DashboardLayout/TopNavigation.d.ts +26 -0
  85. package/dist/v2/navigation/DashboardLayout/TopNavigation.d.ts.map +1 -0
  86. package/dist/v2/navigation/DashboardLayout/TopNavigation.js +468 -0
  87. package/dist/v2/navigation/DashboardLayout/TopNavigation.scss.js +6 -0
  88. package/dist/v2/navigation/DashboardLayout/assets/AdminLogo.png.js +3 -0
  89. package/dist/v2/navigation/DashboardLayout/assets/BookedLogo_Mark.png.js +3 -0
  90. package/dist/v2/navigation/DashboardLayout/index.d.ts +7 -0
  91. package/dist/v2/navigation/DashboardLayout/index.d.ts.map +1 -0
  92. package/package.json +5 -3
  93. package/src/assets/Trend-Down.svg +3 -0
  94. package/src/assets/Trend-Up.svg +3 -0
  95. package/src/auth/Authorizer.tsx +49 -20
  96. package/src/index.ts +2 -1
  97. package/src/v2/components/EntityHeader/EntityHeader.scss +133 -0
  98. package/src/v2/components/EntityHeader/EntityHeader.stories.tsx +103 -0
  99. package/src/v2/components/EntityHeader/EntityHeader.tsx +76 -0
  100. package/src/v2/components/EntityHeader/index.ts +1 -0
  101. package/src/v2/components/NPSScore/NPSScore.scss +330 -0
  102. package/src/v2/components/NPSScore/NPSScore.stories.tsx +29 -0
  103. package/src/v2/components/NPSScore/NPSScore.tsx +209 -0
  104. package/src/v2/components/NPSScore/index.ts +2 -0
  105. package/src/v2/components/Select/Select.scss +188 -0
  106. package/src/v2/components/Select/Select.stories.tsx +164 -0
  107. package/src/v2/components/Select/Select.tsx +56 -0
  108. package/src/v2/components/Select/index.ts +2 -0
  109. package/src/v2/components/Tooltip/Tooltip.scss +92 -0
  110. package/src/v2/components/Tooltip/Tooltip.stories.tsx +164 -0
  111. package/src/v2/components/Tooltip/Tooltip.tsx +64 -0
  112. package/src/v2/components/Tooltip/index.ts +8 -0
  113. package/src/v2/components/UserAvatar/UserAvatar.scss +62 -0
  114. package/src/v2/components/UserAvatar/UserAvatar.stories.tsx +94 -0
  115. package/src/v2/components/UserAvatar/UserAvatar.tsx +96 -0
  116. package/src/v2/components/UserAvatar/index.ts +1 -0
  117. package/src/v2/components/UserPanel/UserPanel.scss +195 -0
  118. package/src/v2/components/UserPanel/UserPanel.stories.tsx +66 -0
  119. package/src/v2/components/UserPanel/UserPanel.tsx +132 -0
  120. package/src/v2/components/UserPanel/index.ts +2 -0
  121. package/src/v2/dashboard-analytics/blog-posts/Blog.scss +92 -0
  122. package/src/v2/dashboard-analytics/blog-posts/Blog.stories.tsx +57 -0
  123. package/src/v2/dashboard-analytics/blog-posts/Blog.tsx +91 -0
  124. package/src/v2/dashboard-analytics/blog-posts/index.ts +2 -0
  125. package/src/v2/dashboard-analytics/chart/Chart.scss +424 -0
  126. package/src/v2/dashboard-analytics/chart/Chart.stories.tsx +157 -0
  127. package/src/v2/dashboard-analytics/chart/Chart.tsx +623 -0
  128. package/src/v2/dashboard-analytics/chart/index.ts +2 -0
  129. package/src/v2/dashboard-analytics/dashboard/Dashboard.scss +254 -0
  130. package/src/v2/dashboard-analytics/dashboard/Dashboard.stories.tsx +298 -0
  131. package/src/v2/dashboard-analytics/dashboard/Dashboard.tsx +248 -0
  132. package/src/v2/dashboard-analytics/dashboard/index.ts +2 -0
  133. package/src/v2/dashboard-analytics/index.ts +12 -0
  134. package/src/v2/dashboard-analytics/metric-card/MetricCard.scss +125 -0
  135. package/src/v2/dashboard-analytics/metric-card/MetricCard.stories.tsx +106 -0
  136. package/src/v2/dashboard-analytics/metric-card/MetricCard.tsx +72 -0
  137. package/src/v2/dashboard-analytics/metric-card/index.ts +2 -0
  138. package/src/v2/dashboard-analytics/venue-card/VenueCard.scss +112 -0
  139. package/src/v2/dashboard-analytics/venue-card/VenueCard.stories.tsx +40 -0
  140. package/src/v2/dashboard-analytics/venue-card/VenueCard.tsx +62 -0
  141. package/src/v2/dashboard-analytics/venue-card/index.ts +2 -0
  142. package/src/v2/dashboard-analytics/venue-closed-card/VenueClosedCard.scss +129 -0
  143. package/src/v2/dashboard-analytics/venue-closed-card/VenueClosedCard.stories.tsx +31 -0
  144. package/src/v2/dashboard-analytics/venue-closed-card/VenueClosedCard.tsx +61 -0
  145. package/src/v2/dashboard-analytics/venue-closed-card/index.ts +2 -0
  146. package/src/v2/design-system/colors/ColorSystem.scss +439 -0
  147. package/src/v2/design-system/colors/ColorSystem.stories.tsx +730 -0
  148. package/src/v2/design-system/typography/Typography.scss +295 -0
  149. package/src/v2/design-system/typography/Typography.stories.tsx +109 -0
  150. package/src/v2/index.ts +43 -7
  151. package/src/v2/navigation/DashboardLayout/AdminSidebar.scss +207 -0
  152. package/src/v2/navigation/DashboardLayout/AdminSidebar.tsx +171 -0
  153. package/src/v2/navigation/DashboardLayout/DashboardFooter.scss +30 -0
  154. package/src/v2/navigation/DashboardLayout/DashboardFooter.tsx +25 -0
  155. package/src/v2/navigation/DashboardLayout/DashboardLayout.scss +91 -0
  156. package/src/v2/navigation/DashboardLayout/DashboardLayout.stories.tsx +370 -0
  157. package/src/v2/navigation/DashboardLayout/DashboardLayout.tsx +215 -0
  158. package/src/v2/navigation/DashboardLayout/ProviderSidebar.scss +266 -0
  159. package/src/v2/navigation/DashboardLayout/ProviderSidebar.tsx +252 -0
  160. package/src/v2/navigation/DashboardLayout/Sidebar.stories.tsx +220 -0
  161. package/src/v2/navigation/DashboardLayout/TopNavigation.scss +206 -0
  162. package/src/v2/navigation/DashboardLayout/TopNavigation.tsx +270 -0
  163. package/src/v2/navigation/DashboardLayout/assets/AdminLogo.png +0 -0
  164. package/src/v2/navigation/DashboardLayout/assets/BookedLogo_Mark.png +0 -0
  165. package/src/v2/navigation/DashboardLayout/index.ts +20 -0
  166. package/src/v2/styles/index.scss +0 -1
  167. package/src/v2/styles/tokens/_colors.scss +531 -98
  168. package/dist/v2/components/Colors/Colors.d.ts +0 -21
  169. package/dist/v2/components/Colors/Colors.d.ts.map +0 -1
  170. package/dist/v2/components/Colors/index.d.ts +0 -3
  171. package/dist/v2/components/Colors/index.d.ts.map +0 -1
  172. package/dist/v2/components/Typography/Typography.d.ts +0 -11
  173. package/dist/v2/components/Typography/Typography.d.ts.map +0 -1
  174. package/dist/v2/components/Typography/index.d.ts +0 -3
  175. package/dist/v2/components/Typography/index.d.ts.map +0 -1
  176. package/src/v2/components/Colors/Colors.scss +0 -64
  177. package/src/v2/components/Colors/Colors.stories.tsx +0 -143
  178. package/src/v2/components/Colors/Colors.tsx +0 -51
  179. package/src/v2/components/Colors/ColorsAliases.stories.tsx +0 -285
  180. package/src/v2/components/Colors/Sizes.stories.tsx +0 -141
  181. package/src/v2/components/Colors/index.ts +0 -2
  182. package/src/v2/components/Typography/Typography.scss +0 -72
  183. package/src/v2/components/Typography/Typography.stories.tsx +0 -266
  184. package/src/v2/components/Typography/Typography.tsx +0 -56
  185. package/src/v2/components/Typography/index.ts +0 -2
  186. package/src/v2/styles/tokens/_aliases.scss +0 -199
@@ -0,0 +1,248 @@
1
+ import React from 'react';
2
+ import { Chart, ChartProps } from '../chart';
3
+ import { MetricCard } from '../metric-card';
4
+ import { Select } from '../../components/Select/Select';
5
+ import { VenueCard } from '../venue-card';
6
+ import { Blog } from '../blog-posts';
7
+ import { VenueClosedCard } from '../venue-closed-card';
8
+ import { ReactComponent as TrendUpIcon } from '../../../assets/Trend-Up.svg'
9
+ import { ReactComponent as TrendDownIcon } from '../../../assets/Trend-Down.svg'
10
+ import './Dashboard.scss';
11
+
12
+ interface MetricsData {
13
+ totalBookings: {
14
+ value: string;
15
+ change: string;
16
+ is_positive: boolean;
17
+ label?: string;
18
+ };
19
+ revenue: {
20
+ value: string;
21
+ change: string;
22
+ is_positive: boolean;
23
+ label?: string;
24
+ };
25
+ numberOfPeople: {
26
+ value: string;
27
+ change: string;
28
+ is_positive: boolean;
29
+ formatted_value?: string;
30
+ label?: string;
31
+ };
32
+ busiestTime: {
33
+ value: string;
34
+ peopleText: string;
35
+ bookingText: string;
36
+ label?: string;
37
+ };
38
+ peopleInVenue: {
39
+ value: string;
40
+ formatted_value: string;
41
+ description: string;
42
+ };
43
+ workHours: {
44
+ company_name: string;
45
+ is_open: boolean;
46
+ status: string;
47
+ message: string;
48
+ opens_at: string;
49
+ };
50
+ }
51
+
52
+ interface DashboardProps {
53
+ metrics: MetricsData;
54
+ bookingsChart: ChartProps;
55
+ revenueChart: ChartProps;
56
+ timeFilter: 'Today' | 'Yesterday' | 'This Week';
57
+ bookingTypeFilter?: 'Bookings For' | 'Bookings Made';
58
+ onFilterChange?: (filter: 'Today' | 'Yesterday' | 'This Week') => void;
59
+ onBookingTypeChange?: (bookingType: 'Bookings For' | 'Bookings Made') => void;
60
+ className?: string;
61
+ isLoading?: boolean;
62
+ isRefreshing?: boolean;
63
+ }
64
+
65
+ export const Dashboard: React.FC<DashboardProps> = ({
66
+ metrics,
67
+ bookingsChart,
68
+ revenueChart,
69
+ timeFilter,
70
+ bookingTypeFilter = 'Bookings For',
71
+ onFilterChange,
72
+ onBookingTypeChange,
73
+ className = '',
74
+ isLoading,
75
+ isRefreshing = false,
76
+ }) => {
77
+ const handleFilterChange = React.useCallback((filter: 'Today' | 'Yesterday' | 'This Week') => {
78
+ onFilterChange?.(filter);
79
+ }, [onFilterChange]);
80
+
81
+ const showRefreshing = isRefreshing && !isLoading;
82
+
83
+ const getTimePeriodText = (filter: 'Today' | 'Yesterday' | 'This Week'): string => {
84
+ switch (filter) {
85
+ case 'Today': return 'today';
86
+ case 'Yesterday': return 'yesterday';
87
+ case 'This Week': return 'this week';
88
+ default: return 'today';
89
+ }
90
+ };
91
+
92
+ const getTrendIcon = (change: string, isPositive: boolean): React.ReactNode | null => {
93
+ if (change.toLowerCase().includes('no change')) return null;
94
+ if (change.toLowerCase().includes('no data')) return null;
95
+ return isPositive ? <TrendUpIcon /> : <TrendDownIcon />;
96
+ };
97
+
98
+ const VenueIcon = () => (
99
+ <svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
100
+ <path d="M8 10L17 6.5V24H8V10Z" fill="black" fill-opacity="0.1"/>
101
+ <path d="M7 23V9.70046C7 9.27995 7.26307 8.90437 7.65826 8.76067L17.3291 5.24398C17.5886 5.14961 17.8755 5.28349 17.9699 5.54301C17.9898 5.59778 18 5.65561 18 5.71388V10.6667L24.3162 12.7721C24.7246 12.9082 25 13.2904 25 13.7208V23H27V25H5V23H7ZM9 23H16V7.85543L9 10.4009V23ZM23 23V14.4416L18 12.7749V23H23Z" fill="#626A90"/>
102
+ </svg>
103
+
104
+ );
105
+
106
+ return (
107
+ <div className={`dashboard ${timeFilter !== 'Today' ? 'dashboard--charts-focus' : ''} ${className}`.trim()}>
108
+ <div className='dashboard__filter'>
109
+ <Select
110
+ variant="default"
111
+ size="md"
112
+ defaultValue={bookingTypeFilter}
113
+ onChange={(e: React.ChangeEvent<HTMLSelectElement>) => {
114
+ const newBookingType = e.target.value as 'Bookings For' | 'Bookings Made';
115
+ onBookingTypeChange?.(newBookingType);
116
+ }}
117
+ >
118
+ <option value="Bookings For">Bookings For</option>
119
+ <option value="Bookings Made">Bookings Made</option>
120
+ </Select>
121
+
122
+ <div className="dashboard__filter-container">
123
+ <Select
124
+ variant="default"
125
+ size="md"
126
+ defaultValue={timeFilter}
127
+ onChange={(e: React.ChangeEvent<HTMLSelectElement>) => {
128
+ const newFilter = e.target.value as 'Today' | 'Yesterday' | 'This Week';
129
+ handleFilterChange(newFilter);
130
+ }}
131
+ className={showRefreshing ? 'select--refreshing' : ''}
132
+ >
133
+ <option value="Today">Today</option>
134
+ <option value="Yesterday">Yesterday</option>
135
+ <option value="This Week">This Week</option>
136
+ </Select>
137
+
138
+ {showRefreshing && (
139
+ <div className="dashboard__refresh-indicator">
140
+ <div className="dashboard__refresh-spinner"></div>
141
+ </div>
142
+ )}
143
+ </div>
144
+ </div>
145
+
146
+ <div className="dashboard__metrics-grid">
147
+ <MetricCard
148
+ icon="https://api.builder.io/api/v1/image/assets/TEMP/a9080a040516a2aee8e202c1fecbbf08f0f4f7b0?placeholderIfAbsent=true"
149
+ title={metrics.totalBookings.label || "Total Bookings"}
150
+ value={metrics.totalBookings.value}
151
+ performance={{
152
+ icon: getTrendIcon(metrics.totalBookings.change, metrics.totalBookings.is_positive),
153
+ text: metrics.totalBookings.change,
154
+ color: metrics.totalBookings.change.toLowerCase().includes('no change')
155
+ ? "#626A90"
156
+ : metrics.totalBookings.is_positive ? "#2D6B18" : "#CC3C35"
157
+ }}
158
+ />
159
+
160
+ <MetricCard
161
+ icon="https://api.builder.io/api/v1/image/assets/TEMP/d2cc0fb13dfe9a16e4258917cb2c331c1f0e1189?placeholderIfAbsent=true"
162
+ title={metrics.revenue.label || "Revenue"}
163
+ value={metrics.revenue.value}
164
+ performance={{
165
+ icon: getTrendIcon(metrics.revenue.change, metrics.revenue.is_positive),
166
+ text: metrics.revenue.change,
167
+ color: metrics.revenue.change.toLowerCase().includes('no change')
168
+ ? "#626A90"
169
+ : metrics.revenue.is_positive ? "#2D6B18" : "#CC3C35"
170
+ }}
171
+ />
172
+
173
+ <MetricCard
174
+ icon="https://api.builder.io/api/v1/image/assets/TEMP/8c83d255f42836d5497be545d10a8ad9295aa968?placeholderIfAbsent=true"
175
+ title={metrics.numberOfPeople.label || `People booked in ${getTimePeriodText(timeFilter)}`}
176
+ value={metrics.numberOfPeople.formatted_value || `${metrics.numberOfPeople.value} people`}
177
+ performance={{
178
+ icon: getTrendIcon(metrics.numberOfPeople.change, metrics.numberOfPeople.is_positive),
179
+ text: metrics.numberOfPeople.change,
180
+ color: metrics.numberOfPeople.change.toLowerCase().includes('no change')
181
+ ? "#626A90"
182
+ : metrics.numberOfPeople.is_positive ? "#2D6B18" : "#CC3C35"
183
+ }}
184
+ />
185
+
186
+ <MetricCard
187
+ icon="https://api.builder.io/api/v1/image/assets/TEMP/265ca1f3a6e063263764400beb86070a49b952cb?placeholderIfAbsent=true"
188
+ title={metrics.busiestTime.label || (timeFilter === 'This Week' ? 'Busiest day' : 'Busiest time')}
189
+ value={metrics.busiestTime.value}
190
+ performance={{
191
+ text: `${metrics.busiestTime.peopleText} // ${metrics.busiestTime.bookingText}`,
192
+ color: "#121E52"
193
+ }}
194
+ />
195
+ </div>
196
+
197
+ <div className="dashboard__content">
198
+ <div className="dashboard__charts-section">
199
+ <Chart
200
+ {...bookingsChart}
201
+ period={timeFilter === 'Today' ? 'today' : timeFilter === 'Yesterday' ? 'yesterday' : timeFilter === 'This Week' ? 'week' : 'today'}
202
+ isFullWidth={true}
203
+ className={showRefreshing ? 'chart--refreshing' : ''}
204
+ isLoading={isLoading}
205
+ isRefreshing={showRefreshing}
206
+ />
207
+ <Chart
208
+ {...revenueChart}
209
+ isRevenue={true}
210
+ period={timeFilter === 'Today' ? 'today' : timeFilter === 'Yesterday' ? 'yesterday' : timeFilter === 'This Week' ? 'week' : 'today'}
211
+ isFullWidth={true}
212
+ className={showRefreshing ? 'chart--refreshing' : ''}
213
+ isLoading={isLoading}
214
+ isRefreshing={showRefreshing}
215
+ />
216
+ </div>
217
+
218
+ {timeFilter === 'Today' && (
219
+ <div className="dashboard__sidebar">
220
+ {metrics.workHours?.is_open ? (
221
+ <div className="dashboard__venue-section">
222
+ <VenueCard
223
+ icon='https://api.builder.io/api/v1/image/assets/TEMP/b44b83b1f30b882d65f942ec883e90ae34fdb2d0?placeholderIfAbsent=true'
224
+ title='In venue'
225
+ value={metrics.peopleInVenue.formatted_value || '0'}
226
+ />
227
+ <VenueCard
228
+ icon={<VenueIcon />}
229
+ title="Open until"
230
+ value={metrics.workHours?.message?.replace('Open until ', '') || ''}
231
+ shadowColor='#121E52'
232
+ />
233
+ </div>
234
+ ) : (
235
+ <VenueClosedCard
236
+ venueInfo={metrics.workHours}
237
+ />
238
+ )}
239
+
240
+ <Blog />
241
+ </div>
242
+ )}
243
+ </div>
244
+ </div>
245
+ );
246
+ };
247
+
248
+ export type { DashboardProps };
@@ -0,0 +1,2 @@
1
+ export { Dashboard } from './Dashboard';
2
+ export type { DashboardProps } from './Dashboard';
@@ -0,0 +1,12 @@
1
+ export { MetricCard } from './metric-card'
2
+ export type { MetricCardProps } from './metric-card'
3
+ export { VenueCard } from './venue-card'
4
+ export type { VenueCardProps } from './venue-card'
5
+ export { VenueClosedCard } from './venue-closed-card'
6
+ export type { VenueClosedCardProps } from './venue-closed-card'
7
+ export { Chart } from './chart'
8
+ export type { ChartProps } from './chart'
9
+ export { Dashboard } from './dashboard'
10
+ export type { DashboardProps } from './dashboard'
11
+ export { Blog } from './blog-posts'
12
+ export type { BlogProps, BlogPost } from './blog-posts'
@@ -0,0 +1,125 @@
1
+ @import '../../design-system/typography/Typography.scss';
2
+
3
+ .metric-card {
4
+ position: relative;
5
+ display: flex;
6
+ align-items: flex-start;
7
+ gap: var(--padding-reg, 16px);
8
+ align-self: stretch;
9
+ padding: 8px;
10
+ background-color: var(--surface-primary);
11
+ border: 1px solid var(--border-primary);
12
+ border-radius: var(--radius-reg, 8px);
13
+
14
+ &.no-border {
15
+ border: none;
16
+ border-radius: 0;
17
+ }
18
+
19
+ &.inline-metric {
20
+ flex-direction: row;
21
+ align-items: center;
22
+ height: 80px;
23
+ }
24
+
25
+ &.h-20 {
26
+ height: 80px;
27
+ }
28
+
29
+ &.h-16 {
30
+ height: 64px;
31
+ }
32
+
33
+ &.status-active {
34
+ animation: shadow-pulse 2s ease-in-out infinite;
35
+ }
36
+
37
+ @media (min-width: 768px) {
38
+ flex-direction: column;
39
+ min-height: 160px;
40
+ padding: 1rem 0.5rem 1rem 1rem;
41
+ gap: 1rem;
42
+ }
43
+
44
+ &__icon-wrapper {
45
+ display: flex;
46
+ width: 48px;
47
+ padding: var(--padding-md, 8px) var(--padding-md, 8px);
48
+ flex-direction: column;
49
+ justify-content: center;
50
+ align-items: center;
51
+ gap: 8px;
52
+ align-self: stretch;
53
+ background-color: var(--surface-secondary);
54
+ border-radius: 4px;
55
+ }
56
+
57
+ &__icon {
58
+ width: 100%;
59
+ height: 100%;
60
+ object-fit: contain;
61
+ aspect-ratio: 1;
62
+ }
63
+
64
+ &__content {
65
+ display: flex;
66
+ flex-direction: column;
67
+ gap: 8px;
68
+ flex: 1;
69
+ }
70
+
71
+ &__header {
72
+ display: flex;
73
+ flex-direction: column;
74
+ gap: 4px;
75
+ }
76
+
77
+ &__title {
78
+ align-self: stretch;
79
+ color: var(--label-secondary);
80
+ font-family: Geist, sans-serif;
81
+ font-size: 13px;
82
+ font-style: normal;
83
+ font-weight: 500;
84
+ line-height: 16px;
85
+ }
86
+
87
+ &__value {
88
+ @include typography('heading.h3');
89
+ color: var(--label-primary);
90
+ }
91
+
92
+ // Performance section
93
+ &__performance {
94
+ display: flex;
95
+ align-items: center;
96
+ gap: 0.25rem;
97
+ @include typography('heading.h6');
98
+ }
99
+
100
+ &__performance-icon {
101
+ display: flex;
102
+ align-items: center;
103
+ flex-shrink: 0;
104
+
105
+ svg {
106
+ width: 16px;
107
+ height: 16px;
108
+ }
109
+ }
110
+
111
+ &__performance-text {
112
+ @include typography('heading.h6');
113
+ }
114
+ }
115
+
116
+ @keyframes shadow-pulse {
117
+ 0%, 100% {
118
+ box-shadow: 0 2px 12px rgba(82, 194, 43, 0.3);
119
+ border-color: var(--success-regular);
120
+ }
121
+ 50% {
122
+ box-shadow: 0 2px 12px rgba(82, 194, 43, 0);
123
+ border-color: var(--border-primary);
124
+ }
125
+ }
@@ -0,0 +1,106 @@
1
+ import { Meta, Story } from '@storybook/react-vite'
2
+ import { MetricCard, MetricCardProps } from './MetricCard'
3
+
4
+ export default {
5
+ title: 'v2/Dashboard Analytics/Metric Card',
6
+ component: MetricCard,
7
+ argTypes: {
8
+ icon: {
9
+ description: 'Icon URL for the metric',
10
+ control: 'text',
11
+ },
12
+ title: {
13
+ description: 'Title of the metric',
14
+ control: 'text',
15
+ },
16
+ value: {
17
+ description: 'Value to display',
18
+ control: 'text',
19
+ },
20
+ showStatusDot: {
21
+ description: 'Show animated status dot',
22
+ control: 'boolean',
23
+ },
24
+ className: {
25
+ description: 'Additional CSS classes',
26
+ control: 'text',
27
+ },
28
+ },
29
+ } as Meta<MetricCardProps>
30
+
31
+ const placeholderIcon = 'https://api.builder.io/api/v1/image/assets/TEMP/a9080a040516a2aee8e202c1fecbbf08f0f4f7b0?placeholderIfAbsent=true'
32
+ const currencyIcon = 'https://api.builder.io/api/v1/image/assets/TEMP/d2cc0fb13dfe9a16e4258917cb2c331c1f0e1189?placeholderIfAbsent=true'
33
+
34
+ export const Default: Story<MetricCardProps> = (args:MetricCardProps) => (
35
+ <div className="w-10">
36
+ <MetricCard {...args} />
37
+ </div>
38
+ )
39
+
40
+ Default.args = {
41
+ icon: placeholderIcon,
42
+ title: 'Bookings made',
43
+ value: '0',
44
+ performance: {
45
+ text: 'No data available',
46
+ color: '#626A90',
47
+ },
48
+ }
49
+
50
+ export const WithPositiveTrend: Story<MetricCardProps> = () => (
51
+ <div className="w-10">
52
+ <MetricCard
53
+ icon={placeholderIcon}
54
+ title="Total Bookings"
55
+ value="24"
56
+ performance={{
57
+ text: '+8 vs yesterday',
58
+ color: '#2D6B18',
59
+ }}
60
+ />
61
+ </div>
62
+ )
63
+
64
+ export const WithNegativeTrend: Story<MetricCardProps> = () => (
65
+ <div className="w-10">
66
+ <MetricCard
67
+ icon={placeholderIcon}
68
+ title="Total Bookings"
69
+ value="16"
70
+ performance={{
71
+ text: '-5 vs yesterday',
72
+ color: '#CC3C35',
73
+ }}
74
+ />
75
+ </div>
76
+ )
77
+
78
+ export const Currency: Story<MetricCardProps> = (args:MetricCardProps) => (
79
+ <div className="w-10">
80
+ <MetricCard {...args} />
81
+ </div>
82
+ )
83
+
84
+ Currency.args = {
85
+ icon: currencyIcon,
86
+ title: 'Revenue made',
87
+ value: '£0.00',
88
+ performance: {
89
+ text: 'No data available',
90
+ color: '#626A90',
91
+ },
92
+ }
93
+
94
+ export const CurrencyWithTrend: Story<MetricCardProps> = () => (
95
+ <div className="w-10">
96
+ <MetricCard
97
+ icon={currencyIcon}
98
+ title="Revenue made"
99
+ value="£1,245.00"
100
+ performance={{
101
+ text: '+£320 vs yesterday',
102
+ color: '#2D6B18',
103
+ }}
104
+ />
105
+ </div>
106
+ )
@@ -0,0 +1,72 @@
1
+ import React from 'react';
2
+ import './MetricCard.scss';
3
+
4
+ interface MetricCardProps {
5
+ icon: string;
6
+ title: string;
7
+ value: string;
8
+ performance?: {
9
+ icon?: React.ReactNode;
10
+ text: string | React.ReactNode;
11
+ color: string;
12
+ };
13
+ showStatusDot?: boolean;
14
+ className?: string;
15
+ }
16
+
17
+ export const MetricCard: React.FC<MetricCardProps> = ({
18
+ icon,
19
+ title,
20
+ value,
21
+ performance,
22
+ showStatusDot = false,
23
+ className = ''
24
+ }) => {
25
+ const cardClasses = [
26
+ 'metric-card',
27
+ className?.includes('no-border') && 'no-border',
28
+ className?.includes('inline-metric') && 'inline-metric',
29
+ className?.includes('h-20') && 'h-20',
30
+ className?.includes('h-16') && 'h-16',
31
+ showStatusDot && 'status-active',
32
+ className
33
+ ].filter(Boolean).join(' ');
34
+
35
+ return (
36
+ <div className={cardClasses}>
37
+ <div className="metric-card__icon-wrapper">
38
+ <img
39
+ src={icon}
40
+ className="metric-card__icon"
41
+ alt={title}
42
+ />
43
+ </div>
44
+
45
+ <div className="metric-card__content">
46
+ <div className="metric-card__header">
47
+ <div className="metric-card__title">
48
+ {title}
49
+ </div>
50
+ <div className="metric-card__value">
51
+ {value}
52
+ </div>
53
+ </div>
54
+
55
+ {performance && (
56
+ <div className="metric-card__performance" style={{ color: performance.color }}>
57
+ {performance.icon && (
58
+ <span className="metric-card__performance-icon">
59
+ {performance.icon}
60
+ </span>
61
+ )}
62
+ <span className="metric-card__performance-text">
63
+ {performance.text}
64
+ </span>
65
+ </div>
66
+ )}
67
+ </div>
68
+ </div>
69
+ );
70
+ };
71
+
72
+ export type { MetricCardProps };
@@ -0,0 +1,2 @@
1
+ export { MetricCard } from './MetricCard'
2
+ export type { MetricCardProps } from './MetricCard'
@@ -0,0 +1,112 @@
1
+ @import '../../design-system/typography/Typography.scss';
2
+
3
+ .venue-card {
4
+ position: relative;
5
+ width: 154px;
6
+ display: flex;
7
+ flex-direction: column;
8
+ align-items: start;
9
+ justify-content: center;
10
+ gap: 1rem;
11
+ min-height: 114px;
12
+ padding: 1rem 0.5rem 1rem 1rem;
13
+ background-color: var(--surface-primary);
14
+ border: 1px solid;
15
+ border-radius: 0.5rem;
16
+
17
+ @media (max-width: 1024px) {
18
+ width: 100%;
19
+ }
20
+
21
+ &.no-border {
22
+ border: none;
23
+ border-radius: 0;
24
+ }
25
+
26
+ &.inline-metric {
27
+ flex-direction: row;
28
+ align-items: center;
29
+ height: 80px;
30
+ }
31
+
32
+ &.h-20 {
33
+ height: 80px;
34
+ }
35
+
36
+ &.h-16 {
37
+ height: 64px;
38
+ }
39
+
40
+ &.status-active {
41
+ animation: shadow-pulse 2s ease-in-out infinite;
42
+ }
43
+
44
+ @media (min-width: 768px) {
45
+ min-height: 114px;
46
+ }
47
+
48
+ // Icon wrapper styles
49
+ &__icon-wrapper {
50
+ display: flex;
51
+ align-items: center;
52
+ justify-content: center;
53
+ width: 32px;
54
+ height: 32px;
55
+ padding: 2px;
56
+ background-color: var(--surface-secondary);
57
+ border-radius: 0.5rem;
58
+ flex-shrink: 0;
59
+ z-index: 0;
60
+ }
61
+
62
+ &__icon {
63
+ width: 100%;
64
+ height: 100%;
65
+ object-fit: contain;
66
+ aspect-ratio: 1;
67
+ align-self: start;
68
+ }
69
+
70
+ // Content container
71
+ &__content {
72
+ width: 100%;
73
+ z-index: 0;
74
+ display: flex;
75
+ flex-direction: column;
76
+ justify-content: space-between;
77
+ flex: 1;
78
+ }
79
+
80
+ // Header section
81
+ &__header {
82
+ display: flex;
83
+ flex-direction: column;
84
+ gap: 0.2rem;
85
+ }
86
+
87
+ &__title {
88
+ @include typography('heading.h5');
89
+ line-height: 1;
90
+ color: var(--label-secondary);
91
+ display: flex;
92
+ align-items: center;
93
+ }
94
+
95
+ &__value {
96
+ @include typography('heading.h3');
97
+ line-height: 1;
98
+ color: var(--label-primary)
99
+ }
100
+ }
101
+
102
+ @keyframes shadow-pulse {
103
+ 0%, 100% {
104
+ box-shadow: 0 2px 12px color-mix(in srgb, var(--shadow-color, rgba(82, 194, 43, 1)) 30%, transparent);
105
+ border-color: var(--shadow-color, var(--success-regular));
106
+ }
107
+ 50% {
108
+ box-shadow: 0 2px 12px transparent;
109
+ border-color: var(--border-primary);
110
+ }
111
+ }
112
+