1. Bootstrap භාවිතයෙන් පිරිසිදු Dashboard UI එකක්
අපි මේ වන විටත් Bootstrap library එක අපේ project එකට CDN link එකක් හරහා එකතු කරගෙන, එහි මූලික classes (container, btn, form-control) භාවිතා කළා. දැන් අපි UI එක තවදුරටත් uluchch කිරීමට Bootstrap Cards සහ Grid System එක භාවිතා කරමු.
Module 7 හි අපි සෑදූ NotesList.tsx component එකේ notes list එක list-group එකක් ලෙස සෑදුවෙමු. අපි එය දැන් Bootstrap Cards බවට පත් කර, grid layout එකක පෙන්වමු. එමගින් එක් එක් සටහන වෙන් වෙන්ව, පිළිවෙලකට පෙනෙනු ඇත.
➡️ NotesList.tsx හි සටහන් Cards ලෙස පෙන්වීම
ඔබේ components/NotesList.tsx file එකේ return statement එක තුළ ඇති notes list එකට අදාළ කේතය පහත පරිදි වෙනස් කරන්න. මෙහිදී අපි list-group වෙනුවට row සහ col classes භාවිතා කර grid එකක් සාදා, එක් එක් note එක card එකක් ලෙස පෙන්වමු.
// Inside NotesList.tsx return statement
<div className="row g-3"> {/* g-3 adds gaps between cards */}
{notes.length > 0 ? (
notes.map(note => (
<div key={note.id} className="col-md-6 col-lg-4">
<div className="card h-100">
<div className="card-body d-flex flex-column">
<h5 className="card-title">{note.title}</h5>
<p className="card-text flex-grow-1">{note.content}</p>
<small className="text-muted">
{new Date(note.created_at).toLocaleDateString()}
</small>
<div className="mt-3">
<button className="btn btn-sm btn-secondary me-2" onClick={() => handleEdit(note)}>Edit</button>
<button className="btn btn-sm btn-danger" onClick={() => handleDelete(note.id)}>Delete</button>
</div>
</div>
</div>
</div>
))
) : (
<div className="col-12">
<p>You haven't created any notes yet.</p>
</div>
)}
</div>
2. Responsive Layout (Mobile & Desktop)
Responsive design යනු වෙබ් අඩවියේ layout එක screen size එක අනුව (mobile, tablet, desktop) ස්වයංක්රීයව වෙනස් වීමයි. Bootstrap හි Grid System එක මේ සඳහා අපට විශාල සහයක් ලබා දේ.
ඉහත කේතයේ අප භාවිතා කළ classes වල තේරුම:
row: Cards තිරස් අතට පිළිවෙලකට තැබීමට container එකක් ලෙස ක්රියා කරයි.col-md-6: Medium (md) screen sizes වලදී (tablets සහ ඊට ඉහළ), grid එකේ columns 12න් 6ක් (එනම් 50% පළලක්) ලබා ගනී. ඒ අනුව, පේළියකට cards දෙකක් පෙන්වයි.col-lg-4: Large (lg) screen sizes වලදී (desktops), columns 12න් 4ක් (33.3% පළලක්) ලබා ගනී. ඒ අනුව, පේළියකට cards තුනක් පෙන්වයි.- Mobile screens වලදී, මෙම class ක්රියාත්මක නොවන නිසා, එක් card එකක් 100% පළලක් ලබාගෙන, එකක් යට එකක් ලෙස පිළිවෙලකට පෙන්වයි.
මේ ආකාරයට, කිසිදු අමතර CSS කේතයක් නොමැතිව අපේ app එක විවිධ devices වලට ගැලපෙන ලෙස සකස් වී ඇත.
3. Flash Messages / Toast Notifications
User කෙනෙක් යම් ක්රියාවක් (සටහනක් save කිරීම, delete කිරීම) කළ පසු, "Your note was saved successfully" වැනි පණිවිඩයක් පෙන්වීම ඉතා හොඳ user experience (UX) එකකි. මේවා **Flash Messages** හෝ **Toast Notifications** ලෙස හැඳින්වේ.
➡️ Toast.tsx Component එක සෑදීම
components folder එක තුළ Toast.tsx නමින් අලුත් file එකක් සාදා, toast එක පෙන්වීමට අවශ්ය UI සහ logic එක නිර්මාණය කරමු.
'use client';
import { useEffect } from 'react';
interface ToastProps {
message: string;
type: 'success' | 'error';
onClose: () => void;
}
export default function Toast({ message, type, onClose }: ToastProps) {
useEffect(() => {
const timer = setTimeout(() => {
onClose();
}, 3000); // Close the toast after 3 seconds
return () => clearTimeout(timer);
}, [onClose]);
const bgColor = type === 'success' ? 'bg-success' : 'bg-danger';
return (
<div
className={`position-fixed top-0 end-0 p-3`}
style={{ zIndex: 1050 }}
>
<div className={`toast show ${bgColor} text-white`} role="alert">
<div className="d-flex">
<div className="toast-body">
{message}
</div>
<button type="button" className="btn-close btn-close-white me-2 m-auto" onClick={onClose}></button>
</div>
</div>
</div>
);
}
➡️ NotesList.tsx එකට Toast එක Integrate කිරීම
දැන් අපි NotesList.tsx file එක update කර, note එකක් delete හෝ update කළ විට toast එකක් පෙන්වීමට සලස්වමු.
පළමුව, Toast component එක import කර, toast එක handle කිරීමට state එකක් සාදාගන්න.
// Inside components/NotesList.tsx
import { useState, useEffect } from 'react';
import Toast from './Toast'; // Import the Toast component
// ... inside the NotesList component function
const [toast, setToast] = useState<{ message: string; type: 'success' | 'error' } | null>(null);
// Update handleDelete function
const handleDelete = async (id: number) => {
if (confirm('Are you sure you want to delete this note?')) {
await fetch(`/api/notes/${id}`, { method: 'DELETE' });
setNotes(notes.filter(note => note.id !== id));
setToast({ message: 'Note deleted successfully', type: 'success' }); // Show toast
}
};
// Update handleUpdate function
const handleUpdate = async (e: React.FormEvent) => {
// ... (existing code)
if (res.ok) {
// ... (existing code)
setEditingNote(null);
setToast({ message: 'Note updated successfully', type: 'success' }); // Show toast
} else {
setToast({ message: 'Failed to update note', type: 'error' }); // Show error toast
}
};
// Inside the return statement, add the Toast component
return (
<>
{toast && <Toast message={toast.message} type={toast.type} onClose={() => setToast(null)} />}
{/* ... rest of the component (notes grid and modal) ... */}
</>
);
දැන් ඔබ note එකක් update හෝ delete කළ විට, තත්පර 3ක් සඳහා තිරයේ දකුණුපස ඉහළ කෙළවරේ සාර්ථක/අසාර්ථක බවට පණිවිඩයක් දිස්වී, ඉබේම மறைந்து යනු ඇත.