Skip to content

Commit 279a69d

Browse files
committed
add test credentials, fix reaming, overdue bugs
1 parent 64b85cd commit 279a69d

6 files changed

Lines changed: 120 additions & 25 deletions

File tree

components/AuthForm.tsx

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import { FIELD_NAMES, FIELD_TYPES } from "@/constants";
2626
import FileUpload from "@/components/FileUpload";
2727
import { showToast } from "@/lib/toast";
2828
import { useRouter } from "next/navigation";
29+
import { CopyButton } from "@/components/CopyButton";
2930

3031
interface Props<T extends FieldValues> {
3132
schema: ZodType<T>;
@@ -135,6 +136,32 @@ const AuthForm = <T extends FieldValues>({
135136
{isSignIn ? "Create an account" : "Sign in"}
136137
</Link>
137138
</p>
139+
140+
{/* Test Credentials Section - Only for Sign In */}
141+
{isSignIn && (
142+
<div className=" rounded-lg border border-gray-600 bg-gray-800/30 px-4 py-2">
143+
<p className="mb-1 text-sm font-medium text-gray-300">
144+
To test the admin panel, you can login with:
145+
</p>
146+
<div className="space-y-2">
147+
<div className="flex items-center justify-between rounded-md bg-gray-700/50 px-3 py-2">
148+
<span className="text-sm text-gray-200">test@admin.com</span>
149+
<CopyButton
150+
text="test@admin.com"
151+
className="size-8 bg-gray-700/50"
152+
/>
153+
</div>
154+
<div className="flex items-center justify-between rounded-md bg-gray-700/50 px-3 py-2">
155+
<span className="text-sm text-gray-200">12345678</span>
156+
<CopyButton text="12345678" className="size-8 bg-gray-700/50" />
157+
</div>
158+
</div>
159+
<p className="mt-3 text-xs text-gray-300">
160+
⚠️ Please try not to mess up the current books, users and the admin
161+
panel setup!
162+
</p>
163+
</div>
164+
)}
138165
</div>
139166
);
140167
};

components/CopyButton.tsx

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,25 @@
11
"use client";
22

3+
import { useState } from "react";
34
import { Button } from "@/components/ui/button";
4-
import { Copy } from "lucide-react";
5+
import { Copy, Check } from "lucide-react";
56

67
interface CopyButtonProps {
78
text: string;
89
className?: string;
910
}
1011

1112
export const CopyButton = ({ text, className }: CopyButtonProps) => {
12-
const copyToClipboard = () => {
13-
navigator.clipboard.writeText(text);
13+
const [copied, setCopied] = useState(false);
14+
15+
const copyToClipboard = async () => {
16+
try {
17+
await navigator.clipboard.writeText(text);
18+
setCopied(true);
19+
setTimeout(() => setCopied(false), 2000); // Reset after 2 seconds
20+
} catch (err) {
21+
console.error("Failed to copy text: ", err);
22+
}
1423
};
1524

1625
return (
@@ -20,7 +29,11 @@ export const CopyButton = ({ text, className }: CopyButtonProps) => {
2029
onClick={copyToClipboard}
2130
className={className}
2231
>
23-
<Copy className="size-4" />
32+
{copied ? (
33+
<Check className="size-4 text-green-500" />
34+
) : (
35+
<Copy className="size-4" />
36+
)}
2437
</Button>
2538
);
2639
};

components/MyProfileTabs.tsx

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -76,12 +76,16 @@ const MyProfileTabs: React.FC<MyProfileTabsProps> = ({
7676
totalReviews,
7777
}) => {
7878
const router = useRouter();
79-
const formatDate = (date: Date | null) => {
79+
const formatDate = (date: Date | string | null) => {
8080
if (!date) return "N/A";
81-
return new Date(date).toLocaleDateString("en-US", {
81+
// Handle timezone-aware timestamps correctly
82+
const dateObj = typeof date === "string" ? new Date(date) : date;
83+
// Use UTC methods to avoid timezone conversion issues
84+
return dateObj.toLocaleDateString("en-US", {
8285
year: "numeric",
8386
month: "short",
8487
day: "numeric",
88+
timeZone: "UTC", // Force UTC to match database storage
8589
});
8690
};
8791

@@ -142,21 +146,47 @@ const MyProfileTabs: React.FC<MyProfileTabsProps> = ({
142146

143147
// Calculate if book is overdue (only for BORROWED status with dueDate)
144148
const today = new Date();
149+
// Use UTC dates for consistent comparison
150+
const todayUTC = new Date(
151+
today.getTime() + today.getTimezoneOffset() * 60000
152+
);
153+
const dueDateUTC = record.dueDate ? new Date(record.dueDate) : null;
154+
145155
const isOverdue =
146-
record.status === "BORROWED" &&
147-
record.dueDate &&
148-
today > new Date(record.dueDate);
149-
const daysOverdue = isOverdue
150-
? Math.floor(
151-
(today.getTime() - new Date(record.dueDate!).getTime()) /
152-
(1000 * 60 * 60 * 24)
156+
record.status === "BORROWED" && dueDateUTC && todayUTC > dueDateUTC;
157+
158+
// Calculate days overdue using date-level comparison (exactly like backend SQL)
159+
// Backend: (${now}::date - ${borrowRecords.dueDate}::date)
160+
// Use UTC dates to avoid timezone issues
161+
const todayDateUTC = new Date(
162+
Date.UTC(
163+
todayUTC.getUTCFullYear(),
164+
todayUTC.getUTCMonth(),
165+
todayUTC.getUTCDate()
166+
)
167+
);
168+
const dueDateOnlyUTC = dueDateUTC
169+
? new Date(
170+
Date.UTC(
171+
dueDateUTC.getUTCFullYear(),
172+
dueDateUTC.getUTCMonth(),
173+
dueDateUTC.getUTCDate()
174+
)
153175
)
154-
: 0;
176+
: null;
177+
178+
const daysOverdue =
179+
isOverdue && dueDateOnlyUTC
180+
? Math.floor(
181+
(todayDateUTC.getTime() - dueDateOnlyUTC.getTime()) /
182+
(1000 * 60 * 60 * 24)
183+
)
184+
: 0;
185+
155186
const daysRemaining =
156-
record.status === "BORROWED" && record.dueDate && !isOverdue
187+
record.status === "BORROWED" && dueDateUTC && !isOverdue
157188
? Math.ceil(
158-
(new Date(record.dueDate).getTime() - today.getTime()) /
159-
(1000 * 60 * 60 * 24)
189+
(dueDateUTC.getTime() - todayUTC.getTime()) / (1000 * 60 * 60 * 24)
160190
)
161191
: 0;
162192
const calculatedFine = isOverdue ? daysOverdue * 1.0 : 0;

lib/admin/actions/borrow.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,9 +94,10 @@ export const approveBorrowRequest = async (recordId: string) => {
9494
return { success: false, error: "Book is no longer available" };
9595
}
9696

97-
// Calculate due date (7 days from approval date)
97+
// Calculate due date (7 days from approval date, set to end of day)
9898
const dueDate = new Date();
9999
dueDate.setDate(dueDate.getDate() + 7);
100+
dueDate.setHours(23, 59, 59, 999); // Set to end of day
100101
const dueDateString = dueDate.toISOString().split("T")[0];
101102

102103
// Update borrow record status to BORROWED and set borrowedBy and dueDate

lib/admin/actions/bulk-operations.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ export async function bulkApproveBorrowRequests(recordIds: string[]) {
138138
try {
139139
const sevenDaysFromNow = new Date();
140140
sevenDaysFromNow.setDate(sevenDaysFromNow.getDate() + 7);
141+
sevenDaysFromNow.setHours(23, 59, 59, 999); // Set to end of day
141142

142143
await db
143144
.update(borrowRecords)

lib/admin/actions/reminders.ts

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -145,12 +145,18 @@ export class EmailService {
145145
// Reminder types
146146
export type ReminderType = "due_soon" | "overdue" | "return_reminder";
147147

148-
// Get books that are due soon (within 2 days)
148+
// Get books that are due soon (within 2 days, including today)
149149
export async function getBooksDueSoon() {
150150
const twoDaysFromNow = new Date();
151151
twoDaysFromNow.setDate(twoDaysFromNow.getDate() + 2);
152152

153153
const now = new Date();
154+
// Set to start of today to include books due today
155+
const startOfToday = new Date(
156+
now.getFullYear(),
157+
now.getMonth(),
158+
now.getDate()
159+
);
154160

155161
const dueSoonBooks = await db
156162
.select({
@@ -161,7 +167,7 @@ export async function getBooksDueSoon() {
161167
userEmail: users.email,
162168
borrowDate: borrowRecords.borrowDate,
163169
dueDate: borrowRecords.dueDate,
164-
daysUntilDue: sql<number>`(${borrowRecords.dueDate}::date - ${now}::date)`,
170+
daysUntilDue: sql<number>`(${borrowRecords.dueDate}::date - ${startOfToday}::date)`,
165171
})
166172
.from(borrowRecords)
167173
.innerJoin(books, eq(borrowRecords.bookId, books.id))
@@ -170,7 +176,7 @@ export async function getBooksDueSoon() {
170176
and(
171177
eq(borrowRecords.status, "BORROWED"),
172178
sql`${borrowRecords.dueDate} IS NOT NULL`,
173-
sql`${borrowRecords.dueDate} > ${now}`,
179+
sql`${borrowRecords.dueDate} >= ${startOfToday}`,
174180
sql`${borrowRecords.dueDate} <= ${twoDaysFromNow}`
175181
)
176182
);
@@ -181,6 +187,12 @@ export async function getBooksDueSoon() {
181187
// Get overdue books
182188
export async function getOverdueBooks() {
183189
const now = new Date();
190+
// Set to start of today to exclude books due today from overdue
191+
const startOfToday = new Date(
192+
now.getFullYear(),
193+
now.getMonth(),
194+
now.getDate()
195+
);
184196

185197
const overdueBooks = await db
186198
.select({
@@ -191,7 +203,7 @@ export async function getOverdueBooks() {
191203
userEmail: users.email,
192204
borrowDate: borrowRecords.borrowDate,
193205
dueDate: borrowRecords.dueDate,
194-
daysOverdue: sql<number>`(${now}::date - ${borrowRecords.dueDate}::date)`,
206+
daysOverdue: sql<number>`(${startOfToday}::date - ${borrowRecords.dueDate}::date)`,
195207
fineAmount: borrowRecords.fineAmount,
196208
})
197209
.from(borrowRecords)
@@ -201,7 +213,7 @@ export async function getOverdueBooks() {
201213
and(
202214
eq(borrowRecords.status, "BORROWED"),
203215
sql`${borrowRecords.dueDate} IS NOT NULL`,
204-
sql`${borrowRecords.dueDate} < ${now}`
216+
sql`${borrowRecords.dueDate} < ${startOfToday}`
205217
)
206218
);
207219

@@ -254,6 +266,8 @@ This is an automated reminder. For assistance, please contact us at support@book
254266
subject,
255267
body
256268
);
269+
// Update the lastReminderSent timestamp after successful email
270+
await updateLastReminderSent(book.recordId);
257271
results.push({
258272
recordId: book.recordId,
259273
userEmail: book.userEmail,
@@ -325,6 +339,8 @@ This is an automated notice. For assistance, please contact us at support@bookwi
325339
subject,
326340
body
327341
);
342+
// Update the lastReminderSent timestamp after successful email
343+
await updateLastReminderSent(book.recordId);
328344
results.push({
329345
recordId: book.recordId,
330346
userEmail: book.userEmail,
@@ -363,6 +379,13 @@ export async function getReminderStats() {
363379
const twoDaysFromNow = new Date();
364380
twoDaysFromNow.setDate(twoDaysFromNow.getDate() + 2);
365381

382+
// Set to start of today to include books due today
383+
const startOfToday = new Date(
384+
now.getFullYear(),
385+
now.getMonth(),
386+
now.getDate()
387+
);
388+
366389
const [dueSoonCount, overdueCount, remindersSentToday] = await Promise.all([
367390
db
368391
.select({ count: sql<number>`count(*)` })
@@ -371,7 +394,7 @@ export async function getReminderStats() {
371394
and(
372395
eq(borrowRecords.status, "BORROWED"),
373396
sql`${borrowRecords.dueDate} IS NOT NULL`,
374-
sql`${borrowRecords.dueDate} > ${now}`,
397+
sql`${borrowRecords.dueDate} >= ${startOfToday}`,
375398
sql`${borrowRecords.dueDate} <= ${twoDaysFromNow}`
376399
)
377400
),
@@ -382,7 +405,7 @@ export async function getReminderStats() {
382405
and(
383406
eq(borrowRecords.status, "BORROWED"),
384407
sql`${borrowRecords.dueDate} IS NOT NULL`,
385-
sql`${borrowRecords.dueDate} < ${now}`
408+
sql`${borrowRecords.dueDate} < ${startOfToday}`
386409
)
387410
),
388411
db

0 commit comments

Comments
 (0)