11import { Input } from "@/components/ui/input" ;
22import { checkCampaignSlugAvailability } from "@/models/campaign" ;
33import { createProperSlug } from "@/models/slug" ;
4- import { useState } from "react" ;
5-
6- export default function SlugInput ( { orgId, name, value, currentSlug, onChange, onBlur, updateSlugAvailable, dict } : { orgId : string , name : string , value : string , currentSlug ?: string , onChange : ( value : string ) => void , onBlur : ( ) => void , updateSlugAvailable ?: ( available : boolean ) => void , dict : any } ) {
7- const [ slugAvailable , setSlugAvailable ] = useState ( true ) ;
8-
9- const checkSlugAvailability = async ( ) => {
10- if ( value !== currentSlug ) {
11- try {
12- await checkCampaignSlugAvailability ( orgId , value ) ;
13- setSlugAvailable ( true ) ;
14- updateSlugAvailable ?.( true ) ;
15- } catch ( _ ) {
16- setSlugAvailable ( false ) ;
17- updateSlugAvailable ?.( false ) ;
18- }
19- }
20- }
4+ import { useEffect , useState } from "react" ;
5+
6+ export default function SlugInput ( {
7+ orgId,
8+ name,
9+ value,
10+ currentSlug,
11+ onChange,
12+ onBlur,
13+ updateSlugAvailable,
14+ dict,
15+ } : {
16+ orgId : string ;
17+ name : string ;
18+ value : string ;
19+ placeholder ?: string ;
20+ currentSlug ?: string ;
21+ onChange : ( value : string ) => void ;
22+ onBlur : ( ) => void ;
23+ updateSlugAvailable ?: ( available : boolean ) => void ;
24+ dict : any ;
25+ } ) {
26+ const [ slugAvailable , setSlugAvailable ] = useState ( true ) ;
27+ const [ valueOverride , setValueOverride ] = useState ( false ) ;
28+
29+ const effectiveSlug = valueOverride ? value : ( currentSlug ?? value ) ;
2130
22- const handleBlur = ( ) => {
23- checkSlugAvailability ( ) ;
24- onBlur ( ) ;
31+ // Keep parent slug state in sync with the autofilled suggestion
32+ // until the user manually edits the field.
33+ useEffect ( ( ) => {
34+ if ( valueOverride ) return ;
35+ if ( ! currentSlug ) return ;
36+ if ( value === currentSlug ) return ;
37+ onChange ( currentSlug ) ;
38+ } , [ currentSlug , value , valueOverride , onChange ] ) ;
39+
40+ const checkSlugAvailability = async ( slugToCheck : string ) => {
41+ if ( ! slugToCheck ) return ;
42+
43+ try {
44+ await checkCampaignSlugAvailability ( orgId , slugToCheck ) ;
45+ setSlugAvailable ( true ) ;
46+ updateSlugAvailable ?.( true ) ;
47+ } catch ( _ ) {
48+ setSlugAvailable ( false ) ;
49+ updateSlugAvailable ?.( false ) ;
2550 }
51+ } ;
52+
53+ const handleBlur = ( ) => {
54+ checkSlugAvailability ( effectiveSlug ) ;
55+ onBlur ( ) ;
56+ } ;
2657
58+ const handleChange = ( e : React . ChangeEvent < HTMLInputElement > ) => {
59+ onChange ( e . target . value ) ;
60+ setValueOverride ( true ) ;
61+ } ;
2762
28- return (
29- < >
30- { name && < p className = "text-sm text-gray-500" > { dict . dashboard . suggested_slug } : < span className = "bg-gray-100 border rounded px-1" > { name ? createProperSlug ( name ) : "" } </ span > </ p > }
31- < Input className = "max-w-[300px]" type = "text" value = { value } onChange = { ( e ) => onChange ( e . target . value ) } onBlur = { handleBlur } />
32- < p className = "text-red-500 text-xs" > { ! slugAvailable && dict . dashboard . slug_not_available } </ p >
33- </ >
34- )
35- }
63+ return (
64+ < >
65+ { name && (
66+ < p className = "text-sm text-gray-500" >
67+ { dict . dashboard . suggested_slug } :{ " " }
68+ < span className = "bg-gray-100 border rounded px-1" >
69+ { name ? createProperSlug ( name ) : "" }
70+ </ span >
71+ </ p >
72+ ) }
73+ < Input
74+ className = "max-w-[300px]"
75+ type = "text"
76+ value = { effectiveSlug }
77+ onChange = { ( e ) => handleChange ( e ) }
78+ onBlur = { handleBlur }
79+ />
80+ < p className = "text-red-500 text-xs" >
81+ { ! slugAvailable && dict . dashboard . slug_not_available }
82+ </ p >
83+ </ >
84+ ) ;
85+ }
0 commit comments