@licklist/design 0.78.21 → 0.78.26
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/dist/assets/Trend-Down.svg +3 -0
- package/dist/assets/Trend-Up.svg +3 -0
- package/dist/auth/Authorizer.d.ts.map +1 -1
- package/dist/auth/Authorizer.js +47 -12
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/v2/components/EntityHeader/EntityHeader.d.ts +13 -0
- package/dist/v2/components/EntityHeader/EntityHeader.d.ts.map +1 -0
- package/dist/v2/components/EntityHeader/EntityHeader.js +85 -0
- package/dist/v2/components/EntityHeader/EntityHeader.scss.js +6 -0
- package/dist/v2/components/EntityHeader/index.d.ts +2 -0
- package/dist/v2/components/EntityHeader/index.d.ts.map +1 -0
- package/dist/v2/components/NPSScore/NPSScore.d.ts +18 -0
- package/dist/v2/components/NPSScore/NPSScore.d.ts.map +1 -0
- package/dist/v2/components/NPSScore/index.d.ts +3 -0
- package/dist/v2/components/NPSScore/index.d.ts.map +1 -0
- package/dist/v2/components/Select/Select.d.ts +10 -0
- package/dist/v2/components/Select/Select.d.ts.map +1 -0
- package/dist/v2/components/Select/index.d.ts +3 -0
- package/dist/v2/components/Select/index.d.ts.map +1 -0
- package/dist/v2/components/Tooltip/Tooltip.d.ts +21 -0
- package/dist/v2/components/Tooltip/Tooltip.d.ts.map +1 -0
- package/dist/v2/components/Tooltip/Tooltip.js +103 -0
- package/dist/v2/components/Tooltip/Tooltip.scss.js +6 -0
- package/dist/v2/components/Tooltip/index.d.ts +2 -0
- package/dist/v2/components/Tooltip/index.d.ts.map +1 -0
- package/dist/v2/components/UserAvatar/UserAvatar.d.ts +12 -0
- package/dist/v2/components/UserAvatar/UserAvatar.d.ts.map +1 -0
- package/dist/v2/components/UserAvatar/UserAvatar.js +77 -0
- package/dist/v2/components/UserAvatar/UserAvatar.scss.js +6 -0
- package/dist/v2/components/UserAvatar/index.d.ts +2 -0
- package/dist/v2/components/UserAvatar/index.d.ts.map +1 -0
- package/dist/v2/components/UserPanel/UserPanel.d.ts +17 -0
- package/dist/v2/components/UserPanel/UserPanel.d.ts.map +1 -0
- package/dist/v2/components/UserPanel/UserPanel.js +144 -0
- package/dist/v2/components/UserPanel/UserPanel.scss.js +6 -0
- package/dist/v2/components/UserPanel/index.d.ts +3 -0
- package/dist/v2/components/UserPanel/index.d.ts.map +1 -0
- package/dist/v2/dashboard-analytics/blog-posts/Blog.d.ts +15 -0
- package/dist/v2/dashboard-analytics/blog-posts/Blog.d.ts.map +1 -0
- package/dist/v2/dashboard-analytics/blog-posts/index.d.ts +3 -0
- package/dist/v2/dashboard-analytics/blog-posts/index.d.ts.map +1 -0
- package/dist/v2/dashboard-analytics/chart/Chart.d.ts +21 -0
- package/dist/v2/dashboard-analytics/chart/Chart.d.ts.map +1 -0
- package/dist/v2/dashboard-analytics/chart/index.d.ts +3 -0
- package/dist/v2/dashboard-analytics/chart/index.d.ts.map +1 -0
- package/dist/v2/dashboard-analytics/dashboard/Dashboard.d.ts +57 -0
- package/dist/v2/dashboard-analytics/dashboard/Dashboard.d.ts.map +1 -0
- package/dist/v2/dashboard-analytics/dashboard/index.d.ts +3 -0
- package/dist/v2/dashboard-analytics/dashboard/index.d.ts.map +1 -0
- package/dist/v2/dashboard-analytics/index.d.ts +13 -0
- package/dist/v2/dashboard-analytics/index.d.ts.map +1 -0
- package/dist/v2/dashboard-analytics/metric-card/MetricCard.d.ts +17 -0
- package/dist/v2/dashboard-analytics/metric-card/MetricCard.d.ts.map +1 -0
- package/dist/v2/dashboard-analytics/metric-card/index.d.ts +3 -0
- package/dist/v2/dashboard-analytics/metric-card/index.d.ts.map +1 -0
- package/dist/v2/dashboard-analytics/venue-card/VenueCard.d.ts +12 -0
- package/dist/v2/dashboard-analytics/venue-card/VenueCard.d.ts.map +1 -0
- package/dist/v2/dashboard-analytics/venue-card/index.d.ts +3 -0
- package/dist/v2/dashboard-analytics/venue-card/index.d.ts.map +1 -0
- package/dist/v2/dashboard-analytics/venue-closed-card/VenueClosedCard.d.ts +25 -0
- package/dist/v2/dashboard-analytics/venue-closed-card/VenueClosedCard.d.ts.map +1 -0
- package/dist/v2/dashboard-analytics/venue-closed-card/index.d.ts +3 -0
- package/dist/v2/dashboard-analytics/venue-closed-card/index.d.ts.map +1 -0
- package/dist/v2/index.d.ts +11 -5
- package/dist/v2/index.d.ts.map +1 -1
- package/dist/v2/navigation/DashboardLayout/AdminSidebar.d.ts +10 -0
- package/dist/v2/navigation/DashboardLayout/AdminSidebar.d.ts.map +1 -0
- package/dist/v2/navigation/DashboardLayout/AdminSidebar.js +296 -0
- package/dist/v2/navigation/DashboardLayout/AdminSidebar.scss.js +6 -0
- package/dist/v2/navigation/DashboardLayout/DashboardFooter.d.ts +7 -0
- package/dist/v2/navigation/DashboardLayout/DashboardFooter.d.ts.map +1 -0
- package/dist/v2/navigation/DashboardLayout/DashboardFooter.js +34 -0
- package/dist/v2/navigation/DashboardLayout/DashboardFooter.scss.js +6 -0
- package/dist/v2/navigation/DashboardLayout/DashboardLayout.d.ts +42 -0
- package/dist/v2/navigation/DashboardLayout/DashboardLayout.d.ts.map +1 -0
- package/dist/v2/navigation/DashboardLayout/DashboardLayout.js +176 -0
- package/dist/v2/navigation/DashboardLayout/DashboardLayout.scss.js +6 -0
- package/dist/v2/navigation/DashboardLayout/ProviderSidebar.d.ts +35 -0
- package/dist/v2/navigation/DashboardLayout/ProviderSidebar.d.ts.map +1 -0
- package/dist/v2/navigation/DashboardLayout/ProviderSidebar.js +366 -0
- package/dist/v2/navigation/DashboardLayout/ProviderSidebar.scss.js +6 -0
- package/dist/v2/navigation/DashboardLayout/TopNavigation.d.ts +26 -0
- package/dist/v2/navigation/DashboardLayout/TopNavigation.d.ts.map +1 -0
- package/dist/v2/navigation/DashboardLayout/TopNavigation.js +360 -0
- package/dist/v2/navigation/DashboardLayout/TopNavigation.scss.js +6 -0
- package/dist/v2/navigation/DashboardLayout/assets/AdminLogo.png.js +3 -0
- package/dist/v2/navigation/DashboardLayout/assets/BookedLogo_Mark.png.js +3 -0
- package/dist/v2/navigation/DashboardLayout/index.d.ts +7 -0
- package/dist/v2/navigation/DashboardLayout/index.d.ts.map +1 -0
- package/package.json +5 -3
- package/src/assets/Trend-Down.svg +3 -0
- package/src/assets/Trend-Up.svg +3 -0
- package/src/auth/Authorizer.tsx +49 -20
- package/src/index.ts +2 -1
- package/src/v2/components/EntityHeader/EntityHeader.scss +133 -0
- package/src/v2/components/EntityHeader/EntityHeader.stories.tsx +103 -0
- package/src/v2/components/EntityHeader/EntityHeader.tsx +76 -0
- package/src/v2/components/EntityHeader/index.ts +1 -0
- package/src/v2/components/NPSScore/NPSScore.scss +330 -0
- package/src/v2/components/NPSScore/NPSScore.stories.tsx +29 -0
- package/src/v2/components/NPSScore/NPSScore.tsx +209 -0
- package/src/v2/components/NPSScore/index.ts +2 -0
- package/src/v2/components/Select/Select.scss +188 -0
- package/src/v2/components/Select/Select.stories.tsx +164 -0
- package/src/v2/components/Select/Select.tsx +56 -0
- package/src/v2/components/Select/index.ts +2 -0
- package/src/v2/components/Tooltip/Tooltip.scss +92 -0
- package/src/v2/components/Tooltip/Tooltip.stories.tsx +164 -0
- package/src/v2/components/Tooltip/Tooltip.tsx +64 -0
- package/src/v2/components/Tooltip/index.ts +8 -0
- package/src/v2/components/UserAvatar/UserAvatar.scss +62 -0
- package/src/v2/components/UserAvatar/UserAvatar.stories.tsx +94 -0
- package/src/v2/components/UserAvatar/UserAvatar.tsx +96 -0
- package/src/v2/components/UserAvatar/index.ts +1 -0
- package/src/v2/components/UserPanel/UserPanel.scss +195 -0
- package/src/v2/components/UserPanel/UserPanel.stories.tsx +66 -0
- package/src/v2/components/UserPanel/UserPanel.tsx +126 -0
- package/src/v2/components/UserPanel/index.ts +2 -0
- package/src/v2/dashboard-analytics/blog-posts/Blog.scss +92 -0
- package/src/v2/dashboard-analytics/blog-posts/Blog.stories.tsx +57 -0
- package/src/v2/dashboard-analytics/blog-posts/Blog.tsx +91 -0
- package/src/v2/dashboard-analytics/blog-posts/index.ts +2 -0
- package/src/v2/dashboard-analytics/chart/Chart.scss +424 -0
- package/src/v2/dashboard-analytics/chart/Chart.stories.tsx +157 -0
- package/src/v2/dashboard-analytics/chart/Chart.tsx +623 -0
- package/src/v2/dashboard-analytics/chart/index.ts +2 -0
- package/src/v2/dashboard-analytics/dashboard/Dashboard.scss +254 -0
- package/src/v2/dashboard-analytics/dashboard/Dashboard.stories.tsx +298 -0
- package/src/v2/dashboard-analytics/dashboard/Dashboard.tsx +248 -0
- package/src/v2/dashboard-analytics/dashboard/index.ts +2 -0
- package/src/v2/dashboard-analytics/index.ts +12 -0
- package/src/v2/dashboard-analytics/metric-card/MetricCard.scss +125 -0
- package/src/v2/dashboard-analytics/metric-card/MetricCard.stories.tsx +106 -0
- package/src/v2/dashboard-analytics/metric-card/MetricCard.tsx +72 -0
- package/src/v2/dashboard-analytics/metric-card/index.ts +2 -0
- package/src/v2/dashboard-analytics/venue-card/VenueCard.scss +112 -0
- package/src/v2/dashboard-analytics/venue-card/VenueCard.stories.tsx +40 -0
- package/src/v2/dashboard-analytics/venue-card/VenueCard.tsx +62 -0
- package/src/v2/dashboard-analytics/venue-card/index.ts +2 -0
- package/src/v2/dashboard-analytics/venue-closed-card/VenueClosedCard.scss +129 -0
- package/src/v2/dashboard-analytics/venue-closed-card/VenueClosedCard.stories.tsx +31 -0
- package/src/v2/dashboard-analytics/venue-closed-card/VenueClosedCard.tsx +61 -0
- package/src/v2/dashboard-analytics/venue-closed-card/index.ts +2 -0
- package/src/v2/design-system/colors/ColorSystem.scss +439 -0
- package/src/v2/design-system/colors/ColorSystem.stories.tsx +730 -0
- package/src/v2/design-system/typography/Typography.scss +295 -0
- package/src/v2/design-system/typography/Typography.stories.tsx +109 -0
- package/src/v2/index.ts +43 -7
- package/src/v2/navigation/DashboardLayout/AdminSidebar.scss +207 -0
- package/src/v2/navigation/DashboardLayout/AdminSidebar.tsx +171 -0
- package/src/v2/navigation/DashboardLayout/DashboardFooter.scss +30 -0
- package/src/v2/navigation/DashboardLayout/DashboardFooter.tsx +25 -0
- package/src/v2/navigation/DashboardLayout/DashboardLayout.scss +91 -0
- package/src/v2/navigation/DashboardLayout/DashboardLayout.stories.tsx +370 -0
- package/src/v2/navigation/DashboardLayout/DashboardLayout.tsx +233 -0
- package/src/v2/navigation/DashboardLayout/ProviderSidebar.scss +271 -0
- package/src/v2/navigation/DashboardLayout/ProviderSidebar.tsx +266 -0
- package/src/v2/navigation/DashboardLayout/Sidebar.stories.tsx +220 -0
- package/src/v2/navigation/DashboardLayout/TopNavigation.scss +206 -0
- package/src/v2/navigation/DashboardLayout/TopNavigation.tsx +279 -0
- package/src/v2/navigation/DashboardLayout/assets/AdminLogo.png +0 -0
- package/src/v2/navigation/DashboardLayout/assets/BookedLogo_Mark.png +0 -0
- package/src/v2/navigation/DashboardLayout/index.ts +20 -0
- package/src/v2/styles/index.scss +0 -1
- package/src/v2/styles/tokens/_colors.scss +531 -98
- package/dist/v2/components/Colors/Colors.d.ts +0 -21
- package/dist/v2/components/Colors/Colors.d.ts.map +0 -1
- package/dist/v2/components/Colors/index.d.ts +0 -3
- package/dist/v2/components/Colors/index.d.ts.map +0 -1
- package/dist/v2/components/Typography/Typography.d.ts +0 -11
- package/dist/v2/components/Typography/Typography.d.ts.map +0 -1
- package/dist/v2/components/Typography/index.d.ts +0 -3
- package/dist/v2/components/Typography/index.d.ts.map +0 -1
- package/src/v2/components/Colors/Colors.scss +0 -64
- package/src/v2/components/Colors/Colors.stories.tsx +0 -143
- package/src/v2/components/Colors/Colors.tsx +0 -51
- package/src/v2/components/Colors/ColorsAliases.stories.tsx +0 -285
- package/src/v2/components/Colors/Sizes.stories.tsx +0 -141
- package/src/v2/components/Colors/index.ts +0 -2
- package/src/v2/components/Typography/Typography.scss +0 -72
- package/src/v2/components/Typography/Typography.stories.tsx +0 -266
- package/src/v2/components/Typography/Typography.tsx +0 -56
- package/src/v2/components/Typography/index.ts +0 -2
- 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,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,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
|
+
|