Skip to content

Commit

Permalink
Merge pull request #3 from hinagiku-dev/backend/dashboard
Browse files Browse the repository at this point in the history
feat: add recent sessions(backend), grouping and chat page
  • Loading branch information
howard9199 authored Nov 24, 2024
2 parents 9d3ce2d + c0398b7 commit 2c75a95
Show file tree
Hide file tree
Showing 4 changed files with 174 additions and 7 deletions.
64 changes: 64 additions & 0 deletions src/routes/chat/+page.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<script>
let messages = [{ sender: 'other', text: 'Hello! How can I assist you today?' }];
let inputText = '';
function sendMessage() {
if (inputText.trim() !== '') {
messages = [...messages, { sender: 'me', text: inputText }];
inputText = '';
}
}
</script>

<div class="chat-container">
<div class="messages">
{#each messages as message}
<div class="message {message.sender}">
<strong>{message.sender === 'me' ? 'You' : 'Support'}:</strong>
{message.text}
</div>
{/each}
</div>
<div class="input-area">
<input
type="text"
bind:value={inputText}
placeholder="Type your message..."
on:keyup={(e) => e.key === 'Enter' && sendMessage()}
/>
<button on:click={sendMessage}>Send</button>
</div>
</div>

<style>
.chat-container {
max-width: 600px;
margin: 0 auto;
}
.messages {
height: 400px;
overflow-y: auto;
border: 1px solid #ccc;
padding: 10px;
}
.message {
margin: 10px 0;
}
.message.me {
text-align: right;
}
.message.other {
text-align: left;
}
.input-area {
display: flex;
margin-top: 10px;
}
.input-area input {
flex: 1;
padding: 10px;
}
.input-area button {
padding: 10px;
}
</style>
48 changes: 47 additions & 1 deletion src/routes/dashboard/+page.server.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { adminDb } from '$lib/server/firebase';
import { redirect } from '@sveltejs/kit';
import type { PageServerLoad } from './$types';

Expand All @@ -6,7 +7,52 @@ export const load: PageServerLoad = async ({ locals }) => {
throw redirect(303, '/login');
}

// find user's joined sessions
const joinedSessionsQuery = await adminDb
.collection('sessions')
.where(`participants.${locals.user.uid}`, '!=', null)
.get();

const joinedSessions = joinedSessionsQuery.docs.map((doc) => {
const data = doc.data();
const id = doc.id;
return {
...convertTimestamps(data),
id
};
});

// find user's created sessions
const createdSessionsQuery = await adminDb
.collection('sessions')
.where('hostId', '==', locals.user.uid)
.get();

const createdSessions = createdSessionsQuery.docs.map((doc) => {
const data = doc.data();
const id = doc.id;
return {
...convertTimestamps(data),
id
};
});

return {
user: locals.user
user: locals.user,
createdSessions: createdSessions,
joinedSessions: joinedSessions
};
};

function convertTimestamps(obj: FirebaseFirestore.DocumentData): FirebaseFirestore.DocumentData {
if (obj !== null && typeof obj === 'object') {
for (const key in obj) {
if (obj[key] && typeof obj[key].toDate === 'function') {
obj[key] = obj[key].toMillis();
} else if (obj[key] !== null && typeof obj[key] === 'object') {
obj[key] = convertTimestamps(obj[key]);
}
}
}
return obj;
}
48 changes: 44 additions & 4 deletions src/routes/join/+page.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,21 @@ export const actions = {

const data = await request.formData();
const tempId = data.get('tempId')?.toString();
const groupNumber = data.get('groupNumber')?.toString();

if (!tempId || !validateTempId(tempId)) {
return fail(400, { tempId, invalid: true });
return fail(400, { tempId, idInvalid: true });
}

// verify group number match "^(?:[1-9]|[1-4][0-9]|50)$"
if (!groupNumber || !/^(?:[1-9]|[1-4][0-9]|50)$/.test(groupNumber)) {
return fail(400, { groupNumber, groupNumberInvalid: true });
}

// Find session by tempId
const sessionQuery = await adminDb
.collection('sessions')
.where('tempId', '==', tempId)
.where('status', '==', 'waiting')
.limit(1)
.get();

Expand All @@ -35,15 +40,50 @@ export const actions = {
if (sessionData.tempIdExpiry.toDate() < new Date()) {
return fail(400, { tempId, expired: true });
}

// Add participant to session
await session.ref.update({
[`participants.${locals.user.uid}`]: {
name: locals.user.name,
joinedAt: new Date()
joinedAt: new Date(),
groupNumber: parseInt(groupNumber)
}
});

// Add participant to group
const groupSnapshot = await adminDb
.collection('groups')
.where('number', '==', parseInt(groupNumber))
.where('sessionId', '==', session.id)
.limit(1)
.get();

let groupRef;

if (groupSnapshot.empty) {
// Create new group
groupRef = adminDb.collection('groups').doc();
await groupRef.set({
id: groupRef.id,
number: parseInt(groupNumber),
sessionId: session.id,
participants: {
[locals.user.uid]: {
name: locals.user.name,
joinedAt: new Date()
}
}
});
} else {
// Add participant to existing group
groupRef = groupSnapshot.docs[0].ref;
await groupRef.update({
[`participants.${locals.user.uid}`]: {
name: locals.user.name,
joinedAt: new Date()
}
});
}

return { success: true, sessionId: session.id };
}
} satisfies Actions;
21 changes: 19 additions & 2 deletions src/routes/join/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
let { form } = $props();
let showScanner = $state(false);
let tempIdInput = $state('');
let tempGroupInput = $state('');
function handleScan(code: string) {
if (validateTempId(code)) {
Expand Down Expand Up @@ -33,7 +34,7 @@
<div>
<label for="tempId" class="mb-2 block font-medium">Session Code</label>
<input
type="text"
type="number"
id="tempId"
name="tempId"
bind:value={tempIdInput}
Expand All @@ -42,13 +43,29 @@
class="w-full rounded-lg border p-2"
placeholder="Enter 6-digit code"
/>
{#if form?.invalid}
{#if form?.idInvalid}
<p class="mt-1 text-sm text-red-600">Please enter a valid 6-digit code</p>
{/if}
{#if form?.notFound}
<p class="mt-1 text-sm text-red-600">Session not found or already started</p>
{/if}
</div>
<div>
<label for="groupNumber" class="mb-2 block font-medium">Group Number</label>
<input
type="number"
id="groupNumber"
name="groupNumber"
bind:value={tempGroupInput}
required
pattern="^(?:[1-9]|[1-4][0-9]|50)$"
class="w-full rounded-lg border p-2"
placeholder="輸入組別"
/>
{#if form?.groupNumberInvalid}
<p class="mt-1 text-sm text-red-600">請輸入有效的組別</p>
{/if}
</div>

<button
type="button"
Expand Down

0 comments on commit 2c75a95

Please sign in to comment.