Skip to content

Commit 0226939

Browse files
committed
Ui fix
1 parent 5e8d105 commit 0226939

3 files changed

Lines changed: 101 additions & 67 deletions

File tree

‎apps/codebility/app/home/announcements/AnnouncementContent.tsx‎

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,38 +12,41 @@ export const AnnouncementContent: React.FC<AnnouncementContentProps> = ({
1212
page,
1313
}) => {
1414
return (
15-
<div className="space-y-8">
16-
{/* Banner Image - Fixed for now, will be editable in future */}
15+
<div className="space-y-4 sm:space-y-6 md:space-y-8">
16+
{/* Banner Image */}
1717
<div className="relative aspect-video w-full overflow-hidden rounded-lg bg-gray-200 dark:bg-gray-800">
1818
<Image
1919
src={page.banner_image}
2020
alt={page.title}
2121
fill
2222
className="object-cover"
23-
sizes="(max-width: 768px) 100vw, 800px"
23+
sizes="(max-width: 640px) 100vw, (max-width: 768px) 90vw, 800px"
24+
priority
2425
/>
2526
</div>
2627

2728
{/* Title */}
2829
<div>
29-
<h3 className="text-3xl font-semibold text-gray-900 dark:text-gray-100">
30+
<h3 className="text-xl sm:text-2xl md:text-3xl font-semibold text-gray-900 dark:text-gray-100">
3031
{page.title}
3132
</h3>
32-
<p className="mt-2 text-base text-gray-500 dark:text-gray-400">
33-
Last updated: {new Date(page.last_updated).toLocaleDateString("en-US", {
33+
<p className="mt-1 sm:mt-2 text-sm sm:text-base text-gray-500 dark:text-gray-400">
34+
Last updated:{" "}
35+
{new Date(page.last_updated).toLocaleDateString("en-US", {
3436
year: "numeric",
3537
month: "long",
3638
day: "numeric",
3739
})}
3840
</p>
3941
</div>
4042

41-
<div className="prose prose-lg dark:prose-invert max-w-none">
42-
<div
43-
className="text-gray-700 dark:text-gray-300 text-lg leading-relaxed"
43+
{/* Content */}
44+
<div className="prose prose-sm sm:prose-base md:prose-lg dark:prose-invert max-w-none">
45+
<div
46+
className="text-gray-700 dark:text-gray-300 text-base sm:text-lg leading-relaxed"
4447
dangerouslySetInnerHTML={{ __html: page.content }}
4548
/>
46-
</div>
4749
</div>
50+
</div>
4851
);
4952
};

‎apps/codebility/app/home/announcements/AnnouncementModal.tsx‎

Lines changed: 86 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,14 @@
22

33
import React, { useState, useEffect } from "react";
44
import { createPortal } from "react-dom";
5-
import { X, Edit } from "lucide-react";
5+
import { X, Edit, ChevronLeft } from "lucide-react";
66
import { AnnouncementTab } from "./AnnouncementTab";
77
import { AnnouncementContent } from "./AnnouncementContent";
88
import { AnnouncementEditor } from "./AnnouncementEditor";
99
import { announcementTabs } from "./data";
1010
import { AnnouncementCategory, AnnouncementPage } from "./types";
1111
import { createClientClientComponent } from "@/utils/supabase/client";
1212

13-
1413
interface AnnouncementModalProps {
1514
isOpen: boolean;
1615
onClose: () => void;
@@ -26,6 +25,7 @@ export const AnnouncementModal: React.FC<AnnouncementModalProps> = ({
2625
const [isEditorOpen, setIsEditorOpen] = useState(false);
2726
const [loading, setLoading] = useState(true);
2827
const [error, setError] = useState<string | null>(null);
28+
const [showSidebar, setShowSidebar] = useState(true);
2929
const supabase = createClientClientComponent();
3030

3131
useEffect(() => {
@@ -34,22 +34,21 @@ export const AnnouncementModal: React.FC<AnnouncementModalProps> = ({
3434

3535
// Fetch announcements from database
3636
useEffect(() => {
37-
const fetchAnnouncements = async () => {
38-
if (!isOpen) return;
39-
if (!supabase) throw new Error('Supabase client not initialized');
40-
41-
try {
42-
setLoading(true);
43-
setError(null);
44-
45-
const { data, error: fetchError } = await supabase
37+
const fetchAnnouncements = async () => {
38+
if (!isOpen) return;
39+
if (!supabase) throw new Error("Supabase client not initialized");
40+
41+
try {
42+
setLoading(true);
43+
setError(null);
44+
45+
const { data, error: fetchError } = await supabase
4646
.from("announcements")
4747
.select("*")
4848
.order("category");
4949

5050
if (fetchError) throw fetchError;
5151

52-
// Map database fields to AnnouncementPage type
5352
const mappedData: AnnouncementPage[] = (data || []).map((item) => ({
5453
category: item.category as AnnouncementCategory,
5554
title: item.title,
@@ -59,30 +58,39 @@ export const AnnouncementModal: React.FC<AnnouncementModalProps> = ({
5958
}));
6059

6160
setPages(mappedData);
62-
} catch (err) {
63-
console.error("Error fetching announcements:", err);
64-
setError(`Failed to load announcements: ${err instanceof Error ? err.message : 'Unknown error'}`);
65-
} finally {
66-
setLoading(false);
67-
}
61+
} catch (err) {
62+
console.error("Error fetching announcements:", err);
63+
setError(
64+
`Failed to load announcements: ${err instanceof Error ? err.message : "Unknown error"}`
65+
);
66+
} finally {
67+
setLoading(false);
68+
}
6869
};
6970

7071
fetchAnnouncements();
72+
}, [isOpen, supabase]);
73+
74+
// Reset sidebar visibility when modal opens/closes
75+
useEffect(() => {
76+
if (isOpen) {
77+
// On mobile, start with sidebar visible
78+
const isMobile = window.innerWidth < 768;
79+
setShowSidebar(isMobile ? true : true);
80+
}
7181
}, [isOpen]);
7282

7383
// Get content for active tab
7484
const activeContent = pages.find((page) => page.category === activeTab);
75-
if (!supabase) throw new Error('Supabase client not initialized');
85+
if (!supabase) throw new Error("Supabase client not initialized");
7686

7787
const handleSave = async (updatedPage: AnnouncementPage) => {
7888
try {
79-
80-
// Optimistic update - update UI immediately
81-
setPages((prevPages) =>
82-
prevPages.map((page) =>
83-
page.category === updatedPage.category ? updatedPage : page
84-
)
85-
);
89+
setPages((prevPages) =>
90+
prevPages.map((page) =>
91+
page.category === updatedPage.category ? updatedPage : page
92+
)
93+
);
8694

8795
// Upsert to database (insert or update)
8896
const { data, error: upsertError } = await supabase
@@ -96,7 +104,7 @@ export const AnnouncementModal: React.FC<AnnouncementModalProps> = ({
96104
updated_at: new Date().toISOString(),
97105
},
98106
{
99-
onConflict: "category", // Use category as unique constraint
107+
onConflict: "category",
100108
}
101109
)
102110
.select()
@@ -106,7 +114,6 @@ export const AnnouncementModal: React.FC<AnnouncementModalProps> = ({
106114

107115
console.log("Successfully saved announcement:", data);
108116

109-
// Update state with the returned data to ensure sync
110117
setPages((prevPages) =>
111118
prevPages.map((page) =>
112119
page.category === updatedPage.category
@@ -123,9 +130,14 @@ export const AnnouncementModal: React.FC<AnnouncementModalProps> = ({
123130
} catch (err) {
124131
console.error("Error saving announcement:", err);
125132
setError("Failed to save announcement");
126-
127-
// Optionally: Revert optimistic update
128-
// You could refetch the data here to ensure consistency
133+
}
134+
};
135+
136+
const handleTabClick = (category: AnnouncementCategory) => {
137+
setActiveTab(category);
138+
// On mobile, hide sidebar after selecting a tab
139+
if (window.innerWidth < 768) {
140+
setShowSidebar(false);
129141
}
130142
};
131143

@@ -142,88 +154,107 @@ export const AnnouncementModal: React.FC<AnnouncementModalProps> = ({
142154

143155
{/* Modal */}
144156
<div
145-
className="fixed inset-0 z-[100] flex items-center justify-center p-4 pointer-events-none"
157+
className="fixed inset-0 z-[100] flex items-center justify-center p-2 sm:p-4 pointer-events-none"
146158
role="dialog"
147159
aria-modal="true"
148160
aria-labelledby="announcement-modal-title"
149161
>
150-
<div className="flex h-[850px] max-h-[95vh] w-full max-w-screen-2xl overflow-hidden rounded-xl bg-white shadow-2xl dark:bg-gray-900 pointer-events-auto">
162+
<div className="flex h-full sm:h-[850px] sm:max-h-[95vh] w-full max-w-screen-2xl overflow-hidden rounded-none sm:rounded-xl bg-white shadow-2xl dark:bg-gray-900 pointer-events-auto">
151163
{/* Left Sidebar - Tabs */}
152-
<div className="w-80 border-r border-gray-200 bg-gray-50 dark:border-gray-800 dark:bg-gray-950">
164+
<div
165+
className={`
166+
${showSidebar ? "translate-x-0" : "-translate-x-full"}
167+
md:translate-x-0
168+
fixed md:relative inset-y-0 left-0 z-10
169+
w-full sm:w-80 md:w-80
170+
border-r border-gray-200 bg-gray-50 dark:border-gray-800 dark:bg-gray-950
171+
transition-transform duration-300 ease-in-out
172+
`}
173+
>
153174
{/* Header */}
154-
<div className="border-b border-gray-200 p-8 dark:border-gray-800">
175+
<div className="border-b border-gray-200 p-4 sm:p-6 md:p-8 dark:border-gray-800">
155176
<h2
156177
id="announcement-modal-title"
157-
className="text-2xl font-bold text-gray-900 dark:text-gray-100"
178+
className="text-xl sm:text-2xl font-bold text-gray-900 dark:text-gray-100"
158179
>
159180
Codebility
160181
</h2>
161-
<p className="mt-2 text-base text-gray-600 dark:text-gray-400">
182+
<p className="mt-1 sm:mt-2 text-sm sm:text-base text-gray-600 dark:text-gray-400">
162183
News & Updates
163184
</p>
164185
</div>
165186

166187
{/* Tabs */}
167-
<div className="space-y-2 p-6" role="tablist">
188+
<div className="space-y-2 p-4 sm:p-6" role="tablist">
168189
{announcementTabs.map((tab) => (
169190
<AnnouncementTab
170191
key={tab.id}
171192
id={tab.id}
172193
label={tab.label}
173194
isActive={activeTab === tab.id}
174-
onClick={setActiveTab}
195+
onClick={handleTabClick}
175196
/>
176197
))}
177198
</div>
178199
</div>
179200

180201
{/* Right Content Area */}
181-
<div className="flex flex-1 flex-col">
202+
<div className="flex flex-1 flex-col min-w-0">
182203
{/* Header with Close and Edit Buttons */}
183-
<div className="flex items-center justify-between border-b border-gray-200 p-8 dark:border-gray-800">
184-
<h3 className="text-2xl font-semibold text-gray-900 dark:text-gray-100">
204+
<div className="flex items-center justify-between border-b border-gray-200 p-4 sm:p-6 md:p-8 dark:border-gray-800 gap-2">
205+
{/* Back button for mobile */}
206+
<button
207+
onClick={() => setShowSidebar(true)}
208+
className="md:hidden rounded-lg p-2 text-gray-400 transition-colors hover:bg-gray-100 hover:text-gray-600 dark:hover:bg-gray-800 dark:hover:text-gray-300 flex-shrink-0"
209+
aria-label="Back to categories"
210+
>
211+
<ChevronLeft className="h-5 w-5" />
212+
</button>
213+
214+
<h3 className="text-lg sm:text-xl md:text-2xl font-semibold text-gray-900 dark:text-gray-100 truncate flex-1 min-w-0">
185215
{announcementTabs.find((tab) => tab.id === activeTab)?.label}
186216
</h3>
187-
<div className="flex items-center gap-2">
217+
218+
<div className="flex items-center gap-1 sm:gap-2 flex-shrink-0">
188219
<button
189220
onClick={() => setIsEditorOpen(true)}
190221
disabled={loading}
191-
className="flex items-center gap-2 rounded-lg px-4 py-2 text-sm font-medium text-blue-600 transition-colors hover:bg-blue-50 dark:text-blue-400 dark:hover:bg-blue-950 disabled:opacity-50 disabled:cursor-not-allowed"
222+
className="flex items-center gap-1 sm:gap-2 rounded-lg px-2 sm:px-3 md:px-4 py-2 text-xs sm:text-sm font-medium text-blue-600 transition-colors hover:bg-blue-50 dark:text-blue-400 dark:hover:bg-blue-950 disabled:opacity-50 disabled:cursor-not-allowed"
192223
aria-label="Edit announcement"
193224
>
194-
<Edit className="h-4 w-4" />
195-
Edit
225+
<Edit className="h-3 w-3 sm:h-4 sm:w-4" />
226+
<span className="hidden sm:inline">Edit</span>
196227
</button>
197228
<button
198229
onClick={onClose}
199230
className="rounded-lg p-2 text-gray-400 transition-colors hover:bg-gray-100 hover:text-gray-600 dark:hover:bg-gray-800 dark:hover:text-gray-300"
200231
aria-label="Close announcement modal"
201232
>
202-
<X className="h-6 w-6" />
233+
<X className="h-5 w-5 sm:h-6 sm:w-6" />
203234
</button>
204235
</div>
205236
</div>
206237

207238
{/* Content Area - Scrollable */}
208-
<div className="flex-1 overflow-y-auto p-8">
239+
<div className="flex-1 overflow-y-auto p-4 sm:p-6 md:p-8">
209240
{loading ? (
210241
<div className="flex h-full items-center justify-center">
211242
<div className="text-center">
212-
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto mb-4"></div>
213-
<p className="text-gray-500 dark:text-gray-400">
243+
<div className="animate-spin rounded-full h-10 w-10 sm:h-12 sm:w-12 border-b-2 border-blue-600 mx-auto mb-4"></div>
244+
<p className="text-sm sm:text-base text-gray-500 dark:text-gray-400">
214245
Loading announcements...
215246
</p>
216247
</div>
217248
</div>
218249
) : error ? (
219250
<div className="flex h-full items-center justify-center">
220-
<div className="text-center">
221-
<p className="text-red-500 dark:text-red-400 mb-2">
251+
<div className="text-center px-4">
252+
<p className="text-sm sm:text-base text-red-500 dark:text-red-400 mb-2">
222253
{error}
223254
</p>
224255
<button
225256
onClick={() => window.location.reload()}
226-
className="text-sm text-blue-600 hover:underline dark:text-blue-400"
257+
className="text-xs sm:text-sm text-blue-600 hover:underline dark:text-blue-400"
227258
>
228259
Retry
229260
</button>
@@ -233,8 +264,8 @@ export const AnnouncementModal: React.FC<AnnouncementModalProps> = ({
233264
<AnnouncementContent page={activeContent} />
234265
) : (
235266
<div className="flex h-full items-center justify-center">
236-
<div className="text-center">
237-
<p className="text-gray-500 dark:text-gray-400">
267+
<div className="text-center px-4">
268+
<p className="text-sm sm:text-base text-gray-500 dark:text-gray-400">
238269
No content available for this section yet.
239270
</p>
240271
</div>

‎apps/codebility/app/home/announcements/AnnouncementTab.tsx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export const AnnouncementTab: React.FC<AnnouncementTabProps> = ({
2020
<button
2121
onClick={() => onClick(id)}
2222
className={`
23-
relative px-6 py-4 text-base font-medium transition-all duration-200
23+
relative px-4 sm:px-6 py-3 sm:py-4 text-sm sm:text-base font-medium transition-all duration-200
2424
${
2525
isActive
2626
? "bg-blue-500/10 text-blue-500 dark:bg-blue-500/20"
@@ -32,7 +32,7 @@ export const AnnouncementTab: React.FC<AnnouncementTabProps> = ({
3232
aria-selected={isActive}
3333
aria-controls={`panel-${id}`}
3434
>
35-
<span>{label}</span>
35+
<span className="truncate">{label}</span>
3636
</button>
3737
);
3838
};

0 commit comments

Comments
 (0)