@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.
Files changed (123) hide show
  1. package/dist/styles/themes/bookedit/_fonts.scss +2 -0
  2. package/dist/v2/components/ActionMenu/ActionMenu.d.ts.map +1 -1
  3. package/dist/v2/components/ActionMenu/ActionMenu.js +5 -3
  4. package/dist/v2/components/AvatarUpload/AvatarUpload.d.ts +12 -0
  5. package/dist/v2/components/AvatarUpload/AvatarUpload.d.ts.map +1 -0
  6. package/dist/v2/components/AvatarUpload/index.d.ts +2 -0
  7. package/dist/v2/components/AvatarUpload/index.d.ts.map +1 -0
  8. package/dist/v2/components/Button/Button.d.ts +1 -1
  9. package/dist/v2/components/Button/Button.d.ts.map +1 -1
  10. package/dist/v2/components/Button/Button.scss.js +1 -1
  11. package/dist/v2/components/DataTable/DataTable.d.ts +41 -0
  12. package/dist/v2/components/DataTable/DataTable.d.ts.map +1 -0
  13. package/dist/v2/components/DataTable/index.d.ts +3 -0
  14. package/dist/v2/components/DataTable/index.d.ts.map +1 -0
  15. package/dist/v2/components/EmptyState/EmptyState.d.ts +14 -0
  16. package/dist/v2/components/EmptyState/EmptyState.d.ts.map +1 -0
  17. package/dist/v2/components/EmptyState/index.d.ts +3 -0
  18. package/dist/v2/components/EmptyState/index.d.ts.map +1 -0
  19. package/dist/v2/components/FormField/FormField.scss.js +1 -1
  20. package/dist/v2/components/InfoGrid/InfoGrid.d.ts +13 -0
  21. package/dist/v2/components/InfoGrid/InfoGrid.d.ts.map +1 -0
  22. package/dist/v2/components/InfoGrid/index.d.ts +2 -0
  23. package/dist/v2/components/InfoGrid/index.d.ts.map +1 -0
  24. package/dist/v2/components/NewTable/NewTable.scss.js +1 -1
  25. package/dist/v2/components/RadioCard/RadioCard.d.ts +17 -0
  26. package/dist/v2/components/RadioCard/RadioCard.d.ts.map +1 -0
  27. package/dist/v2/components/RadioCard/index.d.ts +2 -0
  28. package/dist/v2/components/RadioCard/index.d.ts.map +1 -0
  29. package/dist/v2/components/StatusBadge/StatusBadge.d.ts +8 -0
  30. package/dist/v2/components/StatusBadge/StatusBadge.d.ts.map +1 -0
  31. package/dist/v2/components/StatusBadge/index.d.ts +3 -0
  32. package/dist/v2/components/StatusBadge/index.d.ts.map +1 -0
  33. package/dist/v2/components/StepIndicator/StepIndicator.d.ts +9 -0
  34. package/dist/v2/components/StepIndicator/StepIndicator.d.ts.map +1 -0
  35. package/dist/v2/components/StepIndicator/index.d.ts +2 -0
  36. package/dist/v2/components/StepIndicator/index.d.ts.map +1 -0
  37. package/dist/v2/components/TableControls/TableControls.d.ts +28 -0
  38. package/dist/v2/components/TableControls/TableControls.d.ts.map +1 -0
  39. package/dist/v2/components/TableControls/index.d.ts +3 -0
  40. package/dist/v2/components/TableControls/index.d.ts.map +1 -0
  41. package/dist/v2/components/Tabs/Tabs.d.ts +15 -0
  42. package/dist/v2/components/Tabs/Tabs.d.ts.map +1 -0
  43. package/dist/v2/components/Tabs/index.d.ts +3 -0
  44. package/dist/v2/components/Tabs/index.d.ts.map +1 -0
  45. package/dist/v2/icons/index.d.ts +42 -0
  46. package/dist/v2/icons/index.d.ts.map +1 -1
  47. package/dist/v2/index.d.ts +18 -0
  48. package/dist/v2/index.d.ts.map +1 -1
  49. package/dist/v2/pages/CreateUser/CreateUserPage.d.ts +110 -0
  50. package/dist/v2/pages/CreateUser/CreateUserPage.d.ts.map +1 -0
  51. package/dist/v2/pages/CreateUser/index.d.ts +3 -0
  52. package/dist/v2/pages/CreateUser/index.d.ts.map +1 -0
  53. package/dist/v2/pages/RoleSelection/RoleSelectionPage.d.ts +26 -0
  54. package/dist/v2/pages/RoleSelection/RoleSelectionPage.d.ts.map +1 -0
  55. package/dist/v2/pages/RoleSelection/index.d.ts +3 -0
  56. package/dist/v2/pages/RoleSelection/index.d.ts.map +1 -0
  57. package/dist/v2/pages/UserDetails/UserDetailsPage.d.ts +37 -0
  58. package/dist/v2/pages/UserDetails/UserDetailsPage.d.ts.map +1 -0
  59. package/dist/v2/pages/UserDetails/index.d.ts +3 -0
  60. package/dist/v2/pages/UserDetails/index.d.ts.map +1 -0
  61. package/dist/v2/pages/auth/CreatePassword/CreatePasswordPage.d.ts.map +1 -1
  62. package/dist/v2/pages/auth/Login/LoginPage.d.ts.map +1 -1
  63. package/dist/v2/pages/auth/ResetPassword/ResetPasswordPage.d.ts.map +1 -1
  64. package/dist/v2/styles/components/Button.scss +27 -0
  65. package/package.json +2 -2
  66. package/src/styles/themes/bookedit/_fonts.scss +2 -0
  67. package/src/v2/components/ActionMenu/ActionMenu.tsx +4 -2
  68. package/src/v2/components/AvatarUpload/AvatarUpload.scss +68 -0
  69. package/src/v2/components/AvatarUpload/AvatarUpload.stories.tsx +83 -0
  70. package/src/v2/components/AvatarUpload/AvatarUpload.tsx +69 -0
  71. package/src/v2/components/AvatarUpload/index.ts +1 -0
  72. package/src/v2/components/Button/Button.tsx +1 -0
  73. package/src/v2/components/DataTable/DataTable.scss +181 -0
  74. package/src/v2/components/DataTable/DataTable.tsx +256 -0
  75. package/src/v2/components/DataTable/index.ts +7 -0
  76. package/src/v2/components/EmptyState/EmptyState.scss +39 -0
  77. package/src/v2/components/EmptyState/EmptyState.stories.tsx +45 -0
  78. package/src/v2/components/EmptyState/EmptyState.tsx +37 -0
  79. package/src/v2/components/EmptyState/index.ts +2 -0
  80. package/src/v2/components/FormField/FormField.scss +12 -0
  81. package/src/v2/components/InfoGrid/InfoGrid.scss +51 -0
  82. package/src/v2/components/InfoGrid/InfoGrid.stories.tsx +76 -0
  83. package/src/v2/components/InfoGrid/InfoGrid.tsx +28 -0
  84. package/src/v2/components/InfoGrid/index.ts +1 -0
  85. package/src/v2/components/NewTable/NewTable.scss +4 -4
  86. package/src/v2/components/RadioCard/RadioCard.scss +76 -0
  87. package/src/v2/components/RadioCard/RadioCard.stories.tsx +115 -0
  88. package/src/v2/components/RadioCard/RadioCard.tsx +68 -0
  89. package/src/v2/components/RadioCard/index.ts +1 -0
  90. package/src/v2/components/StatusBadge/StatusBadge.scss +53 -0
  91. package/src/v2/components/StatusBadge/StatusBadge.tsx +31 -0
  92. package/src/v2/components/StatusBadge/index.ts +2 -0
  93. package/src/v2/components/StepIndicator/StepIndicator.scss +62 -0
  94. package/src/v2/components/StepIndicator/StepIndicator.stories.tsx +37 -0
  95. package/src/v2/components/StepIndicator/StepIndicator.tsx +41 -0
  96. package/src/v2/components/StepIndicator/index.ts +1 -0
  97. package/src/v2/components/TableControls/TableControls.scss +63 -0
  98. package/src/v2/components/TableControls/TableControls.tsx +110 -0
  99. package/src/v2/components/TableControls/index.ts +7 -0
  100. package/src/v2/components/Tabs/Tabs.scss +36 -0
  101. package/src/v2/components/Tabs/Tabs.stories.tsx +75 -0
  102. package/src/v2/components/Tabs/Tabs.tsx +52 -0
  103. package/src/v2/components/Tabs/index.ts +2 -0
  104. package/src/v2/icons/index.tsx +219 -0
  105. package/src/v2/index.ts +98 -0
  106. package/src/v2/pages/CreateUser/CreateUserPage.scss +760 -0
  107. package/src/v2/pages/CreateUser/CreateUserPage.stories.tsx +157 -0
  108. package/src/v2/pages/CreateUser/CreateUserPage.tsx +1062 -0
  109. package/src/v2/pages/CreateUser/index.ts +13 -0
  110. package/src/v2/pages/RoleSelection/RoleSelectionPage.scss +193 -0
  111. package/src/v2/pages/RoleSelection/RoleSelectionPage.stories.tsx +112 -0
  112. package/src/v2/pages/RoleSelection/RoleSelectionPage.tsx +127 -0
  113. package/src/v2/pages/RoleSelection/index.ts +2 -0
  114. package/src/v2/pages/UserDetails/UserDetailsPage.scss +236 -0
  115. package/src/v2/pages/UserDetails/UserDetailsPage.stories.tsx +84 -0
  116. package/src/v2/pages/UserDetails/UserDetailsPage.tsx +210 -0
  117. package/src/v2/pages/UserDetails/index.ts +2 -0
  118. package/src/v2/pages/auth/AuthLayout/AuthLayout.scss +8 -6
  119. package/src/v2/pages/auth/CreatePassword/CreatePasswordPage.tsx +1 -3
  120. package/src/v2/pages/auth/Login/LoginPage.tsx +1 -3
  121. package/src/v2/pages/auth/ResetPassword/ResetPasswordPage.scss +2 -0
  122. package/src/v2/pages/auth/ResetPassword/ResetPasswordPage.tsx +1 -2
  123. 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 24px;
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 24px;
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 16px;
87
+ padding: 10px 12px;
88
88
  font-size: 12px;
89
89
  }
90
90
 
91
91
  &__cell {
92
- padding: 12px 16px;
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,2 @@
1
+ export { StatusBadge } from './StatusBadge'
2
+ export type { StatusBadgeProps, StatusBadgeStatus } from './StatusBadge'
@@ -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
+ }