@@ -5,12 +5,22 @@ import { UserController } from '../../controllers/user';
55import { AuthGuard } from '../../utils/auth' ;
66import { supabase } from '../../utils/supabase' ;
77
8+ interface EventTag {
9+ type_id : number ;
10+ type_name : string ;
11+ hashtag_relation : number [ ] ;
12+ }
13+
814export const Route = createFileRoute ( '/events/create' ) ( {
915 beforeLoad : AuthGuard ,
10- component : CreateEventScreen
16+ component : CreateEventScreen ,
17+ validateSearch : ( search : Record < string , unknown > ) => {
18+ return {
19+ type : search . type as string
20+ } ;
21+ }
1122} )
1223
13-
1424const styles = {
1525 container : {
1626 flex : 1 ,
@@ -30,17 +40,74 @@ const styles = {
3040}
3141
3242function CreateEventScreen ( ) {
33- const navigate = Route . useNavigate ( )
34- const [ selectedPhotos , setSelectedPhotos ] = useState < File > ( )
35- const [ preview , setPreview ] = useState < string > ( )
43+ const navigate = Route . useNavigate ( ) ;
44+ const { type } = Route . useSearch ( ) ;
45+ const selectedType = type ? parseInt ( type ) : null ;
46+ const [ selectedPhotos , setSelectedPhotos ] = useState < File > ( ) ;
47+ const [ preview , setPreview ] = useState < string > ( ) ;
3648 const [ inputs , setInputs ] = useState ( {
3749 name : '' ,
3850 start_time : '' ,
3951 end_time : '' ,
4052 location : '' ,
4153 fee : 0 ,
42- description : ''
43- } )
54+ description : '' ,
55+ type : selectedType ,
56+ } ) ;
57+ const [ eventTypeInfo , setEventTypeInfo ] = useState < { type_id : number , type_name : string } | null > ( null ) ;
58+ const [ eventTags , setEventTags ] = useState < EventTag [ ] > ( [ ] ) ;
59+ const [ loading , setLoading ] = useState ( true ) ;
60+ const [ selectedHashtags , setSelectedHashtags ] = useState < number [ ] > ( [ ] ) ;
61+ // Fetch event type information
62+ useEffect ( ( ) => {
63+ async function fetchTypeInfo ( ) {
64+ if ( selectedType ) {
65+ const { data, error } = await supabase
66+ . from ( 'event_type' )
67+ . select ( 'type_id, type_name' )
68+ . eq ( 'type_id' , selectedType )
69+ . single ( ) ;
70+
71+ if ( ! error && data ) {
72+ setEventTypeInfo ( data ) ;
73+ }
74+ }
75+ }
76+
77+ fetchTypeInfo ( ) ;
78+ } , [ selectedType ] ) ;
79+
80+ useEffect ( ( ) => {
81+ async function fetchEventTags ( ) {
82+ try {
83+ setLoading ( true ) ;
84+
85+ if ( ! selectedType ) {
86+ setEventTags ( [ ] ) ;
87+ return ;
88+ }
89+
90+ // Find all hashtags that are associated with this event type
91+ // These are records where hashtag_relation contains the selected type_id
92+ const { data, error } = await supabase
93+ . from ( 'event_type' )
94+ . select ( '*' )
95+ . not ( 'hashtag_relation' , 'eq' , [ 0 ] ) // Exclude main event types
96+ . contains ( 'hashtag_relation' , [ selectedType ] ) // Find hashtags related to this event type
97+ . order ( 'type_id' , { ascending : true } ) ;
98+
99+ if ( error ) throw error ;
100+
101+ setEventTags ( data || [ ] ) ;
102+ } catch ( error ) {
103+ console . error ( 'Error fetching event tags:' , error ) ;
104+ } finally {
105+ setLoading ( false ) ;
106+ }
107+ }
108+
109+ fetchEventTags ( ) ;
110+ } , [ selectedType ] ) ;
44111
45112 // create a preview as a side effect, whenever selected file is changed
46113 useEffect ( ( ) => {
@@ -64,28 +131,47 @@ function CreateEventScreen() {
64131 setSelectedPhotos ( e . target . files [ 0 ] )
65132 }
66133
67-
68134 async function addEvent ( e : FormEvent ) {
69135 e . preventDefault ( )
70- const { data, error } = await supabase
71- . from ( 'events' )
72- . insert ( {
136+
137+ if ( ! selectedType ) {
138+ alert ( '請先選擇活動類型' ) ;
139+ navigate ( { to : '/events/select' } ) ;
140+ return ;
141+ }
142+
143+ try {
144+ // Insert the event with the basic data and hashtags
145+ const eventInsertData = {
73146 ...inputs ,
74- user_id : ( await UserController . get ( ) ) . id
75- } )
76- . select ( '*' )
77- . single ( )
147+ user_id : ( await UserController . get ( ) ) . id ,
148+ type : selectedType ,
149+ // Store the selected hashtags as an array (even if empty)
150+ hashtags : selectedHashtags
151+ } ;
152+
153+ // Insert the event
154+ const { data : createdEvent , error : eventError } = await supabase
155+ . from ( 'events' )
156+ . insert ( eventInsertData )
157+ . select ( '*' )
158+ . single ( ) ;
78159
79- if ( error !== null ) {
80- throw error
81- }
160+ if ( eventError ) throw eventError ;
161+
162+ console . log ( 'Event created with hashtags:' , selectedHashtags ) ;
82163
83- navigate ( {
84- to : '/events/$eventId' ,
85- params : {
86- 'eventId' : data . id . toString ( )
87- }
88- } )
164+ // Navigate to the event page
165+ navigate ( {
166+ to : '/events/$eventId' ,
167+ params : {
168+ 'eventId' : createdEvent . id . toString ( )
169+ }
170+ } ) ;
171+ } catch ( error ) {
172+ console . error ( 'Error creating event:' , error ) ;
173+ alert ( '建立活動時發生錯誤' ) ;
174+ }
89175 }
90176
91177 return (
@@ -96,6 +182,50 @@ function CreateEventScreen() {
96182 </ Link >
97183 < h1 className = 'text-xl text-white' style = { { marginLeft : 60 } } > 新增活動</ h1 >
98184 </ div >
185+
186+ { /* Display selected type */ }
187+ < div className = "px-4 py-2" >
188+ < div className = "bg-gray-700 rounded-lg p-3 mb-4" >
189+ < h3 className = "text-white font-bold mb-2" > 已選擇的類型</ h3 >
190+ < div className = "flex flex-wrap gap-2" >
191+ { eventTypeInfo && (
192+ < span className = "badge badge-primary badge-lg" > { eventTypeInfo . type_name } </ span >
193+ ) }
194+ </ div >
195+ </ div >
196+ </ div >
197+
198+ { /* Display hashtags selection */ }
199+ < div className = "px-4 py-2" >
200+ < div className = "bg-gray-700 rounded-lg p-3 mb-4" >
201+ < h3 className = "text-white font-bold mb-2" > 選擇標籤 (可多選)</ h3 >
202+ < div className = "flex flex-wrap gap-2" >
203+ { loading ? (
204+ < p className = "text-white" > 載入中...</ p >
205+ ) : eventTags . length > 0 ? (
206+ eventTags . map ( ( tag ) => (
207+ < button
208+ key = { tag . type_id }
209+ type = "button"
210+ className = { `badge ${ selectedHashtags . includes ( tag . type_id ) ? 'badge-primary' : 'badge-outline' } badge-lg cursor-pointer` }
211+ onClick = { ( ) => {
212+ setSelectedHashtags ( prev =>
213+ prev . includes ( tag . type_id )
214+ ? prev . filter ( id => id !== tag . type_id )
215+ : [ ...prev , tag . type_id ]
216+ ) ;
217+ } }
218+ >
219+ { tag . type_name }
220+ </ button >
221+ ) )
222+ ) : (
223+ < p className = "text-white" > 沒有可用的標籤</ p >
224+ ) }
225+ </ div >
226+ </ div >
227+ </ div >
228+
99229 < div className = "grid gap-3 grid-cols-1" >
100230 < h2 style = { styles . text } > 活動名稱</ h2 >
101231 < input
0 commit comments