Skip to content

Commit

Permalink
✨feature: complete guestbook.
Browse files Browse the repository at this point in the history
  • Loading branch information
dedeard committed Feb 25, 2024
1 parent fd7185f commit 1831770
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 30 deletions.
43 changes: 38 additions & 5 deletions src/app/(root)/(app)/guestbook/components/FormSignGuestbook.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,37 @@
'use client'

import React from 'react'
import React, { useState } from 'react'
import { addDoc, collection, serverTimestamp } from 'firebase/firestore'
import { useAuth } from '@/contexts/AuthContext'
import { db } from '@/utils/firebase'

const FormSignGuestbook = () => {
const { isInitLoading, isAuthLoading, error, login, logout, user } = useAuth()
const [value, setValue] = useState('')
const [formError, setFormError] = useState('')

const handleSubmit: React.FormEventHandler<HTMLFormElement> = async (e) => {
e.preventDefault()

const message = value

setFormError('')
if (value.length == 0) return setFormError('Message Is Required.')

try {
const data = {
userId: user?.uid,
name: user?.displayName,
createdAt: serverTimestamp(),
message,
}
setValue('')
await addDoc(collection(db, 'guestbook'), data)
} catch (e: any) {
setFormError(e.message)
setValue(message)
}
}

return (
<>
Expand Down Expand Up @@ -42,27 +69,33 @@ const FormSignGuestbook = () => {
))}
</p>

{!!error && <div className="mb-3 border-l-4 border-red-500 bg-red-500/10 px-3 py-4 font-bold backdrop-blur-lg">{error}</div>}
{(error || formError) && (
<div className="mb-3 border-l-4 border-red-500 bg-red-500/10 px-3 py-4 font-bold backdrop-blur-lg">{error || formError}</div>
)}

<div className="mb-3 flex gap-3">
<form className="mb-3 flex gap-3" onSubmit={handleSubmit}>
<div className="flex-1 backdrop-blur">
<input
type="text"
name="message"
maxLength={256}
placeholder="Write your message here..."
disabled={!user}
className="block h-14 w-full border-black/10 bg-white text-sm text-black placeholder-black/60 opacity-60 focus:border-black/10 focus:border-b-black focus:opacity-100 focus:ring-0 dark:border-white/10 dark:bg-black dark:text-white dark:placeholder-white/60 dark:focus:border-b-white"
value={value}
onChange={(e) => setValue(e.currentTarget.value)}
/>
</div>
<div className="backdrop-blur">
<button
type="submit"
className="flex h-14 items-center gap-3 border border-black/10 bg-white px-3 font-bold uppercase opacity-75 dark:border-white/10 dark:bg-black hover:[&:not(:disabled)]:opacity-100"
disabled={!user}
>
Send Message
Submit
</button>
</div>
</div>
</form>
</>
)
}
Expand Down
70 changes: 70 additions & 0 deletions src/app/(root)/(app)/guestbook/components/GuestbookMessages.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
'use client'

import React, { useState, useEffect } from 'react'
import { Timestamp, collection, limit, onSnapshot, orderBy, query } from 'firebase/firestore'
import { db } from '@/utils/firebase'
import { IGuestbookMessage } from '@/types'

function formatDate(date: Date) {
const formatter = new Intl.DateTimeFormat('en-US', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
hour12: true,
})

return formatter.format(date).replace(/\//g, '-')
}

const GuestbookMessages: React.FC<{ initialMessages: string }> = ({ initialMessages }) => {
const [messages, setMessages] = useState<IGuestbookMessage[]>(() => {
return JSON.parse(initialMessages).map((el: IGuestbookMessage) => ({
...el,
createdAt: el.createdAt && new Timestamp(el.createdAt.seconds, el.createdAt.nanoseconds),
}))
})

useEffect(() => {
const colRef = collection(db, 'guestbook')
const q = query(colRef, orderBy('createdAt', 'desc'), limit(100))

const unsub = onSnapshot(q, (querySnapshot) => {
const messages: IGuestbookMessage[] = []
querySnapshot.forEach((doc) => {
messages.push({ _id: doc.id, ...doc.data() } as IGuestbookMessage)
})
setMessages(messages)
})

return () => unsub()
}, [])

return (
<div className="border border-black/5 bg-white/30 backdrop-blur dark:border-white/5 dark:bg-black/30">
<div className="divide-y">
{messages.map((message) => (
<p
key={message._id}
className="flex flex-col items-start gap-x-3 gap-y-1 border-black/5 p-3 text-xs dark:border-white/5 md:!text-sm lg:flex-row lg:py-2"
>
<span className="flex w-full shrink-0 items-center justify-between gap-x-2 truncate opacity-75 lg:w-36">
{message.name.substring(0, 20)}
<span className="flex shrink-0 items-center justify-center gap-x-2 text-xs opacity-75 lg:hidden">
{formatDate(message.createdAt?.toDate() || new Date())}
</span>
</span>
<span className="hidden lg:block">:</span>
<span className="flex-1 whitespace-pre-line">{message.message}</span>
<span className="hidden shrink-0 items-center justify-center gap-x-2 text-xs opacity-75 lg:flex">
{formatDate(message.createdAt?.toDate() || new Date())}
</span>
</p>
))}
</div>
</div>
)
}

