Spaces:
Sleeping
Sleeping
Update web/src/components/sidebar/LeftSidebar.tsx
Browse files
web/src/components/sidebar/LeftSidebar.tsx
CHANGED
|
@@ -8,6 +8,16 @@ import { Badge } from "../ui/badge";
|
|
| 8 |
import { SavedChatSection } from "./SavedChatSection";
|
| 9 |
|
| 10 |
import { Pencil, Check, X, MailPlus, Users } from "lucide-react";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
|
| 12 |
import type {
|
| 13 |
SavedChat,
|
|
@@ -61,8 +71,6 @@ type Props = {
|
|
| 61 |
selectedCourse: string;
|
| 62 |
availableCourses: CourseDirectoryItem[];
|
| 63 |
|
| 64 |
-
onInviteGroupMembers?: () => void;
|
| 65 |
-
|
| 66 |
onRenameGroupName?: (workspaceId: string, newName: string) => Promise<void> | void;
|
| 67 |
onRenameGroupNo?: (workspaceId: string, newNo: number) => Promise<void> | void;
|
| 68 |
};
|
|
@@ -117,7 +125,6 @@ export function LeftSidebar(props: Props) {
|
|
| 117 |
workspaces,
|
| 118 |
selectedCourse,
|
| 119 |
availableCourses,
|
| 120 |
-
onInviteGroupMembers,
|
| 121 |
onRenameGroupName,
|
| 122 |
onRenameGroupNo,
|
| 123 |
} = props;
|
|
@@ -188,16 +195,34 @@ export function LeftSidebar(props: Props) {
|
|
| 188 |
[currentWorkspaceId]
|
| 189 |
);
|
| 190 |
|
| 191 |
-
//
|
| 192 |
const [groupName, setGroupName] = useState<string>(wsGroupName);
|
| 193 |
const [editingGroupName, setEditingGroupName] = useState(false);
|
| 194 |
const [draftGroupName, setDraftGroupName] = useState<string>(wsGroupName);
|
| 195 |
|
| 196 |
-
//
|
| 197 |
const [groupNo, setGroupNo] = useState<number>(toIntOrFallback(wsGroupNo, 1));
|
| 198 |
const [editingGroupNo, setEditingGroupNo] = useState(false);
|
| 199 |
const [draftGroupNo, setDraftGroupNo] = useState<string>(String(toIntOrFallback(wsGroupNo, 1)));
|
| 200 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 201 |
useEffect(() => {
|
| 202 |
const storedName =
|
| 203 |
typeof window !== "undefined" ? window.localStorage.getItem(groupNameStorageKey) : null;
|
|
@@ -208,9 +233,10 @@ export function LeftSidebar(props: Props) {
|
|
| 208 |
|
| 209 |
const storedNo =
|
| 210 |
typeof window !== "undefined" ? window.localStorage.getItem(groupNoStorageKey) : null;
|
| 211 |
-
const no =
|
| 212 |
-
|
| 213 |
-
|
|
|
|
| 214 |
setGroupNo(no);
|
| 215 |
setDraftGroupNo(String(no));
|
| 216 |
setEditingGroupNo(false);
|
|
@@ -292,7 +318,6 @@ export function LeftSidebar(props: Props) {
|
|
| 292 |
<div className="text-[30px] leading-tight font-semibold">{courseName}</div>
|
| 293 |
|
| 294 |
{!isTeamSpace ? (
|
| 295 |
-
// My Space unchanged
|
| 296 |
<div className="space-y-3">
|
| 297 |
<div className="text-[30px] leading-tight font-semibold">
|
| 298 |
Group {String(groupNo)}
|
|
@@ -336,10 +361,9 @@ export function LeftSidebar(props: Props) {
|
|
| 336 |
</div>
|
| 337 |
</div>
|
| 338 |
) : (
|
| 339 |
-
// Team/Group Space (your requested structure)
|
| 340 |
<div className="rounded-2xl border bg-white overflow-hidden">
|
| 341 |
<div className="px-4 pt-4 pb-3 space-y-3">
|
| 342 |
-
{/* Line 1: group name
|
| 343 |
{!editingGroupName ? (
|
| 344 |
<div className="flex items-center gap-2">
|
| 345 |
<div className="text-[18px] font-semibold text-foreground truncate">
|
|
@@ -375,7 +399,7 @@ export function LeftSidebar(props: Props) {
|
|
| 375 |
</div>
|
| 376 |
)}
|
| 377 |
|
| 378 |
-
{/* Line 2:
|
| 379 |
<div className="flex items-center justify-between gap-3">
|
| 380 |
<div className="flex items-center gap-2">
|
| 381 |
<Users className="w-4 h-4 text-muted-foreground" />
|
|
@@ -424,7 +448,7 @@ export function LeftSidebar(props: Props) {
|
|
| 424 |
type="button"
|
| 425 |
variant="secondary"
|
| 426 |
className="h-9 px-3 text-[13px]"
|
| 427 |
-
onClick={
|
| 428 |
>
|
| 429 |
<MailPlus className="w-4 h-4 mr-2" />
|
| 430 |
Invite
|
|
@@ -546,6 +570,31 @@ export function LeftSidebar(props: Props) {
|
|
| 546 |
</div>
|
| 547 |
</div>
|
| 548 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 549 |
</div>
|
| 550 |
);
|
| 551 |
}
|
|
|
|
| 8 |
import { SavedChatSection } from "./SavedChatSection";
|
| 9 |
|
| 10 |
import { Pencil, Check, X, MailPlus, Users } from "lucide-react";
|
| 11 |
+
import { toast } from "sonner";
|
| 12 |
+
|
| 13 |
+
import {
|
| 14 |
+
Dialog,
|
| 15 |
+
DialogContent,
|
| 16 |
+
DialogDescription,
|
| 17 |
+
DialogFooter,
|
| 18 |
+
DialogHeader,
|
| 19 |
+
DialogTitle,
|
| 20 |
+
} from "../ui/dialog";
|
| 21 |
|
| 22 |
import type {
|
| 23 |
SavedChat,
|
|
|
|
| 71 |
selectedCourse: string;
|
| 72 |
availableCourses: CourseDirectoryItem[];
|
| 73 |
|
|
|
|
|
|
|
| 74 |
onRenameGroupName?: (workspaceId: string, newName: string) => Promise<void> | void;
|
| 75 |
onRenameGroupNo?: (workspaceId: string, newNo: number) => Promise<void> | void;
|
| 76 |
};
|
|
|
|
| 125 |
workspaces,
|
| 126 |
selectedCourse,
|
| 127 |
availableCourses,
|
|
|
|
| 128 |
onRenameGroupName,
|
| 129 |
onRenameGroupNo,
|
| 130 |
} = props;
|
|
|
|
| 195 |
[currentWorkspaceId]
|
| 196 |
);
|
| 197 |
|
| 198 |
+
// group name
|
| 199 |
const [groupName, setGroupName] = useState<string>(wsGroupName);
|
| 200 |
const [editingGroupName, setEditingGroupName] = useState(false);
|
| 201 |
const [draftGroupName, setDraftGroupName] = useState<string>(wsGroupName);
|
| 202 |
|
| 203 |
+
// group no
|
| 204 |
const [groupNo, setGroupNo] = useState<number>(toIntOrFallback(wsGroupNo, 1));
|
| 205 |
const [editingGroupNo, setEditingGroupNo] = useState(false);
|
| 206 |
const [draftGroupNo, setDraftGroupNo] = useState<string>(String(toIntOrFallback(wsGroupNo, 1)));
|
| 207 |
|
| 208 |
+
// Invite dialog state (restored)
|
| 209 |
+
const [inviteOpen, setInviteOpen] = useState(false);
|
| 210 |
+
const [inviteEmail, setInviteEmail] = useState("");
|
| 211 |
+
|
| 212 |
+
const handleInviteClick = () => {
|
| 213 |
+
setInviteOpen(true);
|
| 214 |
+
};
|
| 215 |
+
|
| 216 |
+
const handleSendInvite = () => {
|
| 217 |
+
if (!inviteEmail.trim()) {
|
| 218 |
+
toast.error("Please enter an email to invite");
|
| 219 |
+
return;
|
| 220 |
+
}
|
| 221 |
+
toast.success(`Invitation sent to ${inviteEmail}`);
|
| 222 |
+
setInviteEmail("");
|
| 223 |
+
setInviteOpen(false);
|
| 224 |
+
};
|
| 225 |
+
|
| 226 |
useEffect(() => {
|
| 227 |
const storedName =
|
| 228 |
typeof window !== "undefined" ? window.localStorage.getItem(groupNameStorageKey) : null;
|
|
|
|
| 233 |
|
| 234 |
const storedNo =
|
| 235 |
typeof window !== "undefined" ? window.localStorage.getItem(groupNoStorageKey) : null;
|
| 236 |
+
const no =
|
| 237 |
+
storedNo && storedNo.trim()
|
| 238 |
+
? toIntOrFallback(storedNo, toIntOrFallback(wsGroupNo, 1))
|
| 239 |
+
: toIntOrFallback(wsGroupNo, 1);
|
| 240 |
setGroupNo(no);
|
| 241 |
setDraftGroupNo(String(no));
|
| 242 |
setEditingGroupNo(false);
|
|
|
|
| 318 |
<div className="text-[30px] leading-tight font-semibold">{courseName}</div>
|
| 319 |
|
| 320 |
{!isTeamSpace ? (
|
|
|
|
| 321 |
<div className="space-y-3">
|
| 322 |
<div className="text-[30px] leading-tight font-semibold">
|
| 323 |
Group {String(groupNo)}
|
|
|
|
| 361 |
</div>
|
| 362 |
</div>
|
| 363 |
) : (
|
|
|
|
| 364 |
<div className="rounded-2xl border bg-white overflow-hidden">
|
| 365 |
<div className="px-4 pt-4 pb-3 space-y-3">
|
| 366 |
+
{/* Line 1: group name (black) + pencil */}
|
| 367 |
{!editingGroupName ? (
|
| 368 |
<div className="flex items-center gap-2">
|
| 369 |
<div className="text-[18px] font-semibold text-foreground truncate">
|
|
|
|
| 399 |
</div>
|
| 400 |
)}
|
| 401 |
|
| 402 |
+
{/* Line 2: Group {no}(✏️) ({count}) + Invite */}
|
| 403 |
<div className="flex items-center justify-between gap-3">
|
| 404 |
<div className="flex items-center gap-2">
|
| 405 |
<Users className="w-4 h-4 text-muted-foreground" />
|
|
|
|
| 448 |
type="button"
|
| 449 |
variant="secondary"
|
| 450 |
className="h-9 px-3 text-[13px]"
|
| 451 |
+
onClick={handleInviteClick}
|
| 452 |
>
|
| 453 |
<MailPlus className="w-4 h-4 mr-2" />
|
| 454 |
Invite
|
|
|
|
| 570 |
</div>
|
| 571 |
</div>
|
| 572 |
</div>
|
| 573 |
+
|
| 574 |
+
{/* Invite Dialog (restored) */}
|
| 575 |
+
<Dialog open={inviteOpen} onOpenChange={setInviteOpen}>
|
| 576 |
+
<DialogContent className="w-[600px] max-w-[600px] sm:max-w-[600px]" style={{ maxWidth: 600 }}>
|
| 577 |
+
<DialogHeader>
|
| 578 |
+
<DialogTitle>Invite member</DialogTitle>
|
| 579 |
+
<DialogDescription>Send a quick email invite with the team details.</DialogDescription>
|
| 580 |
+
</DialogHeader>
|
| 581 |
+
<div className="space-y-3">
|
| 582 |
+
<Input
|
| 583 |
+
type="email"
|
| 584 |
+
placeholder="name@example.com"
|
| 585 |
+
value={inviteEmail}
|
| 586 |
+
onChange={(e) => setInviteEmail(e.target.value)}
|
| 587 |
+
/>
|
| 588 |
+
<p className="text-xs text-muted-foreground">
|
| 589 |
+
An invitation email with a join link will be sent to this address.
|
| 590 |
+
</p>
|
| 591 |
+
</div>
|
| 592 |
+
<DialogFooter>
|
| 593 |
+
<Button variant="outline" onClick={() => setInviteOpen(false)}>Cancel</Button>
|
| 594 |
+
<Button onClick={handleSendInvite}>Send invite</Button>
|
| 595 |
+
</DialogFooter>
|
| 596 |
+
</DialogContent>
|
| 597 |
+
</Dialog>
|
| 598 |
</div>
|
| 599 |
);
|
| 600 |
}
|