Commit 2be90194 authored by 健杭 徐's avatar 健杭 徐
Browse files

finish all

parent c48b7d0f
...@@ -98,15 +98,56 @@ func main() { ...@@ -98,15 +98,56 @@ func main() {
c.Next() c.Next()
}) })
router.POST("/register", Register)
router.POST("/login", Login)
router.POST("/room/add", AddNewRoom) router.POST("/room/add", AddNewRoom)
router.GET("/room/list", GetRoomList) router.GET("/room/list", GetRoomList)
router.POST("/room/delete", DeleteRoom) router.POST("/room/delete", DeleteRoom)
router.POST("/room/message/add", AddMessage) router.POST("/room/message/add", AddMessage)
router.GET("/room/message/list", GetMessageList) router.GET("/room/message/list", GetMessageList)
router.PUT("/room/message/update", RoomMessageUpdate) router.PUT("/room/message/update", RoomMessageUpdate)
router.PUT("/room/rename", RoomRename)
router.Run(":8080") router.Run(":8080")
} }
func Register(c *gin.Context) {
userName := c.Query("userName")
password := c.Query("password")
var roomId int
err := db.QueryRow(
"INSERT INTO users (username, password) VALUES ($1, $2) RETURNING user_id",
userName, password,
).Scan(&roomId)
if err != nil {
c.JSON(500, Response{Code: 500, Msg: "注册失败", Data: nil})
return
}
log.Printf("New user registered: %s with ID %d", userName, roomId)
c.JSON(200, Response{Code: 0, Msg: "注册成功", Data: nil})
}
func Login(c *gin.Context) {
userName := c.Query("userName")
password := c.Query("password")
var userId int
err := db.QueryRow(
"SELECT user_id FROM users WHERE username = $1 AND password = $2",
userName, password,
).Scan(&userId)
if err != nil {
c.JSON(401, Response{Code: 401, Msg: "账号或密码错误", Data: nil})
return
}
log.Printf("User logged in: %s with ID %d", userName, userId)
c.JSON(200, Response{Code: 0, Msg: "登录成功", Data: nil})
}
func AddNewRoom(c *gin.Context) { func AddNewRoom(c *gin.Context) {
var room RoomPreviewInfo var room RoomPreviewInfo
if err := c.ShouldBindJSON(&room); err != nil { if err := c.ShouldBindJSON(&room); err != nil {
...@@ -186,27 +227,27 @@ func GetRoomList(c *gin.Context) { ...@@ -186,27 +227,27 @@ func GetRoomList(c *gin.Context) {
} }
func DeleteRoom(c *gin.Context) { func DeleteRoom(c *gin.Context) {
roomId, err := strconv.Atoi((c.Query("roomTd"))) roomId := c.Query("roomId")
if err != nil || roomId <= 0 { if roomId == "" {
c.JSON(http.StatusOK, Response{Code: 400, Msg: "Invalid ID"}) c.JSON(400, Response{Code: 400, Msg: "Room ID is required", Data: nil})
return return
} }
result, err := db.Exec("DELETE FROM rooms WHERE roomId = $1", roomId) _, err := db.Exec("DELETE FROM messages WHERE room_id = $1", roomId)
if err != nil { if err != nil {
c.JSON(http.StatusOK, Response{Code: 500, Msg: err.Error()}) c.JSON(500, Response{Code: 500, Msg: "Failed to delete messages in room: " + err.Error(), Data: nil})
return return
} }
rowsAffected, _ := result.RowsAffected() _, err_ := db.Exec("DELETE FROM rooms WHERE room_id = $1", roomId)
if rowsAffected == 0 { if err_ != nil {
c.JSON(http.StatusOK, Response{Code: 400, Msg: "Room not found"}) c.JSON(500, Response{Code: 500, Msg: "Failed to delete room: " + err_.Error(), Data: nil})
return return
} }
c.JSON(http.StatusOK, Response{ c.JSON(200, Response{
Code: 0, Code: 0,
Msg: "Room deleted", Msg: "Room deleted successfully",
Data: nil, Data: nil,
}) })
} }
...@@ -287,6 +328,34 @@ func GetMessageList(c *gin.Context) { ...@@ -287,6 +328,34 @@ func GetMessageList(c *gin.Context) {
}) })
} }
func RoomRename(c *gin.Context) {
roomId, err := strconv.Atoi(c.Query("roomId"))
if err != nil || roomId <= 0 {
c.JSON(http.StatusOK, Response{Code: 400, Msg: "Invalid room ID"})
return
}
var newName struct {
RoomName string `json:"roomName"`
}
if err := c.ShouldBindJSON(&newName); err != nil {
c.JSON(http.StatusBadRequest, Response{Code: 400, Msg: "Invalid input: " + err.Error(), Data: nil})
return
}
_, err = db.Exec("UPDATE rooms SET room_name = $1 WHERE room_id = $2", newName.RoomName, roomId)
if err != nil {
c.JSON(http.StatusInternalServerError, Response{Code: 500, Msg: "Failed to rename room: " + err.Error(), Data: nil})
return
}
c.JSON(http.StatusOK, Response{
Code: 0,
Msg: "Room renamed successfully",
Data: nil,
})
}
func RoomMessageUpdate(c *gin.Context) { func RoomMessageUpdate(c *gin.Context) {
// 处理更新房间消息的逻辑 // 处理更新房间消息的逻辑
} }
...@@ -310,6 +379,12 @@ func createTable() { ...@@ -310,6 +379,12 @@ func createTable() {
"time" TIMESTAMP DEFAULT CURRENT_TIMESTAMP, "time" TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (room_id) REFERENCES rooms(room_id) FOREIGN KEY (room_id) REFERENCES rooms(room_id)
); );
CREATE TABLE IF NOT EXISTS users (
user_id SERIAL PRIMARY KEY,
username VARCHAR(100) NOT NULL UNIQUE,
password VARCHAR(100) NOT NULL
);
` `
_, err := db.Exec(query) _, err := db.Exec(query)
if err != nil { if err != nil {
......
...@@ -100,6 +100,7 @@ ...@@ -100,6 +100,7 @@
margin-bottom: 10px; margin-bottom: 10px;
border-radius: 8px; border-radius: 8px;
cursor: pointer; cursor: pointer;
border: 1px solid #ddd;
&:hover { &:hover {
background-color: #e0e0e0; background-color: #e0e0e0;
...@@ -150,6 +151,53 @@ ...@@ -150,6 +151,53 @@
bottom: 10px; bottom: 10px;
} }
} }
.chat-room-menu {
position: absolute;
right: 10px;
top: 10px;
width: 20px;
background-color: white;
border: 1px solid #ddd;
border-radius: 5px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
z-index: 100;
text-align: center;
cursor: pointer;
}
/* Dropdown menu for actions */
.menu-dropdown {
position: absolute;
right: 6px;
top: 36px;
min-width: 160px;
background: #ffffff;
border: 1px solid #e5e7eb;
border-radius: 8px;
box-shadow: 0 8px 24px rgba(0,0,0,0.12);
padding: 6px 0;
display: none;
}
.menu-open {
display: block;
}
.menu-item {
width: 100%;
background: transparent;
border: none;
text-align: left;
padding: 10px 12px;
font-size: 14px;
color: #111827;
cursor: pointer;
}
.menu-item:hover {
background: #f3f4f6;
}
} }
} }
...@@ -161,7 +209,7 @@ ...@@ -161,7 +209,7 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
box-sizing: border-box; box-sizing: border-box;
background-image: url('../../public/backGround.jpg'); background-image: url('../public/backGround.jpg');
background-size: cover; background-size: cover;
z-index: 2; z-index: 2;
......
'use client'; 'use client';
import "./ChatRoom.css"; import styles from "./ChatRoom.module.css";
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { useRouter } from 'next/navigation';
const backEnd:string = "http://localhost:8080"; const backEnd:string = "http://localhost:8080";
...@@ -33,27 +34,66 @@ interface MessageProps { ...@@ -33,27 +34,66 @@ interface MessageProps {
}>; }>;
} }
function RoomEntry ({rooms, onRoomClick} : {rooms: RoomEntryProps[], onRoomClick: (roomId: number, roomName: string) => void}) { function RoomEntry ({rooms, onRoomClick, onRename, onDelete} : {rooms: RoomEntryProps[], onRoomClick: (roomId: number, roomName: string) => void, onRename: (roomId: number, currentName: string) => void, onDelete: (roomId: number) => void}) {
const [openMenuFor, setOpenMenuFor] = useState<number | null>(null);
useEffect(() => {
const handleClickOutside = (e: MouseEvent) => {
const target = e.target as HTMLElement;
if (!target.closest('[data-menu="room-actions"]')) {
setOpenMenuFor(null);
}
};
document.addEventListener('click', handleClickOutside);
return () => document.removeEventListener('click', handleClickOutside);
}, []);
return ( return (
<div className="chat-room-nav"> <div className={styles["chat-room-nav"]}>
<div className="sidebar-action"> <div className={styles["sidebar-action"]}>
<button type="button" className="button" onClick={openOpenDiv}> <button type="button" className={styles["button"]} onClick={openOpenDiv}>
<div className="button-top">New Chat</div> <div className={styles["button-top"]}>New Chat</div>
<div className="button-bottom"></div> <div className={styles["button-bottom"]}></div>
<div className="button-base"></div> <div className={styles["button-base"]}></div>
</button> </button>
</div> </div>
<div className="chat-list"> <div className={styles["chat-list"]}>
{rooms.map((room) => ( {rooms.map((room) => (
<div className="chat-item" key={room.roomId} onClick={() => onRoomClick(room.roomId, room.roomName)}> <div className={styles["chat-item"]} key={room.roomId}>
<img src={RoomProfile} alt="Avatar" className="avatar" /> <img src={RoomProfile} alt="Avatar" className={styles["avatar"]} />
<div className="chat-info"> <div className={styles["chat-info"]}>
<h3>{room.roomName}</h3> <h3 onClick={() => onRoomClick(room.roomId, room.roomName)}>{room.roomName}</h3>
<span className="chat-message"> <span className={styles["chat-message"]}>
{room.lastSender.Valid ? room.lastSender.String : ''}: {room.lastSender.Valid ? room.lastSender.String : ''}:
{room.lastContent.Valid ? room.lastContent.String : ''}</span> {room.lastContent.Valid ? room.lastContent.String : ''}</span>
<span className="chat-time">{room.lastTime.Valid ? formatTimeToHoursMinutes(room.lastTime.Time) : ''}</span> <span className={styles["chat-time"]}>{room.lastTime.Valid ? formatTimeToHoursMinutes(room.lastTime.Time) : ''}</span>
</div>
<div
className={styles["chat-room-menu"]}
data-menu="room-actions"
onClick={(e) => {
e.stopPropagation();
setOpenMenuFor(prev => (prev === room.roomId ? null : room.roomId));
}}
aria-label="Room actions"
title="Room actions"
>
···
<div className={`${styles["menu-dropdown"]} ${openMenuFor === room.roomId ? styles["menu-open"] : ''}`}>
<button
className={styles["menu-item"]}
onClick={(e) => { e.stopPropagation(); setOpenMenuFor(null); onRename(room.roomId, room.roomName); }}
>
Rename room
</button>
<button
className={styles["menu-item"]}
onClick={(e) => { e.stopPropagation(); setOpenMenuFor(null); onDelete(room.roomId); }}
>
Delete room
</button>
</div>
</div> </div>
</div> </div>
))} ))}
...@@ -65,8 +105,8 @@ function RoomEntry ({rooms, onRoomClick} : {rooms: RoomEntryProps[], onRoomClick ...@@ -65,8 +105,8 @@ function RoomEntry ({rooms, onRoomClick} : {rooms: RoomEntryProps[], onRoomClick
function formatTimeToHoursMinutes(isoString: string) { function formatTimeToHoursMinutes(isoString: string) {
const date = new Date(isoString); const date = new Date(isoString);
const hours = String(date.getHours()).padStart(2, '0'); // 确保两位数 const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0'); // 确保两位数 const minutes = String(date.getMinutes()).padStart(2, '0');
return `${hours}:${minutes}`; return `${hours}:${minutes}`;
} }
...@@ -79,12 +119,12 @@ function InputRoomNameArea({ onAddNewRoom }: { onAddNewRoom: (roomName: string) ...@@ -79,12 +119,12 @@ function InputRoomNameArea({ onAddNewRoom }: { onAddNewRoom: (roomName: string)
closeOpenDiv(); closeOpenDiv();
} }
return ( return (
<div className="open"> <div className={styles["open"]}>
<div className="roomName-input"> <div className={styles["roomName-input"]}>
<h3>Please Enter the New Room Name</h3> <h3>Please Enter the New Room Name</h3>
<input <input
type="text" type="text"
className="RoomNameInput" className={styles["RoomNameInput"]}
placeholder="Start new chat" placeholder="Start new chat"
value = {roomNameInput} value = {roomNameInput}
onChange={(e) => setRoomNameInput(e.target.value)} onChange={(e) => setRoomNameInput(e.target.value)}
...@@ -97,9 +137,9 @@ function InputRoomNameArea({ onAddNewRoom }: { onAddNewRoom: (roomName: string) ...@@ -97,9 +137,9 @@ function InputRoomNameArea({ onAddNewRoom }: { onAddNewRoom: (roomName: string)
} }
}} }}
/> />
<div className="button-container"> <div className={styles["button-container"]}>
<button className="create-button" onClick={handleAddNewRoom}>Submit</button> <button className={styles["create-button"]} onClick={handleAddNewRoom}>Submit</button>
<button className="cancel-button" onClick={closeOpenDiv}>Cancel</button> <button className={styles["cancel-button"]} onClick={closeOpenDiv}>Cancel</button>
</div> </div>
</div> </div>
</div> </div>
...@@ -107,25 +147,33 @@ function InputRoomNameArea({ onAddNewRoom }: { onAddNewRoom: (roomName: string) ...@@ -107,25 +147,33 @@ function InputRoomNameArea({ onAddNewRoom }: { onAddNewRoom: (roomName: string)
} }
function openOpenDiv() { function openOpenDiv() {
const openDiv = document.getElementsByClassName("open")[0] as HTMLDivElement; const openDiv = document.getElementsByClassName(styles.open)[0] as HTMLDivElement | undefined;
if (openDiv) {
openDiv.style.zIndex = "1000"; openDiv.style.zIndex = "1000";
const roomNameInput = document.getElementsByClassName("RoomNameInput")[0] as HTMLInputElement; }
const roomNameInput = document.getElementsByClassName(styles.RoomNameInput)[0] as HTMLInputElement | undefined;
if (roomNameInput) {
roomNameInput.style.zIndex = "1001"; roomNameInput.style.zIndex = "1001";
}
} }
function closeOpenDiv() { function closeOpenDiv() {
const openDiv = document.getElementsByClassName("open")[0] as HTMLDivElement; const openDiv = document.getElementsByClassName(styles.open)[0] as HTMLDivElement | undefined;
if (openDiv) {
openDiv.style.zIndex = "0"; openDiv.style.zIndex = "0";
const roomNameInput = document.getElementsByClassName("RoomNameInput")[0] as HTMLInputElement; }
const roomNameInput = document.getElementsByClassName(styles.RoomNameInput)[0] as HTMLInputElement | undefined;
if (roomNameInput) {
roomNameInput.style.zIndex = "0"; roomNameInput.style.zIndex = "0";
(document.getElementsByClassName("RoomNameInput")[0] as HTMLInputElement).value = ''; roomNameInput.value = '';
}
} }
function MessageItem (props: MessageProps & { onAddNewComment: (content: string) => void}) { function MessageItem (props: MessageProps & { onAddNewComment: (content: string) => void}) {
const [inputValue, setInputValue] = useState(""); const [inputValue, setInputValue] = useState("");
if (props.roomId === 0) { if (props.roomId === 0) {
return <div className="message-item">Please select a room to chat.</div>; return <div className={styles["message-item"]}>Please select a room to chat.</div>;
} }
const handlerSend = () => { const handlerSend = () => {
...@@ -137,30 +185,30 @@ function MessageItem (props: MessageProps & { onAddNewComment: (content: string) ...@@ -137,30 +185,30 @@ function MessageItem (props: MessageProps & { onAddNewComment: (content: string)
setInputValue(''); setInputValue('');
} }
return ( return (
<div className="message-item"> <div className={styles["message-item"]}>
<div className="message-header"> <div className={styles["message-header"]}>
<img src={RoomProfile} alt="Avatar" className="avatar" /> <img src={RoomProfile} alt="Avatar" className={styles["avatar"]} />
<h2>{props.roomName}</h2> <h2>{props.roomName}</h2>
</div> </div>
<div className="message-list"> <div className={styles["message-list"]}>
{props.messages.map((msg, index) => ( {props.messages.map((msg, index) => (
<div key={index} className="message"> <div key={index} className={styles["message"]}>
<img src={Profile[msg.profile]} alt={`${msg.sender}'s avatar`} className="avatar" /> <img src={Profile[msg.profile]} alt={`${msg.sender}'s avatar`} className={styles["avatar"]} />
<div className="message-content"> <div className={styles["message-content"]}>
<div className="message-info"> <div className={styles["message-info"]}>
<span className="message-sender">{msg.sender}</span> <span className={styles["message-sender"]}>{msg.sender}</span>
<span className="message-time">{formatTimeToHoursMinutes(msg.time)}</span> <span className={styles["message-time"]}>{formatTimeToHoursMinutes(msg.time)}</span>
</div> </div>
<p className="message-text">{msg.content}</p> <p className={styles["message-text"]}>{msg.content}</p>
</div> </div>
</div> </div>
))} ))}
</div> </div>
<div className="message-input"> <div className={styles["message-input"]}>
<input <input
type="text" type="text"
placeholder="Type a message..." placeholder="Type a message..."
className="Inputarea" className={styles["Inputarea"]}
value={inputValue} value={inputValue}
onChange={(e) => setInputValue(e.target.value)} onChange={(e) => setInputValue(e.target.value)}
onKeyUpCapture={ onKeyUpCapture={
...@@ -169,9 +217,9 @@ function MessageItem (props: MessageProps & { onAddNewComment: (content: string) ...@@ -169,9 +217,9 @@ function MessageItem (props: MessageProps & { onAddNewComment: (content: string)
handlerSend(); handlerSend();
} }
}}/> }}/>
<button className="send-button" onClick={handlerSend}> <button className={styles["send-button"]} onClick={handlerSend}>
<div className="svg-wrapper-1"> <div className={styles["svg-wrapper-1"]}>
<div className="svg-wrapper"> <div className={styles["svg-wrapper"]}>
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24" viewBox="0 0 24 24"
...@@ -194,11 +242,13 @@ function MessageItem (props: MessageProps & { onAddNewComment: (content: string) ...@@ -194,11 +242,13 @@ function MessageItem (props: MessageProps & { onAddNewComment: (content: string)
// From Uiverse.io by adamgiebl // From Uiverse.io by adamgiebl
} }
export function ChatRoom({ userName }: { userName: string }) { export function ChatRoomComponent({ userName }: { userName: string }) {
const [rooms, setRooms] = useState<RoomEntryProps[]>([]); const [rooms, setRooms] = useState<RoomEntryProps[]>([]);
const [currentRoom, setCurrentRoom] = useState<MessageProps | null>(null); const [currentRoom, setCurrentRoom] = useState<MessageProps | null>(null);
useEffect(() => { const ROOM_LIST_REFRESH_INTERVAL = 1000;
const MESSAGE_REFRESH_INTERVAL = 1000;
const fetchRooms = async () => { const fetchRooms = async () => {
try { try {
const response = await fetch(backEnd+"/room/list"); const response = await fetch(backEnd+"/room/list");
...@@ -215,39 +265,57 @@ export function ChatRoom({ userName }: { userName: string }) { ...@@ -215,39 +265,57 @@ export function ChatRoom({ userName }: { userName: string }) {
} catch (error) { } catch (error) {
console.error("Error fetching rooms:", error); console.error("Error fetching rooms:", error);
} }
} };
fetchRooms();
}, []);
const handleRoomClick = async (roomId: number, roomName: string) => { const fetchCurrentRoomMessages = async (roomId: number) => {
setCurrentRoom({ if (!roomId) return;
roomId: roomId,
roomName: roomName,
messages: []
});
try { try {
const response = await fetch(backEnd+`/room/message/list?roomId=${roomId}`) const response = await fetch(backEnd+`/room/message/list?roomId=${roomId}`);
const result = await response.json(); const result = await response.json();
debugger;
if (result.code === 0) { if (result.code === 0) {
setCurrentRoom({ setCurrentRoom(prev => {
roomId: roomId, if (!prev || prev.roomId !== roomId) return prev;
roomName: roomName, return {
...prev,
messages: result.data || [] messages: result.data || []
};
}); });
} else { }
alert(`Error fetching messages: ${result.msg}`); } catch (error) {
console.error("Error fetching messages:", error);
}
};
useEffect(() => {
fetchRooms();
const roomListInterval = setInterval(() => {
fetchRooms();
}, ROOM_LIST_REFRESH_INTERVAL);
return () => clearInterval(roomListInterval);
}, []);
useEffect(() => {
if (!currentRoom?.roomId) return;
const messageInterval = setInterval(() => {
fetchCurrentRoomMessages(currentRoom.roomId);
}, MESSAGE_REFRESH_INTERVAL);
return () => clearInterval(messageInterval);
}, [currentRoom?.roomId]);
const handleRoomClick = async (roomId: number, roomName: string) => {
setCurrentRoom({ setCurrentRoom({
roomId: roomId, roomId: roomId,
roomName: roomName, roomName: roomName,
messages: [] messages: []
}); });
}
} catch (error){ await fetchCurrentRoomMessages(roomId);
console.error("Error fetching messages:", error);
alert("Failed to fetch messages. See console for details.");
}
} }
async function addNewRoom(roomName: string) { async function addNewRoom(roomName: string) {
...@@ -334,9 +402,51 @@ export function ChatRoom({ userName }: { userName: string }) { ...@@ -334,9 +402,51 @@ export function ChatRoom({ userName }: { userName: string }) {
} }
} }
const handleRename = async (roomId: number, currentName: string) => {
const newName = prompt('Enter new room name', currentName);
if (!newName || newName.trim() === '' || newName === currentName) return;
try {
const response = await fetch(backEnd + '/room/rename', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ roomId, roomName: newName })
});
const result = await response.json();
if (result.code !== 0) {
alert('Rename failed: ' + (result.msg || 'unknown error'));
return;
}
setRooms(prev => prev.map(r => r.roomId === roomId ? { ...r, roomName: newName } : r));
setCurrentRoom(prev => prev && prev.roomId === roomId ? { ...prev, roomName: newName } : prev);
} catch (err) {
console.error('Rename error', err);
alert('Rename error');
}
};
const handleDelete = async (roomId: number) => {
if (!confirm('Delete this room? This cannot be undone.')) return;
try {
const response = await fetch(backEnd + '/room/delete?roomId=' + roomId, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
});
const result = await response.json();
if (result.code !== 0) {
alert('Delete failed: ' + (result.msg || 'unknown error'));
return;
}
setRooms(prev => prev.filter(r => r.roomId !== roomId));
setCurrentRoom(prev => (prev && prev.roomId === roomId) ? null : prev);
} catch (err) {
console.error('Delete error', err);
alert('Delete error');
}
};
return ( return (
<div className="chat-room"> <div className={styles["chat-room"]}>
<RoomEntry rooms={rooms} onRoomClick={handleRoomClick}/> <RoomEntry rooms={rooms} onRoomClick={handleRoomClick} onRename={handleRename} onDelete={handleDelete}/>
<MessageItem <MessageItem
roomId={currentRoom?.roomId || 0} roomId={currentRoom?.roomId || 0}
roomName={currentRoom?.roomName || ""} roomName={currentRoom?.roomName || ""}
...@@ -347,3 +457,23 @@ export function ChatRoom({ userName }: { userName: string }) { ...@@ -347,3 +457,23 @@ export function ChatRoom({ userName }: { userName: string }) {
</div> </div>
); );
} }
export default function ChatRoom() {
const [userName, setUserName] = useState<string | null>(null);
const router = useRouter();
useEffect(() => {
const storedUserName = localStorage.getItem('userName');
if (storedUserName) {
setUserName(storedUserName);
} else {
router.push('/');
}
}, [router]);
if (!userName) {
return <div>Loading...</div>;
}
return <ChatRoomComponent userName={userName} />;
}
\ No newline at end of file
* /*
{ {
margin: 0; margin: 0;
padding: 0; padding: 0;
box-sizing: border-box; box-sizing: border-box;
font-family: 'Poppins', sans-serif; font-family: 'Poppins', sans-serif;
} } */
.SetName-Body .SetName-Body
{ {
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
min-height: 100vh; min-height: 100vh;
width: 100vw; width: 100vw;
background-size: cover; background-size: cover;
background: url('../../public/SetbackGround.jpg') no-repeat center; background: url('../public/SetbackGround.jpg') no-repeat center;
} }
.login-box .login-box
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
backdrop-filter: blur(15px); backdrop-filter: blur(15px);
} }
h2 .setname-h2
{ {
font-size: 2em; font-size: 2em;
color: white; color: white;
...@@ -73,7 +73,7 @@ h2 ...@@ -73,7 +73,7 @@ h2
border: none; border: none;
outline: none; outline: none;
background-color: transparent; background-color: transparent;
color: white; color: black;
font-size: 1em; font-size: 1em;
padding: 0 35px 0 5px; padding: 0 35px 0 5px;
} }
...@@ -105,3 +105,10 @@ h2 ...@@ -105,3 +105,10 @@ h2
{ {
background-color: lightgray; background-color: lightgray;
} }
.setname-repeat
{
font-size: .9em;
color: white;
margin: 20px 0 10px 0;
}
\ No newline at end of file
'use client';
import styles from"./Register.module.css"
import { MdLock, MdPerson } from "react-icons/md";
import { useState } from "react";
const backEnd:string = "http://localhost:8080";
export default function Register({ onLogin }: { onLogin: (name: string) => void }) {
const [userName, setUserName] = useState('');
const [password, setPassword] = useState('');
const [confirmPassword, setConfirmPassword] = useState('');
const [passwordError, setPasswordError] = useState('');
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setPasswordError('');
if (password !== confirmPassword) {
setPasswordError('Passwords do not match');
return;
}
if (password.length < 6) {
setPasswordError('Password must be at least 6 characters');
return;
}
try {
const response = await fetch(backEnd + '/register', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ userName, password }),
});
if (!response.ok) {
throw new Error('Failed to register');
}
const data = await response.json();
onLogin(data.userName);
} catch (error) {
console.error(error);
}
}
return (
<div className={styles["SetName-Body"]}>
<div className={styles["login-box"]}>
<form onSubmit={handleSubmit}>
<h2 className={styles["setname-h2"]}>Register</h2>
<div className={styles["input-box"]}>
<span className={styles["icon"]}>
<MdPerson />
</span>
<input
required
value={userName}
onChange={(e) => setUserName(e.target.value)} />
<label>Name</label>
</div>
<div className={styles["input-box"]}>
<span className={styles["icon"]}>
<MdLock />
</span>
<input
type="password"
required
value={password}
onChange={(e) => setPassword(e.target.value)} />
<label>Password</label>
</div>
<div className={styles["input-box"]}>
<span className={styles["icon"]}>
<MdLock />
</span>
<input
type="password"
required
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)} />
<label>Confirm Password</label>
</div>
{passwordError && (
<div style={{ color: 'red', fontSize: '14px', marginTop: '5px', textAlign: 'center' }}>
{passwordError}
</div>
)}
<div>
<button className={styles["SetName-button"]} type="submit">Register</button>
</div>
</form>
</div>
</div>
);
}
\ No newline at end of file
/*
{
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Poppins', sans-serif;
} */
.SetName-Body
{
z-index: 2000;
display: flex;
position: absolute;
justify-content: center;
align-items: center;
min-height: 100vh;
width: 100vw;
background-size: cover;
background: url('../public/SetbackGround.jpg') no-repeat center;
}
.login-box
{
position: relative;
width: 400px;
height: 450px;
background-color: transparent;
border: 2px solid rgba(255, 255 , 255, .5);
border-radius: 20px;
display: flex;
justify-content: center;
align-items: center;
backdrop-filter: blur(15px);
}
.setname-h2
{
font-size: 2em;
color: white;
text-align: center;
}
.input-box
{
position: relative;
width: 310px;
margin: 30px 0;
border-bottom: 2px solid white;
}
.input-box label
{
position: absolute;
top: 50%;
left: 5px;
transform: translateY(-50%);
font-size: 1em;
color: white;
pointer-events: none;
transition: .5s;
}
.input-box input:focus~label,
.input-box input:valid~label
{
top: -5px;
}
.input-box input
{
width: 100%;
height: 50px;
border: none;
outline: none;
background-color: transparent;
color: black;
font-size: 1em;
padding: 0 35px 0 5px;
}
.input-box .icon
{
position: absolute;
right: 8px;
color: white;
font-size: .9em;
line-height: 57px;
}
.SetName-button
{
outline: none;
border: none;
width: 100%;
height: 50px;
background-color: white;
border-radius: 40px;
cursor: pointer;
color: black;
font-size: 1em;
font-weight: bold;
}
.SetName-button:hover
{
background-color: lightgray;
}
.register_link
{
font-size: .9em;
text-align: center;
color: white;
margin: 20px 0 10px 0;
a
{
text-decoration: none;
color: white;
font-weight: 600;
}
a:hover
{
text-decoration: underline;
}
}
\ No newline at end of file
'use client';
import styles from"./SetName.module.css"
import { MdLock, MdPerson } from "react-icons/md";
import { useState } from "react";
import Link from "next/link";
const backEnd:string = "http://localhost:8080";
export default function SetName({ onLogin }: { onLogin: (name: string) => void }) {
const [userName, setUserName] = useState('');
const [password, setPassword] = useState('');
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
e.preventDefault();
try {
const response = await fetch(`${backEnd}/login`, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({ userName, password })
});
const data = await response.json();
if (data.code === 0) {
onLogin(userName);
} else {
alert(data.msg);
}
} catch (error) {
console.error("Error logging in:", error);
}
}
return (
<div className={styles["SetName-Body"]}>
<div className={styles["login-box"]}>
<form onSubmit={handleSubmit}>
<h2 className={styles["setname-h2"]}>Login</h2>
<div className={styles["input-box"]}>
<span className={styles["icon"]}>
<MdPerson />
</span>
<input
required
value={userName}
onChange={(e) => setUserName(e.target.value)} />
<label>Name</label>
</div>
<div className={styles["input-box"]}>
<span className={styles["icon"]}>
<MdLock />
</span>
<input
type="password"
required
value={password}
onChange={(e) => setPassword(e.target.value)} />
<label>Password</label>
</div>
<div>
<button className={styles["SetName-button"]} type="submit">Login in</button>
</div>
<div className={styles["register_link"]}>
<p>Don't have an account?<Link href="/Register">Register</Link></p>
</div>
</form>
</div>
</div>
);
}
\ No newline at end of file
'use client'; 'use client';
import React, { useState } from 'react'; import React from 'react';
import { ChatRoom } from "./pages/ChatRoom/ChatRoom"; import SetName from './SetName/page';
import { SetName } from './pages/SetName/SetName';
import "./globals.css"; import "./globals.css";
import { useRouter } from 'next/navigation';
export default function Page() { export default function Page() {
const [userName, setUserName] = useState(''); const router = useRouter();
const handleLogin = (name: string) => { const handleLogin = (name: string) => {
setUserName(name); localStorage.setItem('userName', name);
router.push('/ChatRoom');
}; };
return ( return (
<div className='root'> <div className='root'>
{!userName ? (
<SetName onLogin={handleLogin} /> <SetName onLogin={handleLogin} />
) : (
<ChatRoom userName={userName} />
)}
</div> </div>
); );
} }
\ No newline at end of file
'use client';
import "./SetName.css"
import { MdPerson } from "react-icons/md";
import { useRouter } from "next/compat/router";
import { useState } from "react";
export function SetName({ onLogin }: { onLogin: (name: string) => void }) {
const [userName, setUserName] = useState('');
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
e.preventDefault();
if(userName.trim()) {
onLogin(userName);
}
}
return (
<div className="SetName-Body">
<div className="login-box">
<form onSubmit={handleSubmit}>
<h2>Login</h2>
<div className="input-box">
<span className="icon">
<MdPerson />
</span>
<input
required
value={userName}
onChange={(e) => setUserName(e.target.value)} />
<label>Name</label>
</div>
<div>
<button className="SetName-button" type="submit">Login in</button>
</div>
</form>
</div>
</div>
);
}
\ No newline at end of file
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment