update db str
This commit is contained in:
@@ -587,6 +587,14 @@
|
||||
background: #16a34a;
|
||||
}
|
||||
|
||||
.calendar-day-selected {
|
||||
box-shadow: inset 0 0 0 2px #1be62c;
|
||||
}
|
||||
.calendar-day-selected:not(.calendar-day-today):not(.calendar-day-has-entry) {
|
||||
background: #f0fdf4;
|
||||
color: #1be62c;
|
||||
}
|
||||
|
||||
/* Recent Entries */
|
||||
.recent-entries {
|
||||
margin-bottom: 1rem;
|
||||
|
||||
@@ -171,3 +171,16 @@ export async function getEntriesByDate(
|
||||
{ token }
|
||||
)
|
||||
}
|
||||
// ============================================
|
||||
// TIMEZONE CONVERSION ENDPOINTS
|
||||
// ============================================
|
||||
|
||||
export async function convertUTCToIST(utcTimestamp: string) {
|
||||
return apiCall<{ utc: string; ist: string }>(
|
||||
`/api/entries/convert-timestamp/utc-to-ist`,
|
||||
{
|
||||
method: 'POST',
|
||||
body: { timestamp: utcTimestamp },
|
||||
}
|
||||
)
|
||||
}
|
||||
106
src/lib/timezone.ts
Normal file
106
src/lib/timezone.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
* Timezone Utilities
|
||||
* Handles conversion between UTC and IST (Indian Standard Time)
|
||||
*/
|
||||
|
||||
/**
|
||||
* Convert UTC ISO string to IST
|
||||
* @param utcIsoString - UTC timestamp in ISO format (e.g., "2026-03-04T10:30:45.123Z")
|
||||
* @returns Date object in IST timezone
|
||||
*/
|
||||
export function utcToIST(utcIsoString: string): Date {
|
||||
return new Date(utcIsoString)
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a UTC ISO timestamp as IST
|
||||
* @param utcIsoString - UTC timestamp in ISO format
|
||||
* @param format - Format type: 'date', 'time', 'datetime', 'full'
|
||||
* @returns Formatted string in IST
|
||||
*/
|
||||
export function formatIST(
|
||||
utcIsoString: string,
|
||||
format: 'date' | 'time' | 'datetime' | 'full' = 'datetime'
|
||||
): string {
|
||||
const date = new Date(utcIsoString)
|
||||
|
||||
// IST is UTC+5:30
|
||||
const istDate = new Date(date.getTime() + 5.5 * 60 * 60 * 1000)
|
||||
|
||||
switch (format) {
|
||||
case 'date':
|
||||
return istDate.toLocaleDateString('en-IN', {
|
||||
year: 'numeric',
|
||||
month: 'short',
|
||||
day: '2-digit',
|
||||
}).toUpperCase()
|
||||
|
||||
case 'time':
|
||||
return istDate.toLocaleTimeString('en-IN', {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
hour12: false,
|
||||
}).toUpperCase()
|
||||
|
||||
case 'datetime':
|
||||
return istDate.toLocaleString('en-IN', {
|
||||
year: 'numeric',
|
||||
month: 'short',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
hour12: false,
|
||||
}).toUpperCase()
|
||||
|
||||
case 'full':
|
||||
return istDate.toLocaleString('en-IN', {
|
||||
weekday: 'short',
|
||||
year: 'numeric',
|
||||
month: 'short',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit',
|
||||
hour12: false,
|
||||
}).toUpperCase()
|
||||
|
||||
default:
|
||||
return istDate.toISOString()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get IST date components from UTC ISO string
|
||||
* @param utcIsoString - UTC timestamp in ISO format
|
||||
* @returns Object with date components in IST
|
||||
*/
|
||||
export function getISTDateComponents(utcIsoString: string) {
|
||||
const date = new Date(utcIsoString)
|
||||
const istDate = new Date(date.getTime() + 5.5 * 60 * 60 * 1000)
|
||||
|
||||
return {
|
||||
year: istDate.getUTCFullYear(),
|
||||
month: istDate.getUTCMonth(),
|
||||
date: istDate.getUTCDate(),
|
||||
day: istDate.getUTCDay(),
|
||||
hours: istDate.getUTCHours(),
|
||||
minutes: istDate.getUTCMinutes(),
|
||||
seconds: istDate.getUTCSeconds(),
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format date as YYYY-MM-DD (IST)
|
||||
* @param utcIsoString - UTC timestamp in ISO format
|
||||
* @returns Date string in YYYY-MM-DD format (IST)
|
||||
*/
|
||||
export function formatISTDateOnly(utcIsoString: string): string {
|
||||
const date = new Date(utcIsoString)
|
||||
const istDate = new Date(date.getTime() + 5.5 * 60 * 60 * 1000)
|
||||
|
||||
const year = istDate.getUTCFullYear()
|
||||
const month = String(istDate.getUTCMonth() + 1).padStart(2, '0')
|
||||
const day = String(istDate.getUTCDate()).padStart(2, '0')
|
||||
|
||||
return `${year}-${month}-${day}`
|
||||
}
|
||||
@@ -1,11 +1,13 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { useAuth } from '../contexts/AuthContext'
|
||||
import { getUserEntries, type JournalEntry } from '../lib/api'
|
||||
import { formatIST, formatISTDateOnly, getISTDateComponents } from '../lib/timezone'
|
||||
import BottomNav from '../components/BottomNav'
|
||||
|
||||
export default function HistoryPage() {
|
||||
const { user, userId, loading } = useAuth()
|
||||
const [currentMonth, setCurrentMonth] = useState(new Date())
|
||||
const [selectedDate, setSelectedDate] = useState(new Date())
|
||||
const [entries, setEntries] = useState<JournalEntry[]>([])
|
||||
const [loadingEntries, setLoadingEntries] = useState(false)
|
||||
|
||||
@@ -42,11 +44,11 @@ export default function HistoryPage() {
|
||||
|
||||
const hasEntryOnDate = (day: number) => {
|
||||
return entries.some((entry) => {
|
||||
const entryDate = new Date(entry.createdAt)
|
||||
const components = getISTDateComponents(entry.createdAt)
|
||||
return (
|
||||
entryDate.getDate() === day &&
|
||||
entryDate.getMonth() === currentMonth.getMonth() &&
|
||||
entryDate.getFullYear() === currentMonth.getFullYear()
|
||||
components.date === day &&
|
||||
components.month === currentMonth.getMonth() &&
|
||||
components.year === currentMonth.getFullYear()
|
||||
)
|
||||
})
|
||||
}
|
||||
@@ -61,18 +63,11 @@ export default function HistoryPage() {
|
||||
}
|
||||
|
||||
const formatDate = (date: string) => {
|
||||
return new Date(date).toLocaleDateString('en-US', {
|
||||
weekday: 'short',
|
||||
month: 'short',
|
||||
day: '2-digit',
|
||||
}).toUpperCase()
|
||||
return formatIST(date, 'date')
|
||||
}
|
||||
|
||||
const formatTime = (date: string) => {
|
||||
return new Date(date).toLocaleTimeString('en-US', {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
}).toUpperCase()
|
||||
return formatIST(date, 'time')
|
||||
}
|
||||
|
||||
const { daysInMonth, startingDayOfWeek } = getDaysInMonth(currentMonth)
|
||||
@@ -89,15 +84,28 @@ export default function HistoryPage() {
|
||||
setCurrentMonth(new Date(currentMonth.getFullYear(), currentMonth.getMonth() + 1))
|
||||
}
|
||||
|
||||
// Get entries for current month
|
||||
const currentMonthEntries = entries.filter((entry) => {
|
||||
const entryDate = new Date(entry.createdAt)
|
||||
// Get entries for selected date (in IST)
|
||||
const selectedDateEntries = entries.filter((entry) => {
|
||||
const components = getISTDateComponents(entry.createdAt)
|
||||
return (
|
||||
entryDate.getMonth() === currentMonth.getMonth() &&
|
||||
entryDate.getFullYear() === currentMonth.getFullYear()
|
||||
components.date === selectedDate.getDate() &&
|
||||
components.month === selectedDate.getMonth() &&
|
||||
components.year === selectedDate.getFullYear()
|
||||
)
|
||||
})
|
||||
|
||||
const isDateSelected = (day: number) => {
|
||||
return (
|
||||
day === selectedDate.getDate() &&
|
||||
currentMonth.getMonth() === selectedDate.getMonth() &&
|
||||
currentMonth.getFullYear() === selectedDate.getFullYear()
|
||||
)
|
||||
}
|
||||
|
||||
const handleDateClick = (day: number) => {
|
||||
setSelectedDate(new Date(currentMonth.getFullYear(), currentMonth.getMonth(), day))
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="history-page" style={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
||||
@@ -157,13 +165,14 @@ export default function HistoryPage() {
|
||||
const day = i + 1
|
||||
const hasEntry = hasEntryOnDate(day)
|
||||
const isTodayDate = isToday(day)
|
||||
const isSelected = isDateSelected(day)
|
||||
|
||||
return (
|
||||
<button
|
||||
key={day}
|
||||
type="button"
|
||||
className={`calendar-day ${hasEntry ? 'calendar-day-has-entry' : ''} ${isTodayDate ? 'calendar-day-today' : ''}`}
|
||||
onClick={() => console.log('View entries for', day)}
|
||||
className={`calendar-day ${hasEntry ? 'calendar-day-has-entry' : ''} ${isTodayDate ? 'calendar-day-today' : ''} ${isSelected ? 'calendar-day-selected' : ''}`}
|
||||
onClick={() => handleDateClick(day)}
|
||||
>
|
||||
{day}
|
||||
</button>
|
||||
@@ -173,7 +182,9 @@ export default function HistoryPage() {
|
||||
</div>
|
||||
|
||||
<section className="recent-entries">
|
||||
<h3 className="recent-entries-title">RECENT ENTRIES</h3>
|
||||
<h3 className="recent-entries-title">
|
||||
{selectedDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }).toUpperCase()}
|
||||
</h3>
|
||||
|
||||
{loadingEntries ? (
|
||||
<p style={{ color: '#9ca3af', fontSize: '0.875rem', textAlign: 'center', padding: '1.5rem 0', fontFamily: 'Inter, sans-serif' }}>
|
||||
@@ -181,12 +192,12 @@ export default function HistoryPage() {
|
||||
</p>
|
||||
) : (
|
||||
<div className="entries-list">
|
||||
{currentMonthEntries.length === 0 ? (
|
||||
{selectedDateEntries.length === 0 ? (
|
||||
<p style={{ color: '#9ca3af', fontSize: '0.875rem', textAlign: 'center', padding: '1.5rem 0', fontFamily: 'Inter, sans-serif' }}>
|
||||
No entries for this month yet. Start writing!
|
||||
No entries for this day yet. Start writing!
|
||||
</p>
|
||||
) : (
|
||||
currentMonthEntries.map((entry) => (
|
||||
selectedDateEntries.map((entry) => (
|
||||
<button
|
||||
key={entry.id}
|
||||
type="button"
|
||||
@@ -198,7 +209,6 @@ export default function HistoryPage() {
|
||||
<span className="entry-time">{formatTime(entry.createdAt)}</span>
|
||||
</div>
|
||||
<h4 className="entry-title">{entry.title}</h4>
|
||||
<p className="entry-preview">{entry.content}</p>
|
||||
</button>
|
||||
))
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user