export default GuestbookMessages
47 changes: 24 additions & 23 deletions src/app/(root)/(app)/guestbook/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import type { Metadata } from 'next'
import PageTitle from '../components/PageTitle'
import FormSignGuestbook from './components/FormSignGuestbook'
import GuestbookMessages from './components/GuestbookMessages'
import { collection, limit, getDocs, orderBy, query } from 'firebase/firestore'
import { db } from '@/utils/firebase'
import { IGuestbookMessage } from '@/types'

export const dynamic = 'force-dynamic'

export const metadata: Metadata = {
title: 'Guestbook - Dede Ariansya',
Expand All @@ -13,33 +19,28 @@ export const metadata: Metadata = {
},
}

export default function GuestbookPage() {
export default async function GuestbookPage() {
const messages = await loadMessages()

return (
<>
<PageTitle title="G-book" />
<FormSignGuestbook />
<div className="border border-black/5 bg-white/30 backdrop-blur dark:border-white/5 dark:bg-black/30">
<div className="divide-y lg:divide-y-0 lg:py-3">
{Array.from(Array(30)).map((_, i) => (
<pre
key={i}
className="flex flex-col items-start gap-x-2 border-black/5 p-3 text-xs dark:border-white/5 md:!text-sm lg:flex-row lg:py-0"
>
<code className="flex w-full shrink-0 items-center justify-between gap-x-2 truncate opacity-75 lg:w-36">
Samuel Pokam
<code className="flex shrink-0 items-center justify-center gap-x-2 opacity-75 lg:hidden">
<code>02-21-2024 09:28 AM</code>
</code>
</code>
<code className="hidden lg:block">:</code>
<code className="flex-1 whitespace-pre-line">awesome work</code>
<code className="hidden shrink-0 items-center justify-center gap-x-2 opacity-75 lg:flex">
<code>02-21-2024 09:28 AM</code>
</code>
</pre>
))}
</div>
</div>
<GuestbookMessages initialMessages={JSON.stringify(messages)} />
</>
)
}

const loadMessages = async () => {
const colRef = collection(db, 'guestbook')
const q = query(colRef, orderBy('createdAt', 'desc'), limit(100))

const querySnapshot = await getDocs(q)

const messages: IGuestbookMessage[] = []
querySnapshot.forEach((doc) => {
messages.push({ _id: doc.id, ...doc.data() } as IGuestbookMessage)
})

return messages
}
9 changes: 9 additions & 0 deletions src/types/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { Timestamp } from 'firebase/firestore'

export interface IRepository {
id: number
node_id: string
Expand Down Expand Up @@ -32,3 +34,10 @@ export interface IPost {
slug: string
content: string
}

export interface IGuestbookMessage {
_id: string
name: string
message: string
createdAt?: Timestamp
}
4 changes: 2 additions & 2 deletions src/utils/firebase.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { FIREBASE_CONFIG } from '@/constans/common'
import { initializeApp, getApps, getApp } from 'firebase/app'
import { getAuth, GithubAuthProvider, GoogleAuthProvider } from 'firebase/auth'
import { getDatabase } from 'firebase/database'
import { getFirestore } from 'firebase/firestore'

export const app = getApps().length > 0 ? getApp() : initializeApp(FIREBASE_CONFIG)

export const auth = getAuth(app)

export const db = getDatabase(app)
export const db = getFirestore(app)

export const getProviderById = (id: 'github.com' | 'google.com') => {
switch (id) {
Expand Down

0 comments on commit 1831770

Please sign in to comment.