33import { useAutoAnimate } from "@formkit/auto-animate/react" ;
44import { useState } from "react" ;
55import type { Props } from "react-select" ;
6+ import CreatableSelect from "react-select/creatable" ;
67
8+ // ✅ NEW
79import { useIsPlatform } from "@calcom/atoms/hooks/useIsPlatform" ;
810import type { SelectClassNames } from "@calcom/features/eventtypes/lib/types" ;
911import { useLocale } from "@calcom/lib/hooks/useLocale" ;
10- import { Icon } from "@calcom/ui/components/icon" ;
11- import { Select } from "@calcom/ui/components/form" ;
12- import { Tooltip } from "@calcom/ui/components/tooltip" ;
12+ import classNames from "@calcom/ui/classNames" ;
1313import { Avatar } from "@calcom/ui/components/avatar" ;
1414import { Button } from "@calcom/ui/components/button" ;
15- import classNames from "@calcom/ui/classNames" ;
15+ import { Icon } from "@calcom/ui/components/icon" ;
16+ // import { Select } from "@calcom/ui/components/form"; ❌ remove old Select
17+ import { Tooltip } from "@calcom/ui/components/tooltip" ;
1618
1719import type { PriorityDialogCustomClassNames , WeightDialogCustomClassNames } from "./HostEditDialogs" ;
1820import { PriorityDialog , WeightDialog } from "./HostEditDialogs" ;
1921
22+ // ✅ simple email regex util
23+ const isValidEmail = ( val : string ) => / \S + @ \S + \. \S + / . test ( val ) ;
24+
2025export type CheckedSelectOption = {
21- avatar : string ;
26+ avatar ? : string ;
2227 label : string ;
2328 value : string ;
2429 priority ?: number ;
2530 weight ?: number ;
2631 isFixed ?: boolean ;
2732 disabled ?: boolean ;
2833 defaultScheduleId ?: number | null ;
34+ isPending ?: boolean ; // ✅ NEW
2935} ;
3036
3137export type CheckedTeamSelectCustomClassNames = {
@@ -44,6 +50,7 @@ export type CheckedTeamSelectCustomClassNames = {
4450 priorityDialog ?: PriorityDialogCustomClassNames ;
4551 weightDialog ?: WeightDialogCustomClassNames ;
4652} ;
53+
4754export const CheckedTeamSelect = ( {
4855 options = [ ] ,
4956 value = [ ] ,
@@ -67,19 +74,32 @@ export const CheckedTeamSelect = ({
6774
6875 return (
6976 < >
70- < Select
77+ < CreatableSelect < CheckedSelectOption , true >
7178 { ...props }
7279 name = { props . name }
7380 placeholder = { props . placeholder || t ( "select" ) }
74- isSearchable = { true }
81+ isMulti
82+ isSearchable
7583 options = { options }
7684 value = { value }
77- isMulti
7885 className = { customClassNames ?. hostsSelect ?. select }
79- innerClassNames = { customClassNames ?. hostsSelect ?. innerClassNames }
86+ classNames = { customClassNames ?. hostsSelect ?. innerClassNames as any }
87+ onChange = { ( newVal ) => props . onChange ( newVal ) }
88+ onCreateOption = { ( inputValue ) => {
89+ if ( isValidEmail ( inputValue ) ) {
90+ const newOption : CheckedSelectOption = {
91+ value : inputValue ,
92+ label : `${ inputValue } (invite pending)` ,
93+ avatar : "" , // no avatar for pending
94+ isPending : true ,
95+ } ;
96+ props . onChange ( [ ...( value || [ ] ) , newOption ] ) ;
97+ } else {
98+ alert ( "Invalid email address" ) ;
99+ }
100+ } }
80101 />
81- { /* This class name conditional looks a bit odd but it allows a seamless transition when using autoanimate
82- - Slides down from the top instead of just teleporting in from nowhere*/ }
102+
83103 < ul
84104 className = { classNames (
85105 "mb-4 mt-3 rounded-md" ,
@@ -88,83 +108,78 @@ export const CheckedTeamSelect = ({
88108 ) }
89109 ref = { animationRef } >
90110 { value . map ( ( option , index ) => (
91- < >
92- < li
93- key = { option . value }
111+ < li
112+ key = { option . value }
113+ className = { classNames (
114+ `flex px-3 py-2 ${ index === value . length - 1 ? "" : "border-subtle border-b" } ` ,
115+ customClassNames ?. selectedHostList ?. listItem ?. container
116+ ) } >
117+ { ! isPlatform && option . avatar && < Avatar size = "sm" imageSrc = { option . avatar } alt = { option . label } /> }
118+ { ( ! option . avatar || isPlatform ) && (
119+ < Icon
120+ name = "user"
121+ className = { classNames ( "mt-0.5 h-4 w-4" , customClassNames ?. selectedHostList ?. listItem ?. avatar ) }
122+ />
123+ ) }
124+ < p
94125 className = { classNames (
95- `flex px-3 py-2 ${ index === value . length - 1 ? "" : "border-subtle border-b" } ` ,
96- customClassNames ?. selectedHostList ?. listItem ?. container
126+ "text-emphasis my-auto ms-3 text-sm" ,
127+ customClassNames ?. selectedHostList ?. listItem ?. name
97128 ) } >
98- { ! isPlatform && < Avatar size = "sm" imageSrc = { option . avatar } alt = { option . label } /> }
99- { isPlatform && (
100- < Icon
101- name = "user"
102- className = { classNames (
103- "mt-0.5 h-4 w-4" ,
104- customClassNames ?. selectedHostList ?. listItem ?. avatar
105- ) }
106- />
129+ { option . label }
130+ </ p >
131+
132+ < div className = "ml-auto flex items-center" >
133+ { /* Skip priority/weight for pending emails */ }
134+ { ! option . isPending && ! option . isFixed && (
135+ < >
136+ < Tooltip content = { t ( "change_priority" ) } >
137+ < Button
138+ color = "minimal"
139+ onClick = { ( ) => {
140+ setPriorityDialogOpen ( true ) ;
141+ setCurrentOption ( option ) ;
142+ } }
143+ className = { classNames (
144+ "mr-6 h-2 p-0 text-sm hover:bg-transparent" ,
145+ getPriorityTextAndColor ( option . priority ) . color ,
146+ customClassNames ?. selectedHostList ?. listItem ?. changePriorityButton
147+ ) } >
148+ { t ( getPriorityTextAndColor ( option . priority ) . text ) }
149+ </ Button >
150+ </ Tooltip >
151+
152+ { isRRWeightsEnabled ? (
153+ < Button
154+ color = "minimal"
155+ className = { classNames (
156+ "mr-6 h-2 w-4 p-0 text-sm hover:bg-transparent" ,
157+ customClassNames ?. selectedHostList ?. listItem ?. changeWeightButton
158+ ) }
159+ onClick = { ( ) => {
160+ setWeightDialogOpen ( true ) ;
161+ setCurrentOption ( option ) ;
162+ } } >
163+ { option . weight ?? 100 } %
164+ </ Button >
165+ ) : null }
166+ </ >
107167 ) }
108- < p
168+
169+ < Icon
170+ name = "x"
171+ onClick = { ( ) => props . onChange ( value . filter ( ( item ) => item . value !== option . value ) ) }
109172 className = { classNames (
110- "text-emphasis my-auto ms-3 text-sm" ,
111- customClassNames ?. selectedHostList ?. listItem ?. name
112- ) } >
113- { option . label }
114- </ p >
115- < div className = "ml-auto flex items-center" >
116- { option && ! option . isFixed ? (
117- < >
118- < Tooltip content = { t ( "change_priority" ) } >
119- < Button
120- color = "minimal"
121- onClick = { ( ) => {
122- setPriorityDialogOpen ( true ) ;
123- setCurrentOption ( option ) ;
124- } }
125- className = { classNames (
126- "mr-6 h-2 p-0 text-sm hover:bg-transparent" ,
127- getPriorityTextAndColor ( option . priority ) . color ,
128- customClassNames ?. selectedHostList ?. listItem ?. changePriorityButton
129- ) } >
130- { t ( getPriorityTextAndColor ( option . priority ) . text ) }
131- </ Button >
132- </ Tooltip >
133- { isRRWeightsEnabled ? (
134- < Button
135- color = "minimal"
136- className = { classNames (
137- "mr-6 h-2 w-4 p-0 text-sm hover:bg-transparent" ,
138- customClassNames ?. selectedHostList ?. listItem ?. changeWeightButton
139- ) }
140- onClick = { ( ) => {
141- setWeightDialogOpen ( true ) ;
142- setCurrentOption ( option ) ;
143- } } >
144- { option . weight ?? 100 } %
145- </ Button >
146- ) : (
147- < > </ >
148- ) }
149- </ >
150- ) : (
151- < > </ >
173+ "my-auto ml-2 h-4 w-4" ,
174+ customClassNames ?. selectedHostList ?. listItem ?. removeButton
152175 ) }
153-
154- < Icon
155- name = "x"
156- onClick = { ( ) => props . onChange ( value . filter ( ( item ) => item . value !== option . value ) ) }
157- className = { classNames (
158- "my-auto ml-2 h-4 w-4" ,
159- customClassNames ?. selectedHostList ?. listItem ?. removeButton
160- ) }
161- />
162- </ div >
163- </ li >
164- </ >
176+ />
177+ </ div >
178+ </ li >
165179 ) ) }
166180 </ ul >
167- { currentOption && ! currentOption . isFixed ? (
181+
182+ { currentOption && ! currentOption . isFixed && ! currentOption . isPending && (
168183 < >
169184 < PriorityDialog
170185 isOpenDialog = { priorityDialogOpen }
@@ -181,8 +196,6 @@ export const CheckedTeamSelect = ({
181196 customClassNames = { customClassNames ?. weightDialog }
182197 />
183198 </ >
184- ) : (
185- < > </ >
186199 ) }
187200 </ >
188201 ) ;
0 commit comments