Skip to content

Commit 056d29d

Browse files
committed
Merge branch 'main' into DEVT-51-collaborative-chat-ui
2 parents 1ae24eb + 2171ae2 commit 056d29d

File tree

4 files changed

+95
-67
lines changed

4 files changed

+95
-67
lines changed

app/classrooms/[classroomId]/chat/MessageBox.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ export default function MessageBox({
104104
value={value}
105105
onChange={(e) => setValue(e.target.value)}
106106
placeholder="Type your message..."
107+
onEnter={handleSend}
107108
/>
108109
<Button
109110
onClick={handleSend}

app/classrooms/[classroomId]/upload/uploadComponent.tsx

Lines changed: 51 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,13 @@ import {
1010
uploadFile,
1111
} from "@shared/lib/ragflow/dataset-client";
1212
import { Input } from "@shared/components/ui/input";
13+
import { Button } from "@shared/components/ui/button";
14+
import {
15+
Card,
16+
CardContent,
17+
CardHeader,
18+
CardTitle,
19+
} from "@shared/components/ui/card";
1320
import { Skeleton } from "@shared/components/ui/skeleton";
1421
import { ScrollArea } from "@shared/components/ui/scroll-area";
1522
import { toast } from "sonner";
@@ -105,10 +112,6 @@ export default function UploadComponent({
105112

106113
setLoading(false);
107114

108-
// if (!response || typeof response !== "object") {
109-
// setErrorMessage("Invalid response from server.");
110-
// return;
111-
// }
112115
if (
113116
response.isAdmin &&
114117
response.parseCallSuccess &&
@@ -128,43 +131,47 @@ export default function UploadComponent({
128131
}
129132

130133
return (
131-
<div className="flex min-h-screen flex-col items-center justify-center bg-gray-50 p-6 text-black">
132-
<div className="w-full max-w-md rounded-lg bg-white p-6 shadow-md">
133-
<h1 className="mb-4 text-xl font-bold">File Upload</h1>
134-
{datasetClient == undefined || uploadedFiles == null ? (
135-
<div className="flex items-center space-x-4">
136-
<Skeleton className="h-12 w-12 rounded-full" />
137-
<div className="space-y-2">
138-
<Skeleton className="h-4 w-[250px]" />
139-
<Skeleton className="h-4 w-[200px]" />
140-
</div>
141-
</div>
142-
) : (
143-
<>
144-
<form onSubmit={handleSubmit}>
145-
<div className="mb-4">
146-
<Input
147-
type="file"
148-
onChange={handleFileChange}
149-
ref={inputFile}
150-
className="w-full text-sm text-gray-500 file:mr-4 file:rounded-md file:border-0 file:bg-blue-50 file:px-4 file:text-sm file:font-semibold file:text-blue-700 hover:file:bg-blue-100"
151-
/>
134+
<div className="flex min-h-screen flex-col items-center justify-center p-6">
135+
<Card className="w-full max-w-md">
136+
<CardHeader>
137+
<CardTitle>File Upload</CardTitle>
138+
</CardHeader>
139+
<CardContent>
140+
{datasetClient == undefined || uploadedFiles == null ? (
141+
<div className="flex items-center space-x-4">
142+
<Skeleton className="h-12 w-12 rounded-full" />
143+
<div className="space-y-2">
144+
<Skeleton className="h-4 w-[250px]" />
145+
<Skeleton className="h-4 w-[200px]" />
152146
</div>
153-
154-
<button
155-
type="submit"
156-
disabled={!file || loading}
157-
className="w-full rounded-md bg-blue-500 px-4 py-2 font-bold text-white hover:bg-blue-600 disabled:cursor-not-allowed disabled:bg-gray-300"
158-
>
159-
{loading ? "Uploading..." : "Upload"}
160-
</button>
161-
</form>
162-
163-
{/* Move to component with files passed in as props so that newly fetch data doesn't trigger a rerender (and thus another fetch) infinitely so*/}
164-
<FileList uploadedFiles={uploadedFiles} />
165-
</>
166-
)}
167-
</div>
147+
</div>
148+
) : (
149+
<>
150+
<form onSubmit={handleSubmit} className="space-y-4">
151+
<div>
152+
<Input
153+
type="file"
154+
onChange={handleFileChange}
155+
ref={inputFile}
156+
className="w-full"
157+
/>
158+
</div>
159+
160+
<Button
161+
type="submit"
162+
disabled={!file || loading}
163+
className="w-full"
164+
>
165+
{loading ? "Uploading..." : "Upload"}
166+
</Button>
167+
</form>
168+
169+
{/* Move to component with files passed in as props so that newly fetch data doesn't trigger a rerender (and thus another fetch) infinitely so*/}
170+
<FileList uploadedFiles={uploadedFiles} />
171+
</>
172+
)}
173+
</CardContent>
174+
</Card>
168175
</div>
169176
);
170177
}
@@ -173,21 +180,21 @@ function FileList({ uploadedFiles }: { uploadedFiles: UploadedFile[] }) {
173180
const pathname = usePathname();
174181
return (
175182
uploadedFiles.length > 0 && (
176-
<ScrollArea className="mt-5 max-h-[50vh] rounded-md border px-3">
183+
<ScrollArea className="mt-5 max-h-[50vh]">
177184
<div className="mt-6">
178185
<h2 className="text-lg font-semibold">Uploaded Files</h2>
179186
<ul className="my-2 space-y-2">
180187
{uploadedFiles.map((file) => (
181-
<li key={file.id} className="rounded-md bg-gray-100 p-3">
188+
<li key={file.id} className="rounded-md border p-3">
182189
<Link
183190
href={`${pathname}/preview?documentId=${file.id}&datasetId=${file.datasetId}`}
184191
rel="noopener noreferrer"
185192
target="_blank"
186-
className="font-medium"
193+
className="font-medium hover:underline"
187194
>
188195
{file.name}
189196
</Link>
190-
<p className="text-sm text-gray-500">
197+
<p className="text-sm text-muted-foreground">
191198
{(file.size / 1024).toFixed(2)} KB - {file.type} -{" "}
192199
<strong>{file.status}</strong>
193200
</p>

app/classrooms/join/[code]/route.ts

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import { NextResponse, NextRequest } from "next/server";
1+
import { NextRequest } from "next/server";
22
import { createServiceClient } from "@shared/utils/supabase/service-server";
33
import { getCurrentUserIdServer } from "@shared/lib/supabase/shared";
4+
import { redirect } from "next/navigation";
45

56
export async function GET(
67
request: NextRequest,
@@ -21,7 +22,7 @@ export async function GET(
2122

2223
if (classroomError || !classroom) {
2324
console.error("Classroom not found:", classroomError);
24-
return NextResponse.redirect(new URL("/classrooms", request.url));
25+
return redirect("/classrooms");
2526
}
2627

2728
//ensures that the user is authenticated
@@ -30,7 +31,7 @@ export async function GET(
3031
if (!userId) {
3132
console.error("User is not authenticated");
3233
//login page
33-
return NextResponse.redirect(new URL("/login", request.url));
34+
return redirect("/classrooms/login");
3435
}
3536

3637
//if the person is already in there, should be redirect to just classroom
@@ -43,11 +44,11 @@ export async function GET(
4344

4445
if (memberError) {
4546
console.error("Error checking membership:", memberError);
46-
return NextResponse.redirect(new URL("/classrooms", request.url));
47+
return redirect("/classrooms");
4748
}
4849

4950
if (existingMember) {
50-
return NextResponse.redirect(new URL("/classrooms", request.url));
51+
return redirect("/classrooms");
5152
}
5253

5354
const { error: insertError } = await supabase
@@ -59,11 +60,12 @@ export async function GET(
5960

6061
if (insertError) {
6162
console.error("Error adding member to classroom:", insertError);
62-
return NextResponse.redirect(new URL("/classrooms", request.url));
63+
return redirect("/classrooms");
6364
}
6465
//redirect to classroom
6566

66-
const success_url = new URL("/classrooms", request.url);
67-
success_url.searchParams.append("join_success", classroom.id.toString());
68-
return NextResponse.redirect(success_url);
67+
const successParams = new URLSearchParams({
68+
join_success: classroom.id.toString(),
69+
});
70+
return redirect(`/classrooms?${successParams.toString()}`);
6971
}

shared/components/ui/chat/chat-input.tsx

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,41 @@ import * as React from "react";
22
import { Textarea } from "@shared/components/ui/textarea";
33
import { cn } from "@shared/lib/utils";
44

5-
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
65
interface ChatInputProps
7-
extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {}
6+
extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {
7+
//leave as optional
8+
onEnter?: () => void;
9+
}
810

911
const ChatInput = React.forwardRef<HTMLTextAreaElement, ChatInputProps>(
10-
({ className, ...props }, ref) => (
11-
<Textarea
12-
autoComplete="off"
13-
ref={ref}
14-
name="message"
15-
className={cn(
16-
"flex h-16 max-h-12 w-full resize-none items-center rounded-md bg-background px-4 py-3 text-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
17-
className
18-
)}
19-
{...props}
20-
/>
21-
)
12+
({ className, onKeyDown, onEnter, ...props }, ref) => {
13+
const handleKeyDown = (event: React.KeyboardEvent<HTMLTextAreaElement>) => {
14+
//if it is only the enter key
15+
if (event.key === "Enter" && !event.shiftKey) {
16+
//prevent new line
17+
event.preventDefault();
18+
if (onEnter) {
19+
onEnter();
20+
}
21+
}
22+
if (onKeyDown) {
23+
onKeyDown(event);
24+
}
25+
};
26+
return (
27+
<Textarea
28+
autoComplete="off"
29+
ref={ref}
30+
name="message"
31+
onKeyDown={handleKeyDown}
32+
className={cn(
33+
"flex h-16 max-h-12 w-full resize-none items-center rounded-md bg-background px-4 py-3 text-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
34+
className
35+
)}
36+
{...props}
37+
/>
38+
);
39+
}
2240
);
2341
ChatInput.displayName = "ChatInput";
2442

0 commit comments

Comments
 (0)