260223:1415 20260223 nextJS & nestJS Best pratices
All checks were successful
Build and Deploy / deploy (push) Successful in 4m44s
All checks were successful
Build and Deploy / deploy (push) Successful in 4m44s
This commit is contained in:
@@ -12,9 +12,10 @@ import { format } from "date-fns";
|
||||
export default async function DrawingDetailPage({
|
||||
params,
|
||||
}: {
|
||||
params: { id: string };
|
||||
params: Promise<{ id: string }>;
|
||||
}) {
|
||||
const id = parseInt(params.id);
|
||||
const { id: rawId } = await params;
|
||||
const id = parseInt(rawId);
|
||||
if (isNaN(id)) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
42
frontend/app/(dashboard)/error.tsx
Normal file
42
frontend/app/(dashboard)/error.tsx
Normal file
@@ -0,0 +1,42 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect } from 'react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { AlertCircle, RefreshCw } from 'lucide-react';
|
||||
import Link from 'next/link';
|
||||
|
||||
export default function DashboardError({
|
||||
error,
|
||||
reset,
|
||||
}: {
|
||||
error: Error & { digest?: string };
|
||||
reset: () => void;
|
||||
}) {
|
||||
useEffect(() => {
|
||||
console.error('[Dashboard Error Boundary]', error);
|
||||
}, [error]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center min-h-[60vh] gap-4 text-center px-4">
|
||||
<AlertCircle className="h-10 w-10 text-destructive" />
|
||||
<div>
|
||||
<h2 className="text-lg font-semibold">Something went wrong</h2>
|
||||
<p className="text-muted-foreground mt-1 text-sm max-w-md">
|
||||
{error.message || 'An error occurred while loading this page.'}
|
||||
</p>
|
||||
{error.digest && (
|
||||
<p className="text-xs text-muted-foreground mt-2">Error ID: {error.digest}</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex gap-3">
|
||||
<Button onClick={reset} variant="outline" size="sm">
|
||||
<RefreshCw className="mr-2 h-4 w-4" />
|
||||
Try again
|
||||
</Button>
|
||||
<Button asChild variant="ghost" size="sm">
|
||||
<Link href="/dashboard">Back to Dashboard</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import apiClient from "@/lib/api/client";
|
||||
import { toast } from "sonner";
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Schemas
|
||||
@@ -63,12 +64,12 @@ export default function ProfilePage() {
|
||||
currentPassword: data.currentPassword,
|
||||
newPassword: data.newPassword,
|
||||
});
|
||||
|
||||
alert("เปลี่ยนรหัสผ่านสำเร็จ"); // ในอนาคตใช้ Toast
|
||||
|
||||
toast.success('เปลี่ยนรหัสผ่านสำเร็จ');
|
||||
reset();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
alert("ไม่สามารถเปลี่ยนรหัสผ่านได้: รหัสผ่านปัจจุบันไม่ถูกต้อง");
|
||||
toast.error('ไม่สามารถเปลี่ยนรหัสผ่านได้: รหัสผ่านปัจจุบันไม่ถูกต้อง');
|
||||
console.error('[ProfilePage] onPasswordSubmit:', error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
@@ -159,8 +160,8 @@ export default function ProfilePage() {
|
||||
</div>
|
||||
</CardContent>
|
||||
{/* <CardFooter>
|
||||
<Button>บันทึกการเปลี่ยนแปลง</Button>
|
||||
</CardFooter>
|
||||
<Button>บันทึกการเปลี่ยนแปลง</Button>
|
||||
</CardFooter>
|
||||
*/}
|
||||
</Card>
|
||||
</TabsContent>
|
||||
@@ -243,7 +244,7 @@ export default function ProfilePage() {
|
||||
onCheckedChange={setNotifyEmail}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
<div className="flex items-center justify-between space-x-2">
|
||||
<Label htmlFor="notify-line" className="flex flex-col space-y-1">
|
||||
<span>LINE Notifications</span>
|
||||
@@ -273,7 +274,7 @@ export default function ProfilePage() {
|
||||
</div>
|
||||
</CardContent>
|
||||
<CardFooter>
|
||||
<Button variant="outline" onClick={() => alert("บันทึกการตั้งค่าแจ้งเตือนแล้ว")}>
|
||||
<Button variant="outline" onClick={() => toast.success('บันทึกการตั้งค่าแจ้งเตือนแล้ว')}>
|
||||
บันทึกการตั้งค่า
|
||||
</Button>
|
||||
</CardFooter>
|
||||
@@ -282,4 +283,4 @@ export default function ProfilePage() {
|
||||
</Tabs>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ import {
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import apiClient from "@/lib/api/client";
|
||||
import { toast } from "sonner";
|
||||
|
||||
// 1. กำหนด Schema สำหรับตรวจสอบข้อมูล (Validation)
|
||||
// อ้างอิงจาก Data Dictionary ตาราง projects
|
||||
@@ -58,7 +58,6 @@ export default function CreateProjectPage() {
|
||||
register,
|
||||
handleSubmit,
|
||||
setValue, // ใช้สำหรับ manual set value (เช่น Select)
|
||||
watch, // ใช้ดูค่า (สำหรับ Debug หรือ Conditional Logic)
|
||||
formState: { errors },
|
||||
} = useForm<ProjectValues>({
|
||||
resolver: zodResolver(projectSchema),
|
||||
@@ -82,12 +81,12 @@ export default function CreateProjectPage() {
|
||||
|
||||
// await apiClient.post("/projects", data);
|
||||
|
||||
alert("สร้างโครงการสำเร็จ"); // TODO: เปลี่ยนเป็น Toast
|
||||
router.push("/projects");
|
||||
toast.success('สร้างโครงการสำเร็จ');
|
||||
router.push('/projects');
|
||||
router.refresh();
|
||||
} catch (error) {
|
||||
console.error("Failed to create project:", error);
|
||||
alert("เกิดข้อผิดพลาดในการสร้างโครงการ");
|
||||
toast.error('เกิดข้อผิดพลาดในการสร้างโครงการ');
|
||||
console.error('[CreateProjectPage]', error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
@@ -202,7 +201,7 @@ export default function CreateProjectPage() {
|
||||
เราต้องใช้ onValueChange เพื่อเชื่อมกับ React Hook Form
|
||||
*/}
|
||||
<Select
|
||||
onValueChange={(value: any) => setValue("status", value)}
|
||||
onValueChange={(value: 'Active' | 'Inactive' | 'On Hold') => setValue('status', value)}
|
||||
defaultValue="Active"
|
||||
>
|
||||
<SelectTrigger>
|
||||
|
||||
Reference in New Issue
Block a user