1- "use client ";
1+ import NoticeBoard from "@/components/Admin/NoticeBoard ";
22
3- import React , { useState , ChangeEvent , FormEvent } from "react" ;
4- import { useRouter } from "next/navigation" ;
5-
6- interface AnnouncementFormData {
7- clubname : string ;
8- heading : string ;
9- info : string ;
10- announcelogo : string ;
11- }
12-
13- export default function Announce ( ) : JSX . Element {
14- const router = useRouter ( ) ;
15-
16- const [ group , setGroup ] = useState ( "all" ) ;
17- const [ manualEmails , setManualEmails ] = useState ( "" ) ;
18- const [ loading , setLoading ] = useState ( false ) ;
19- const [ errors , setErrors ] = useState < string [ ] > ( [ ] ) ;
20- const [ logoPreview , setLogoPreview ] = useState ( "" ) ;
21-
22- const [ formData , setFormData ] = useState < AnnouncementFormData > ( {
23- clubname : "CAMC" ,
24- heading : "" ,
25- info : "" ,
26- announcelogo : "" ,
27- } ) ;
28-
29- const handleChange = (
30- e : ChangeEvent < HTMLInputElement | HTMLTextAreaElement >
31- ) => {
32- const { name, value } = e . target ;
33-
34- setFormData ( ( prev ) => ( {
35- ...prev ,
36- [ name ] : value ,
37- } ) ) ;
38-
39- if ( errors . length > 0 ) {
40- setErrors ( [ ] ) ;
41- }
42- } ;
43-
44- const handleLogoChange = ( e : ChangeEvent < HTMLInputElement > ) => {
45- const file = e . target . files ?. [ 0 ] ;
46-
47- if ( ! file ) {
48- return ;
49- }
50-
51- const reader = new FileReader ( ) ;
52-
53- reader . onloadend = ( ) => {
54- const imageBase64 = reader . result as string ;
55-
56- setLogoPreview ( imageBase64 ) ;
57-
58- setFormData ( ( prev ) => ( {
59- ...prev ,
60- announcelogo : imageBase64 ,
61- } ) ) ;
62- } ;
63-
64- reader . readAsDataURL ( file ) ;
65- } ;
66-
67- const validateForm = ( ) => {
68- const validationErrors : string [ ] = [ ] ;
69-
70- if ( ! formData . heading . trim ( ) ) {
71- validationErrors . push ( "Announcement heading is required." ) ;
72- }
73-
74- if ( ! formData . info . trim ( ) ) {
75- validationErrors . push ( "Announcement content is required." ) ;
76- }
77-
78- setErrors ( validationErrors ) ;
79-
80- return validationErrors . length === 0 ;
81- } ;
82-
83- const handleSubmit = async ( e : FormEvent < HTMLFormElement > ) => {
84- e . preventDefault ( ) ;
85-
86- if ( ! validateForm ( ) ) {
87- return ;
88- }
89-
90- setLoading ( true ) ;
91- setErrors ( [ ] ) ;
92-
93- try {
94- const emailsArray = manualEmails
95- . split ( "," )
96- . map ( ( email ) => email . trim ( ) )
97- . filter ( ( email ) => email . length > 0 ) ;
98-
99- const response = await fetch (
100- `${ process . env . NEXT_PUBLIC_BACKEND_URL } /api/v1/announce` ,
101- {
102- method : "POST" ,
103- headers : {
104- "Content-Type" : "application/json" ,
105- } ,
106- credentials : "include" ,
107- body : JSON . stringify ( {
108- ...formData ,
109- group,
110- emails : emailsArray ,
111- } ) ,
112- }
113- ) ;
114-
115- const result = await response . json ( ) ;
116-
117- if ( ! response . ok ) {
118- if ( result . errors && Array . isArray ( result . errors ) ) {
119- setErrors ( result . errors ) ;
120- } else {
121- setErrors ( [ result . message || "Failed to publish announcement." ] ) ;
122- }
123-
124- return ;
125- }
126-
127- alert ( result . message || "Announcement published successfully." ) ;
128-
129- setFormData ( {
130- clubname : "CAMC" ,
131- heading : "" ,
132- info : "" ,
133- announcelogo : "" ,
134- } ) ;
135-
136- setLogoPreview ( "" ) ;
137- setManualEmails ( "" ) ;
138- setGroup ( "all" ) ;
139-
140- router . push ( "/admin/noticeboardview" ) ;
141- } catch ( error : any ) {
142- setErrors ( [
143- error ?. message || "Something went wrong while publishing announcement." ,
144- ] ) ;
145- } finally {
146- setLoading ( false ) ;
147- }
148- } ;
149-
150- return (
151- < div className = "min-h-screen bg-slate-50 px-4 py-10" >
152- < div className = "mx-auto w-full max-w-2xl rounded-2xl bg-white shadow-lg border border-slate-200" >
153- < div className = "flex items-center justify-between border-b border-slate-200 px-8 py-6" >
154- < h1 className = "text-2xl font-bold text-slate-900" >
155- Create Announcement
156- </ h1 >
157-
158- < button
159- type = "button"
160- onClick = { ( ) => router . back ( ) }
161- className = "flex h-9 w-9 items-center justify-center rounded-full bg-red-500 text-white hover:bg-red-600 transition"
162- >
163- ×
164- </ button >
165- </ div >
166-
167- < form onSubmit = { handleSubmit } className = "p-8" >
168- { errors . length > 0 && (
169- < div className = "mb-6 rounded-lg border border-red-200 bg-red-50 p-4" >
170- { errors . map ( ( error , index ) => (
171- < p key = { index } className = "text-sm text-red-600" >
172- { error }
173- </ p >
174- ) ) }
175- </ div >
176- ) }
177-
178- < div className = "mb-6" >
179- < label className = "mb-2 block text-sm font-semibold text-slate-700" >
180- Club Name
181- </ label >
182-
183- < input
184- type = "text"
185- value = { formData . clubname }
186- readOnly
187- className = "w-full rounded-lg border border-slate-300 bg-slate-100 px-4 py-3 text-slate-700"
188- />
189- </ div >
190-
191- < div className = "mb-6" >
192- < label className = "mb-2 block text-sm font-semibold text-slate-700" >
193- Heading
194- </ label >
195-
196- < input
197- type = "text"
198- name = "heading"
199- value = { formData . heading }
200- onChange = { handleChange }
201- placeholder = "Enter announcement heading"
202- className = "w-full rounded-lg border border-slate-300 px-4 py-3 outline-none focus:border-blue-600"
203- />
204- </ div >
205-
206- < div className = "mb-6" >
207- < label className = "mb-2 block text-sm font-semibold text-slate-700" >
208- Announcement Content
209- </ label >
210-
211- < textarea
212- name = "info"
213- value = { formData . info }
214- onChange = { handleChange }
215- placeholder = "Write your announcement here"
216- rows = { 6 }
217- className = "w-full rounded-lg border border-slate-300 px-4 py-3 outline-none focus:border-blue-600 resize-none"
218- />
219- </ div >
220-
221- < div className = "mb-6" >
222- < label className = "mb-2 block text-sm font-semibold text-slate-700" >
223- Announcement Logo
224- </ label >
225-
226- < div className = "flex items-center gap-4" >
227- < label className = "flex h-24 w-24 cursor-pointer items-center justify-center overflow-hidden rounded-lg border border-slate-300 bg-slate-50" >
228- { logoPreview ? (
229- < img
230- src = { logoPreview }
231- alt = "Announcement logo"
232- className = "h-full w-full object-cover"
233- />
234- ) : (
235- < span className = "text-xs text-slate-400 text-center px-2" >
236- Upload Logo
237- </ span >
238- ) }
239-
240- < input
241- type = "file"
242- accept = "image/*"
243- onChange = { handleLogoChange }
244- className = "hidden"
245- />
246- </ label >
247-
248- < p className = "text-sm text-slate-500" >
249- Optional image that will appear with the announcement.
250- </ p >
251- </ div >
252- </ div >
253-
254- < div className = "mb-6" >
255- < label className = "mb-2 block text-sm font-semibold text-slate-700" >
256- Recipient Group
257- </ label >
258-
259- < select
260- value = { group }
261- onChange = { ( e ) => setGroup ( e . target . value ) }
262- className = "w-full rounded-lg border border-slate-300 px-4 py-3 outline-none focus:border-blue-600"
263- >
264- < option value = "none" > None</ option >
265- < option value = "all@iiti.ac.in" > All Students</ option >
266- < option value = "managerplacement@iiti.ac.in" > Manager</ option >
267- </ select >
268- </ div >
269-
270- < div className = "mb-8" >
271- < label className = "mb-2 block text-sm font-semibold text-slate-700" >
272- Extra Emails
273- </ label >
274-
275- < input
276- type = "text"
277- value = { manualEmails }
278- onChange = { ( e ) => setManualEmails ( e . target . value ) }
279- placeholder = "example1@gmail.com, example2@gmail.com"
280- className = "w-full rounded-lg border border-slate-300 px-4 py-3 outline-none focus:border-blue-600"
281- />
282-
283- < p className = "mt-2 text-sm text-slate-500" >
284- Enter additional email addresses separated by commas.
285- </ p >
286- </ div >
287-
288- < button
289- type = "submit"
290- disabled = { loading }
291- className = "w-full rounded-lg bg-blue-700 px-4 py-3 text-white font-semibold hover:bg-blue-800 disabled:cursor-not-allowed disabled:bg-blue-400 transition"
292- >
293- { loading ? "Publishing..." : "Publish Announcement" }
294- </ button >
295- </ form >
296- </ div >
297- </ div >
298- ) ;
3+ export default function Page ( ) {
4+ return < NoticeBoard /> ;
2995}
0 commit comments