@licklist/design 0.78.5-dev.69 → 0.78.5-dev.70
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/styles/themes/bookedit/_fonts.scss +2 -0
- package/dist/v2/components/ActionMenu/ActionMenu.d.ts.map +1 -1
- package/dist/v2/components/ActionMenu/ActionMenu.js +5 -3
- package/dist/v2/components/AvatarUpload/AvatarUpload.d.ts +12 -0
- package/dist/v2/components/AvatarUpload/AvatarUpload.d.ts.map +1 -0
- package/dist/v2/components/AvatarUpload/index.d.ts +2 -0
- package/dist/v2/components/AvatarUpload/index.d.ts.map +1 -0
- package/dist/v2/components/Button/Button.d.ts +1 -1
- package/dist/v2/components/Button/Button.d.ts.map +1 -1
- package/dist/v2/components/Button/Button.scss.js +1 -1
- package/dist/v2/components/DataTable/DataTable.d.ts +41 -0
- package/dist/v2/components/DataTable/DataTable.d.ts.map +1 -0
- package/dist/v2/components/DataTable/index.d.ts +3 -0
- package/dist/v2/components/DataTable/index.d.ts.map +1 -0
- package/dist/v2/components/EmptyState/EmptyState.d.ts +14 -0
- package/dist/v2/components/EmptyState/EmptyState.d.ts.map +1 -0
- package/dist/v2/components/EmptyState/index.d.ts +3 -0
- package/dist/v2/components/EmptyState/index.d.ts.map +1 -0
- package/dist/v2/components/FormField/FormField.scss.js +1 -1
- package/dist/v2/components/InfoGrid/InfoGrid.d.ts +13 -0
- package/dist/v2/components/InfoGrid/InfoGrid.d.ts.map +1 -0
- package/dist/v2/components/InfoGrid/index.d.ts +2 -0
- package/dist/v2/components/InfoGrid/index.d.ts.map +1 -0
- package/dist/v2/components/NewTable/NewTable.scss.js +1 -1
- package/dist/v2/components/RadioCard/RadioCard.d.ts +17 -0
- package/dist/v2/components/RadioCard/RadioCard.d.ts.map +1 -0
- package/dist/v2/components/RadioCard/index.d.ts +2 -0
- package/dist/v2/components/RadioCard/index.d.ts.map +1 -0
- package/dist/v2/components/StatusBadge/StatusBadge.d.ts +8 -0
- package/dist/v2/components/StatusBadge/StatusBadge.d.ts.map +1 -0
- package/dist/v2/components/StatusBadge/index.d.ts +3 -0
- package/dist/v2/components/StatusBadge/index.d.ts.map +1 -0
- package/dist/v2/components/StepIndicator/StepIndicator.d.ts +9 -0
- package/dist/v2/components/StepIndicator/StepIndicator.d.ts.map +1 -0
- package/dist/v2/components/StepIndicator/index.d.ts +2 -0
- package/dist/v2/components/StepIndicator/index.d.ts.map +1 -0
- package/dist/v2/components/TableControls/TableControls.d.ts +28 -0
- package/dist/v2/components/TableControls/TableControls.d.ts.map +1 -0
- package/dist/v2/components/TableControls/index.d.ts +3 -0
- package/dist/v2/components/TableControls/index.d.ts.map +1 -0
- package/dist/v2/components/Tabs/Tabs.d.ts +15 -0
- package/dist/v2/components/Tabs/Tabs.d.ts.map +1 -0
- package/dist/v2/components/Tabs/index.d.ts +3 -0
- package/dist/v2/components/Tabs/index.d.ts.map +1 -0
- package/dist/v2/icons/index.d.ts +42 -0
- package/dist/v2/icons/index.d.ts.map +1 -1
- package/dist/v2/index.d.ts +18 -0
- package/dist/v2/index.d.ts.map +1 -1
- package/dist/v2/pages/CreateUser/CreateUserPage.d.ts +110 -0
- package/dist/v2/pages/CreateUser/CreateUserPage.d.ts.map +1 -0
- package/dist/v2/pages/CreateUser/index.d.ts +3 -0
- package/dist/v2/pages/CreateUser/index.d.ts.map +1 -0
- package/dist/v2/pages/RoleSelection/RoleSelectionPage.d.ts +26 -0
- package/dist/v2/pages/RoleSelection/RoleSelectionPage.d.ts.map +1 -0
- package/dist/v2/pages/RoleSelection/index.d.ts +3 -0
- package/dist/v2/pages/RoleSelection/index.d.ts.map +1 -0
- package/dist/v2/pages/UserDetails/UserDetailsPage.d.ts +37 -0
- package/dist/v2/pages/UserDetails/UserDetailsPage.d.ts.map +1 -0
- package/dist/v2/pages/UserDetails/index.d.ts +3 -0
- package/dist/v2/pages/UserDetails/index.d.ts.map +1 -0
- package/dist/v2/pages/auth/CreatePassword/CreatePasswordPage.d.ts.map +1 -1
- package/dist/v2/pages/auth/Login/LoginPage.d.ts.map +1 -1
- package/dist/v2/pages/auth/ResetPassword/ResetPasswordPage.d.ts.map +1 -1
- package/dist/v2/styles/components/Button.scss +27 -0
- package/package.json +2 -2
- package/src/styles/themes/bookedit/_fonts.scss +2 -0
- package/src/v2/components/ActionMenu/ActionMenu.tsx +4 -2
- package/src/v2/components/AvatarUpload/AvatarUpload.scss +68 -0
- package/src/v2/components/AvatarUpload/AvatarUpload.stories.tsx +83 -0
- package/src/v2/components/AvatarUpload/AvatarUpload.tsx +69 -0
- package/src/v2/components/AvatarUpload/index.ts +1 -0
- package/src/v2/components/Button/Button.tsx +1 -0
- package/src/v2/components/DataTable/DataTable.scss +181 -0
- package/src/v2/components/DataTable/DataTable.tsx +256 -0
- package/src/v2/components/DataTable/index.ts +7 -0
- package/src/v2/components/EmptyState/EmptyState.scss +39 -0
- package/src/v2/components/EmptyState/EmptyState.stories.tsx +45 -0
- package/src/v2/components/EmptyState/EmptyState.tsx +37 -0
- package/src/v2/components/EmptyState/index.ts +2 -0
- package/src/v2/components/FormField/FormField.scss +12 -0
- package/src/v2/components/InfoGrid/InfoGrid.scss +51 -0
- package/src/v2/components/InfoGrid/InfoGrid.stories.tsx +76 -0
- package/src/v2/components/InfoGrid/InfoGrid.tsx +28 -0
- package/src/v2/components/InfoGrid/index.ts +1 -0
- package/src/v2/components/NewTable/NewTable.scss +4 -4
- package/src/v2/components/RadioCard/RadioCard.scss +76 -0
- package/src/v2/components/RadioCard/RadioCard.stories.tsx +115 -0
- package/src/v2/components/RadioCard/RadioCard.tsx +68 -0
- package/src/v2/components/RadioCard/index.ts +1 -0
- package/src/v2/components/StatusBadge/StatusBadge.scss +53 -0
- package/src/v2/components/StatusBadge/StatusBadge.tsx +31 -0
- package/src/v2/components/StatusBadge/index.ts +2 -0
- package/src/v2/components/StepIndicator/StepIndicator.scss +62 -0
- package/src/v2/components/StepIndicator/StepIndicator.stories.tsx +37 -0
- package/src/v2/components/StepIndicator/StepIndicator.tsx +41 -0
- package/src/v2/components/StepIndicator/index.ts +1 -0
- package/src/v2/components/TableControls/TableControls.scss +63 -0
- package/src/v2/components/TableControls/TableControls.tsx +110 -0
- package/src/v2/components/TableControls/index.ts +7 -0
- package/src/v2/components/Tabs/Tabs.scss +36 -0
- package/src/v2/components/Tabs/Tabs.stories.tsx +75 -0
- package/src/v2/components/Tabs/Tabs.tsx +52 -0
- package/src/v2/components/Tabs/index.ts +2 -0
- package/src/v2/icons/index.tsx +219 -0
- package/src/v2/index.ts +98 -0
- package/src/v2/pages/CreateUser/CreateUserPage.scss +760 -0
- package/src/v2/pages/CreateUser/CreateUserPage.stories.tsx +157 -0
- package/src/v2/pages/CreateUser/CreateUserPage.tsx +1062 -0
- package/src/v2/pages/CreateUser/index.ts +13 -0
- package/src/v2/pages/RoleSelection/RoleSelectionPage.scss +193 -0
- package/src/v2/pages/RoleSelection/RoleSelectionPage.stories.tsx +112 -0
- package/src/v2/pages/RoleSelection/RoleSelectionPage.tsx +127 -0
- package/src/v2/pages/RoleSelection/index.ts +2 -0
- package/src/v2/pages/UserDetails/UserDetailsPage.scss +236 -0
- package/src/v2/pages/UserDetails/UserDetailsPage.stories.tsx +84 -0
- package/src/v2/pages/UserDetails/UserDetailsPage.tsx +210 -0
- package/src/v2/pages/UserDetails/index.ts +2 -0
- package/src/v2/pages/auth/AuthLayout/AuthLayout.scss +8 -6
- package/src/v2/pages/auth/CreatePassword/CreatePasswordPage.tsx +1 -3
- package/src/v2/pages/auth/Login/LoginPage.tsx +1 -3
- package/src/v2/pages/auth/ResetPassword/ResetPasswordPage.scss +2 -0
- package/src/v2/pages/auth/ResetPassword/ResetPasswordPage.tsx +1 -2
- package/src/v2/styles/components/Button.scss +27 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { Meta, StoryObj } from '@storybook/react'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
import { InfoGrid } from './InfoGrid'
|
|
4
|
+
import { Badge } from '../Badge'
|
|
5
|
+
|
|
6
|
+
export default {
|
|
7
|
+
title: 'v2/Components/InfoGrid',
|
|
8
|
+
component: InfoGrid,
|
|
9
|
+
parameters: {
|
|
10
|
+
layout: 'centered',
|
|
11
|
+
},
|
|
12
|
+
} as Meta<typeof InfoGrid>
|
|
13
|
+
|
|
14
|
+
type Story = StoryObj<typeof InfoGrid>
|
|
15
|
+
|
|
16
|
+
export const TwoColumns: Story = {
|
|
17
|
+
args: {
|
|
18
|
+
columns: 2,
|
|
19
|
+
items: [
|
|
20
|
+
{ label: 'Full Name', value: 'Jane Doe' },
|
|
21
|
+
{ label: 'Email', value: 'jane.doe@example.com' },
|
|
22
|
+
{ label: 'Phone', value: '+1 (555) 000-1234' },
|
|
23
|
+
{ label: 'Member Since', value: '12 Jan 2023' },
|
|
24
|
+
],
|
|
25
|
+
},
|
|
26
|
+
decorators: [
|
|
27
|
+
(Story) => (
|
|
28
|
+
<div style={{ width: 480 }}>
|
|
29
|
+
<Story />
|
|
30
|
+
</div>
|
|
31
|
+
),
|
|
32
|
+
],
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const ThreeColumns: Story = {
|
|
36
|
+
args: {
|
|
37
|
+
columns: 3,
|
|
38
|
+
items: [
|
|
39
|
+
{ label: 'Provider', value: 'Acme Adventures' },
|
|
40
|
+
{ label: 'Location', value: 'Sydney, NSW' },
|
|
41
|
+
{ label: 'Category', value: 'Outdoor' },
|
|
42
|
+
{ label: 'Total Bookings', value: '1,204' },
|
|
43
|
+
{ label: 'Revenue', value: '$48,320' },
|
|
44
|
+
{ label: 'Rating', value: '4.8 / 5' },
|
|
45
|
+
],
|
|
46
|
+
},
|
|
47
|
+
decorators: [
|
|
48
|
+
(Story) => (
|
|
49
|
+
<div style={{ width: 720 }}>
|
|
50
|
+
<Story />
|
|
51
|
+
</div>
|
|
52
|
+
),
|
|
53
|
+
],
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export const WithReactNodeValues: Story = {
|
|
57
|
+
args: {
|
|
58
|
+
columns: 2,
|
|
59
|
+
items: [
|
|
60
|
+
{ label: 'Full Name', value: 'John Smith' },
|
|
61
|
+
{
|
|
62
|
+
label: 'Status',
|
|
63
|
+
value: <Badge variant="success">Active</Badge>,
|
|
64
|
+
},
|
|
65
|
+
{ label: 'Role', value: 'Manager' },
|
|
66
|
+
{ label: 'Last Login', value: '2 hours ago' },
|
|
67
|
+
],
|
|
68
|
+
},
|
|
69
|
+
decorators: [
|
|
70
|
+
(Story) => (
|
|
71
|
+
<div style={{ width: 480 }}>
|
|
72
|
+
<Story />
|
|
73
|
+
</div>
|
|
74
|
+
),
|
|
75
|
+
],
|
|
76
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import './InfoGrid.scss'
|
|
3
|
+
|
|
4
|
+
export interface InfoGridItem {
|
|
5
|
+
label: string
|
|
6
|
+
value: React.ReactNode
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface InfoGridProps {
|
|
10
|
+
items: InfoGridItem[]
|
|
11
|
+
columns?: 2 | 3 | 4
|
|
12
|
+
className?: string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const InfoGrid: React.FC<InfoGridProps> = ({
|
|
16
|
+
items,
|
|
17
|
+
columns = 2,
|
|
18
|
+
className = '',
|
|
19
|
+
}) => (
|
|
20
|
+
<div className={`info-grid info-grid--cols-${columns} ${className}`}>
|
|
21
|
+
{items.map((item, index) => (
|
|
22
|
+
<div key={index} className="info-grid__item">
|
|
23
|
+
<span className="info-grid__label">{item.label}</span>
|
|
24
|
+
<span className="info-grid__value">{item.value}</span>
|
|
25
|
+
</div>
|
|
26
|
+
))}
|
|
27
|
+
</div>
|
|
28
|
+
)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './InfoGrid'
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
&__header-cell {
|
|
21
|
-
padding: 16px
|
|
21
|
+
padding: 14px 16px;
|
|
22
22
|
font-size: var(--text-small-size, 13px);
|
|
23
23
|
font-weight: 500;
|
|
24
24
|
color: var(--label-secondary, #9399B3);
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
&__cell {
|
|
61
|
-
padding: 16px
|
|
61
|
+
padding: 16px 16px;
|
|
62
62
|
font-size: var(--text-base-size, 14px);
|
|
63
63
|
color: var(--label-primary, #121E52);
|
|
64
64
|
vertical-align: middle;
|
|
@@ -84,12 +84,12 @@
|
|
|
84
84
|
min-width: 600px; // Ensure table doesn't collapse too much
|
|
85
85
|
|
|
86
86
|
&__header-cell {
|
|
87
|
-
padding: 12px
|
|
87
|
+
padding: 10px 12px;
|
|
88
88
|
font-size: 12px;
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
&__cell {
|
|
92
|
-
padding: 12px
|
|
92
|
+
padding: 12px 12px;
|
|
93
93
|
font-size: 13px;
|
|
94
94
|
}
|
|
95
95
|
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
.radio-card {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
gap: 8px;
|
|
5
|
+
|
|
6
|
+
&__option {
|
|
7
|
+
display: flex;
|
|
8
|
+
align-items: flex-start;
|
|
9
|
+
gap: 12px;
|
|
10
|
+
padding: 14px 16px;
|
|
11
|
+
border: 1.5px solid var(--border-primary, #e8e9ef);
|
|
12
|
+
border-radius: 8px;
|
|
13
|
+
cursor: pointer;
|
|
14
|
+
background: var(--surface-primary, #ffffff);
|
|
15
|
+
transition: border-color 0.15s, background 0.15s;
|
|
16
|
+
|
|
17
|
+
&--selected {
|
|
18
|
+
border-color: var(--border-selected, #6200ee);
|
|
19
|
+
background: var(--surface-action-soft, #f3e8ff);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
&--disabled {
|
|
23
|
+
opacity: 0.5;
|
|
24
|
+
cursor: not-allowed;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
&__radio-input {
|
|
29
|
+
position: absolute;
|
|
30
|
+
opacity: 0;
|
|
31
|
+
width: 0;
|
|
32
|
+
height: 0;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
&__radio {
|
|
36
|
+
width: 18px;
|
|
37
|
+
height: 18px;
|
|
38
|
+
border-radius: 50%;
|
|
39
|
+
border: 1.5px solid var(--border-primary, #e8e9ef);
|
|
40
|
+
flex-shrink: 0;
|
|
41
|
+
margin-top: 2px;
|
|
42
|
+
display: flex;
|
|
43
|
+
align-items: center;
|
|
44
|
+
justify-content: center;
|
|
45
|
+
|
|
46
|
+
&--selected {
|
|
47
|
+
border-color: var(--border-selected, #6200ee);
|
|
48
|
+
|
|
49
|
+
&::after {
|
|
50
|
+
content: '';
|
|
51
|
+
width: 8px;
|
|
52
|
+
height: 8px;
|
|
53
|
+
border-radius: 50%;
|
|
54
|
+
background: var(--border-selected, #6200ee);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
&__text {
|
|
60
|
+
flex: 1;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
&__label {
|
|
64
|
+
font-size: 14px;
|
|
65
|
+
font-weight: 600;
|
|
66
|
+
color: var(--label-primary, #121e52);
|
|
67
|
+
display: block;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
&__description {
|
|
71
|
+
font-size: 13px;
|
|
72
|
+
color: var(--label-secondary, #626a90);
|
|
73
|
+
margin-top: 2px;
|
|
74
|
+
display: block;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react'
|
|
2
|
+
import React, { useState } from 'react'
|
|
3
|
+
import { RadioCard } from './RadioCard'
|
|
4
|
+
|
|
5
|
+
const meta: Meta<typeof RadioCard> = {
|
|
6
|
+
title: 'v2/Components/RadioCard',
|
|
7
|
+
component: RadioCard,
|
|
8
|
+
parameters: {
|
|
9
|
+
layout: 'centered',
|
|
10
|
+
},
|
|
11
|
+
tags: ['autodocs'],
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export default meta
|
|
15
|
+
type Story = StoryObj<typeof RadioCard>
|
|
16
|
+
|
|
17
|
+
export const Default: Story = {
|
|
18
|
+
render: () => {
|
|
19
|
+
const Wrapper = () => {
|
|
20
|
+
const [value, setValue] = useState('manager')
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<div style={{ width: 400 }}>
|
|
24
|
+
<RadioCard
|
|
25
|
+
name="role-default"
|
|
26
|
+
value={value}
|
|
27
|
+
onChange={setValue}
|
|
28
|
+
options={[
|
|
29
|
+
{
|
|
30
|
+
value: 'manager',
|
|
31
|
+
label: 'Manager',
|
|
32
|
+
description: 'Full access to manage staff, bookings, and settings.',
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
value: 'sub-manager',
|
|
36
|
+
label: 'Sub-Manager',
|
|
37
|
+
description: 'Can manage bookings and staff but cannot change settings.',
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
value: 'viewer',
|
|
41
|
+
label: 'Viewer',
|
|
42
|
+
description: 'Read-only access to reports and bookings.',
|
|
43
|
+
},
|
|
44
|
+
]}
|
|
45
|
+
/>
|
|
46
|
+
</div>
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return <Wrapper />
|
|
51
|
+
},
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export const NoDescriptions: Story = {
|
|
55
|
+
render: () => {
|
|
56
|
+
const Wrapper = () => {
|
|
57
|
+
const [value, setValue] = useState('option-a')
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<div style={{ width: 400 }}>
|
|
61
|
+
<RadioCard
|
|
62
|
+
name="role-no-desc"
|
|
63
|
+
value={value}
|
|
64
|
+
onChange={setValue}
|
|
65
|
+
options={[
|
|
66
|
+
{ value: 'option-a', label: 'Option A' },
|
|
67
|
+
{ value: 'option-b', label: 'Option B' },
|
|
68
|
+
{ value: 'option-c', label: 'Option C' },
|
|
69
|
+
]}
|
|
70
|
+
/>
|
|
71
|
+
</div>
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return <Wrapper />
|
|
76
|
+
},
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export const WithDisabled: Story = {
|
|
80
|
+
render: () => {
|
|
81
|
+
const Wrapper = () => {
|
|
82
|
+
const [value, setValue] = useState('manager')
|
|
83
|
+
|
|
84
|
+
return (
|
|
85
|
+
<div style={{ width: 400 }}>
|
|
86
|
+
<RadioCard
|
|
87
|
+
name="role-disabled"
|
|
88
|
+
value={value}
|
|
89
|
+
onChange={setValue}
|
|
90
|
+
options={[
|
|
91
|
+
{
|
|
92
|
+
value: 'manager',
|
|
93
|
+
label: 'Manager',
|
|
94
|
+
description: 'Full access to manage staff, bookings, and settings.',
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
value: 'sub-manager',
|
|
98
|
+
label: 'Sub-Manager',
|
|
99
|
+
description: 'Can manage bookings and staff but cannot change settings.',
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
value: 'viewer',
|
|
103
|
+
label: 'Viewer',
|
|
104
|
+
description: 'Read-only access. This option is currently unavailable.',
|
|
105
|
+
disabled: true,
|
|
106
|
+
},
|
|
107
|
+
]}
|
|
108
|
+
/>
|
|
109
|
+
</div>
|
|
110
|
+
)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return <Wrapper />
|
|
114
|
+
},
|
|
115
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import './RadioCard.scss'
|
|
3
|
+
|
|
4
|
+
export interface RadioCardOption {
|
|
5
|
+
value: string
|
|
6
|
+
label: string
|
|
7
|
+
description?: string
|
|
8
|
+
disabled?: boolean
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface RadioCardProps {
|
|
12
|
+
options: RadioCardOption[]
|
|
13
|
+
value: string
|
|
14
|
+
onChange(value: string): void
|
|
15
|
+
name: string
|
|
16
|
+
className?: string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const RadioCard: React.FC<RadioCardProps> = ({
|
|
20
|
+
options,
|
|
21
|
+
value,
|
|
22
|
+
onChange,
|
|
23
|
+
name,
|
|
24
|
+
className = '',
|
|
25
|
+
}) => (
|
|
26
|
+
<div className={`radio-card ${className}`}>
|
|
27
|
+
{options.map((option) => {
|
|
28
|
+
const isSelected = option.value === value
|
|
29
|
+
const isDisabled = option.disabled ?? false
|
|
30
|
+
|
|
31
|
+
const optionClass = [
|
|
32
|
+
'radio-card__option',
|
|
33
|
+
isSelected ? 'radio-card__option--selected' : '',
|
|
34
|
+
isDisabled ? 'radio-card__option--disabled' : '',
|
|
35
|
+
]
|
|
36
|
+
.filter(Boolean)
|
|
37
|
+
.join(' ')
|
|
38
|
+
|
|
39
|
+
const radioClass = [
|
|
40
|
+
'radio-card__radio',
|
|
41
|
+
isSelected ? 'radio-card__radio--selected' : '',
|
|
42
|
+
]
|
|
43
|
+
.filter(Boolean)
|
|
44
|
+
.join(' ')
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<label key={option.value} className={optionClass}>
|
|
48
|
+
<input
|
|
49
|
+
type="radio"
|
|
50
|
+
name={name}
|
|
51
|
+
value={option.value}
|
|
52
|
+
checked={isSelected}
|
|
53
|
+
disabled={isDisabled}
|
|
54
|
+
onChange={() => !isDisabled && onChange(option.value)}
|
|
55
|
+
className="radio-card__radio-input"
|
|
56
|
+
/>
|
|
57
|
+
<div className={radioClass} />
|
|
58
|
+
<div className="radio-card__text">
|
|
59
|
+
<span className="radio-card__label">{option.label}</span>
|
|
60
|
+
{option.description && (
|
|
61
|
+
<span className="radio-card__description">{option.description}</span>
|
|
62
|
+
)}
|
|
63
|
+
</div>
|
|
64
|
+
</label>
|
|
65
|
+
)
|
|
66
|
+
})}
|
|
67
|
+
</div>
|
|
68
|
+
)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './RadioCard'
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
@import '../../styles/index.scss';
|
|
2
|
+
|
|
3
|
+
.status-badge {
|
|
4
|
+
display: inline-flex;
|
|
5
|
+
align-self: flex-start;
|
|
6
|
+
width: fit-content;
|
|
7
|
+
justify-content: center;
|
|
8
|
+
align-items: center;
|
|
9
|
+
padding: 4px 8px;
|
|
10
|
+
border-radius: 100px;
|
|
11
|
+
border: 1px solid transparent;
|
|
12
|
+
font-family: var(--font-family-mono, 'Geist Mono', monospace);
|
|
13
|
+
font-size: 10px;
|
|
14
|
+
font-weight: 500;
|
|
15
|
+
line-height: 13px;
|
|
16
|
+
white-space: nowrap;
|
|
17
|
+
color: var(--label-primary, #121e52);
|
|
18
|
+
|
|
19
|
+
&--active {
|
|
20
|
+
background-color: var(--surface-colour-green-soft, #eef9ea);
|
|
21
|
+
border-color: var(--colour-overlay, #d4edcc);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
&--invited {
|
|
25
|
+
background-color: var(--surface-colour-blue-soft, #e7f4fc);
|
|
26
|
+
border-color: var(--colour-overlay, #b4dbf6);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
&--suspended {
|
|
30
|
+
background-color: var(--surface-colour-yellow-soft, #fefce8);
|
|
31
|
+
border-color: var(--colour-overlay, #fde68a);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
&--imported {
|
|
35
|
+
background-color: var(--surface-colour-yellow-soft, #fefce8);
|
|
36
|
+
border-color: var(--colour-overlay, #fde68a);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
&--disabled {
|
|
40
|
+
background-color: var(--surface-colour-red-soft, #fceceb);
|
|
41
|
+
border-color: var(--colour-overlay, #f5c4c2);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
&--inactive {
|
|
45
|
+
background-color: var(--surface-secondary, #f8f8fa);
|
|
46
|
+
border-color: var(--border-primary, #e8e9ef);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
&--expired {
|
|
50
|
+
background-color: var(--surface-status-alert, #fcf6e7);
|
|
51
|
+
border-color: var(--border-status-alert, #fed7b6);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import './StatusBadge.scss'
|
|
3
|
+
|
|
4
|
+
export type StatusBadgeStatus =
|
|
5
|
+
| 'active'
|
|
6
|
+
| 'disabled'
|
|
7
|
+
| 'suspended'
|
|
8
|
+
| 'invited'
|
|
9
|
+
| 'imported'
|
|
10
|
+
| 'inactive'
|
|
11
|
+
| 'expired'
|
|
12
|
+
|
|
13
|
+
export interface StatusBadgeProps {
|
|
14
|
+
status: StatusBadgeStatus
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const STATUS_LABELS: Record<StatusBadgeStatus, string> = {
|
|
18
|
+
active: 'Active',
|
|
19
|
+
disabled: 'Disabled',
|
|
20
|
+
suspended: 'Suspended',
|
|
21
|
+
invited: 'Invited',
|
|
22
|
+
imported: 'Imported',
|
|
23
|
+
inactive: 'Inactive',
|
|
24
|
+
expired: 'Expired',
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const StatusBadge: React.FC<StatusBadgeProps> = ({ status }) => (
|
|
28
|
+
<span className={`status-badge status-badge--${status}`}>
|
|
29
|
+
{STATUS_LABELS[status]}
|
|
30
|
+
</span>
|
|
31
|
+
)
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
.step-indicator {
|
|
2
|
+
display: flex;
|
|
3
|
+
align-items: center;
|
|
4
|
+
gap: 0;
|
|
5
|
+
|
|
6
|
+
&__step {
|
|
7
|
+
display: flex;
|
|
8
|
+
align-items: center;
|
|
9
|
+
gap: 8px;
|
|
10
|
+
color: var(--label-secondary, #626a90);
|
|
11
|
+
|
|
12
|
+
&--active .step-indicator__num {
|
|
13
|
+
background: var(--border-selected, #6200ee);
|
|
14
|
+
border-color: var(--border-selected, #6200ee);
|
|
15
|
+
color: #ffffff;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
&--done .step-indicator__num {
|
|
19
|
+
background: var(--surface-action-soft, #f3e8ff);
|
|
20
|
+
border-color: var(--surface-action-soft, #f3e8ff);
|
|
21
|
+
color: var(--border-selected, #6200ee);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
&--active .step-indicator__label {
|
|
25
|
+
color: var(--label-primary, #121e52);
|
|
26
|
+
font-weight: 600;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
&--done .step-indicator__label {
|
|
30
|
+
color: var(--label-secondary, #626a90);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
&__num {
|
|
35
|
+
width: 28px;
|
|
36
|
+
height: 28px;
|
|
37
|
+
border-radius: 50%;
|
|
38
|
+
border: 1.5px solid var(--border-primary, #e8e9ef);
|
|
39
|
+
display: flex;
|
|
40
|
+
align-items: center;
|
|
41
|
+
justify-content: center;
|
|
42
|
+
font-size: 13px;
|
|
43
|
+
font-weight: 600;
|
|
44
|
+
flex-shrink: 0;
|
|
45
|
+
background: transparent;
|
|
46
|
+
color: var(--label-secondary, #626a90);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
&__label {
|
|
50
|
+
font-size: 13px;
|
|
51
|
+
font-weight: 500;
|
|
52
|
+
white-space: nowrap;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
&__divider {
|
|
56
|
+
flex: 1;
|
|
57
|
+
min-width: 24px;
|
|
58
|
+
height: 1px;
|
|
59
|
+
background: var(--border-primary, #e8e9ef);
|
|
60
|
+
margin: 0 8px;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react'
|
|
2
|
+
import { StepIndicator } from './StepIndicator'
|
|
3
|
+
|
|
4
|
+
const meta: Meta<typeof StepIndicator> = {
|
|
5
|
+
title: 'v2/Components/StepIndicator',
|
|
6
|
+
component: StepIndicator,
|
|
7
|
+
parameters: {
|
|
8
|
+
layout: 'centered',
|
|
9
|
+
},
|
|
10
|
+
tags: ['autodocs'],
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export default meta
|
|
14
|
+
type Story = StoryObj<typeof StepIndicator>
|
|
15
|
+
|
|
16
|
+
const steps = ['Email', 'Details', 'Role', 'Providers']
|
|
17
|
+
|
|
18
|
+
export const Step1of4: Story = {
|
|
19
|
+
args: {
|
|
20
|
+
steps,
|
|
21
|
+
currentStep: 1,
|
|
22
|
+
},
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const Step2of4: Story = {
|
|
26
|
+
args: {
|
|
27
|
+
steps,
|
|
28
|
+
currentStep: 2,
|
|
29
|
+
},
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const Step4of4: Story = {
|
|
33
|
+
args: {
|
|
34
|
+
steps,
|
|
35
|
+
currentStep: 4,
|
|
36
|
+
},
|
|
37
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import './StepIndicator.scss'
|
|
3
|
+
|
|
4
|
+
export interface StepIndicatorProps {
|
|
5
|
+
steps: string[]
|
|
6
|
+
currentStep: number
|
|
7
|
+
className?: string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const StepIndicator: React.FC<StepIndicatorProps> = ({
|
|
11
|
+
steps,
|
|
12
|
+
currentStep,
|
|
13
|
+
className = '',
|
|
14
|
+
}) => (
|
|
15
|
+
<div className={`step-indicator ${className}`}>
|
|
16
|
+
{steps.map((label, index) => {
|
|
17
|
+
const stepNumber = index + 1
|
|
18
|
+
const isDone = currentStep > stepNumber
|
|
19
|
+
const isActive = currentStep === stepNumber
|
|
20
|
+
const modifierClass = isDone
|
|
21
|
+
? 'step-indicator__step--done'
|
|
22
|
+
: isActive
|
|
23
|
+
? 'step-indicator__step--active'
|
|
24
|
+
: 'step-indicator__step--pending'
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<React.Fragment key={stepNumber}>
|
|
28
|
+
<div className={`step-indicator__step ${modifierClass}`}>
|
|
29
|
+
<div className="step-indicator__num">
|
|
30
|
+
{isDone ? '✓' : stepNumber}
|
|
31
|
+
</div>
|
|
32
|
+
<span className="step-indicator__label">{label}</span>
|
|
33
|
+
</div>
|
|
34
|
+
{index < steps.length - 1 && (
|
|
35
|
+
<div className="step-indicator__divider" />
|
|
36
|
+
)}
|
|
37
|
+
</React.Fragment>
|
|
38
|
+
)
|
|
39
|
+
})}
|
|
40
|
+
</div>
|
|
41
|
+
)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './StepIndicator'
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
@import '../../styles/index.scss';
|
|
2
|
+
|
|
3
|
+
.table-controls {
|
|
4
|
+
display: flex;
|
|
5
|
+
flex-direction: column;
|
|
6
|
+
gap: 16px;
|
|
7
|
+
width: 100%;
|
|
8
|
+
|
|
9
|
+
// ── Search + action row ────────────────────────────────────────────────────
|
|
10
|
+
&__top-row {
|
|
11
|
+
display: flex;
|
|
12
|
+
justify-content: space-between;
|
|
13
|
+
align-items: flex-start;
|
|
14
|
+
gap: 16px;
|
|
15
|
+
flex-wrap: wrap;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
&__search-group {
|
|
19
|
+
display: flex;
|
|
20
|
+
flex-direction: column;
|
|
21
|
+
gap: 8px;
|
|
22
|
+
flex: 1;
|
|
23
|
+
min-width: 0;
|
|
24
|
+
max-width: 480px;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
&__search-label {
|
|
28
|
+
font-size: 13px;
|
|
29
|
+
font-weight: 500;
|
|
30
|
+
line-height: 16px;
|
|
31
|
+
color: var(--label-primary, #121e52);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
&__search {
|
|
35
|
+
width: 100%;
|
|
36
|
+
height: 40px;
|
|
37
|
+
padding: 0 12px;
|
|
38
|
+
font-family: var(--font-family-sans, 'Geist', sans-serif);
|
|
39
|
+
font-size: 14px;
|
|
40
|
+
color: var(--label-primary, #121e52);
|
|
41
|
+
background: var(--surface-primary, #ffffff);
|
|
42
|
+
border: 1px solid var(--border-primary, #e8e9ef);
|
|
43
|
+
border-radius: var(--radius-md, 6px);
|
|
44
|
+
outline: none;
|
|
45
|
+
transition: border-color 0.15s;
|
|
46
|
+
box-sizing: border-box;
|
|
47
|
+
|
|
48
|
+
&::placeholder {
|
|
49
|
+
color: var(--label-tertiary, #9ea4bf);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
&:focus {
|
|
53
|
+
border-color: var(--border-selected, #6200ee);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
&__actions {
|
|
58
|
+
display: flex;
|
|
59
|
+
gap: 8px;
|
|
60
|
+
flex-shrink: 0;
|
|
61
|
+
align-items: center;
|
|
62
|
+
}
|
|
63
|
+
}
|