refactor: migrate from Next.js to SolidJS and GraphQL

- Converted web application from Next.js to SolidJS with Vite
- Replaced React components with SolidJS components
- Implemented GraphQL client using URQL
- Added authentication, room, and chat components
- Updated project structure and configuration files
- Removed unnecessary Next.js and docs-related files
- Added Docker support for web and API applications
This commit is contained in:
Juan Sebastián Montoya 2025-03-04 01:08:52 -05:00
parent 8f3aa2fc26
commit 16731409df
81 changed files with 13585 additions and 1163 deletions

View file

@ -0,0 +1,176 @@
import { createSignal, createEffect, For, Show } from 'solid-js';
import { gql } from '@urql/core';
import { createQuery, createMutation, createSubscription } from '@urql/solid';
import { Message, Room } from '../types';
const ROOM_QUERY = gql`
query GetRoom($id: ID!) {
room(id: $id) {
id
name
description
isPrivate
owner {
id
username
}
members {
id
username
}
}
}
`;
const MESSAGES_QUERY = gql`
query GetMessages($roomId: ID!) {
messages(roomId: $roomId) {
id
content
createdAt
user {
id
username
}
}
}
`;
const SEND_MESSAGE_MUTATION = gql`
mutation SendMessage($content: String!, $roomId: ID!) {
sendMessage(content: $content, roomId: $roomId) {
id
content
createdAt
user {
id
username
}
}
}
`;
const MESSAGE_SUBSCRIPTION = gql`
subscription OnMessageAdded($roomId: ID!) {
messageAdded(roomId: $roomId) {
id
content
createdAt
user {
id
username
}
}
}
`;
interface ChatRoomProps {
roomId: string;
userId: string;
}
export function ChatRoom(props: ChatRoomProps) {
const [message, setMessage] = createSignal('');
const [messages, setMessages] = createSignal<Message[]>([]);
// Query room details
const [roomQuery] = createQuery({
query: ROOM_QUERY,
variables: { id: props.roomId },
});
// Query messages
const [messagesQuery] = createQuery({
query: MESSAGES_QUERY,
variables: { roomId: props.roomId },
});
// Send message mutation
const [, sendMessage] = createMutation(SEND_MESSAGE_MUTATION);
// Subscribe to new messages
const [messageSubscription] = createSubscription({
query: MESSAGE_SUBSCRIPTION,
variables: { roomId: props.roomId },
});
// Load initial messages
createEffect(() => {
const result = messagesQuery;
if (result.data?.messages) {
setMessages(result.data.messages);
}
});
// Handle new messages from subscription
createEffect(() => {
const result = messageSubscription;
if (result.data?.messageAdded) {
const newMessage = result.data.messageAdded;
setMessages((prev) => [...prev, newMessage]);
}
});
const handleSendMessage = async (e: Event) => {
e.preventDefault();
if (!message().trim()) return;
try {
await sendMessage({
content: message(),
roomId: props.roomId,
});
setMessage('');
} catch (error) {
console.error('Failed to send message:', error);
}
};
const formatTime = (dateString: string) => {
const date = new Date(dateString);
return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
};
return (
<div class='chat-room'>
<Show when={roomQuery.data?.room} fallback={<div>Loading room...</div>}>
<div class='chat-header'>
<h2>{roomQuery.data?.room.name}</h2>
<p>{roomQuery.data?.room.description}</p>
</div>
</Show>
<div class='chat-messages'>
<Show
when={!roomQuery.fetching}
fallback={<div>Loading messages...</div>}
>
<For each={messages()}>
{(message) => (
<div
class={`message ${message.user.id === props.userId ? 'own-message' : ''}`}
>
<div class='message-header'>
<span class='username'>{message.user.username}</span>
<span class='time'>{formatTime(message.createdAt)}</span>
</div>
<div class='message-content'>{message.content}</div>
</div>
)}
</For>
</Show>
</div>
<form class='message-form' onSubmit={handleSendMessage}>
<input
type='text'
value={message()}
onInput={(e) => setMessage(e.currentTarget.value)}
placeholder='Type a message...'
/>
<button type='submit'>Send</button>
</form>
</div>
);
}