#PropertyTitle
September 18, 2026 at 1:00 PM
This article will implete pagination on the web Project
import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react"; export interface Property { id: number; propertyTitle: string; agencyId: number; propertyTypeId: number; propertyTypeName: string; description: string; amenities: string; monthlyRent: number; location: string; unitsAvailable: number; applicationDeadline: string; status: string; createdBy: string; createdDate: string; isDeleted: boolean; deletedDate: string; deletedBy: string; updatedDate: string; updatedBy: string; } export interface PropertyResponse { data: Property[]; message: string; totalCount: number; pageNumber: number; pageSize: number; } export interface GetAllPropertyParams { pageSize: number; pageNumber: number; searchTerm?: string; statusFilter?: string; } export interface DeleteResponse { response: string; } export const propertyApi = createApi({ reducerPath: "propertyApi", baseQuery: fetchBaseQuery({ baseUrl: "https://localhost:7083/api/", prepareHeaders: (headers) => { const token = localStorage.getItem("token"); if (token) { headers.set("Authorization", `Bearer ${token}`); } headers.set("Content-Type", "application/json"); return headers; }, }), tagTypes: ["Property"], endpoints: (builder) => ({ // ========================================== // GET ALL PROPERTY // ========================================== getAllProperty: builder.query< PropertyResponse, GetAllPropertyParams >({ query: ({ pageSize, pageNumber, searchTerm = "", statusFilter = "", }) => ({ url: "Property/GetAllProperty", method: "GET", params: { PageSize: pageSize, PageNumber: pageNumber, SearchTerm: searchTerm, StatusFilter: statusFilter, }, }), providesTags: ["Property"], }), // ========================================== // DELETE PROPERTY // ========================================== deleteProperty: builder.mutation< DeleteResponse, number >({ query: (propertyId) => ({ url: "Property/DeleteProperty", method: "DELETE", body: { propertyId, }, }), invalidatesTags: ["Property"], }), }), }); export const { useGetAllPropertyQuery, useDeletePropertyMutation, } = propertyApi; ## Table import { useEffect, useState } from "react"; import { useNavigate } from "react-router-dom"; import { FaEdit, FaTrash, FaSearch, FaExclamationTriangle, FaTimes, } from "react-icons/fa"; import { useGetAllPropertyQuery, useDeletePropertyMutation, } from "../services/propertyApi"; const PAGE_SIZE = 10; const PropertyTable = () => { const navigate = useNavigate(); // ========================================== // PAGINATION // ========================================== const [pageNumber, setPageNumber] = useState(1); // ========================================== // SEARCH // ========================================== const [searchInput, setSearchInput] = useState(""); const [searchTerm, setSearchTerm] = useState(""); // ========================================== // STATUS FILTER // ========================================== const [statusFilter, setStatusFilter] = useState(""); // ========================================== // DELETE MODAL // ========================================== const [deletePropertyId, setDeletePropertyId] = useState<number | null>(null); // ========================================== // ROLE // ========================================== const role = localStorage.getItem("role"); const isTenant = role?.toLowerCase() === "tenant"; // ========================================== // GET PROPERTIES // ========================================== const { data, isLoading, isFetching, isError, } = useGetAllPropertyQuery({ pageSize: PAGE_SIZE, pageNumber, searchTerm, statusFilter, }); // ========================================== // DELETE MUTATION // ========================================== const [ deleteProperty, { isLoading: isDeleting, }, ] = useDeletePropertyMutation(); // ========================================== // DEBOUNCE SEARCH // ========================================== useEffect(() => { const timer = setTimeout(() => { setPageNumber(1); setSearchTerm(searchInput); }, 500); return () => clearTimeout(timer); }, [searchInput]); // ========================================== // DATA // ========================================== const properties = data?.data ?? []; const totalCount = data?.totalCount ?? 0; const totalPages = Math.ceil( totalCount / PAGE_SIZE ); // ========================================== // APPLY // ========================================== const handleApply = ( propertyId: number ) => { navigate( `/application-form/${propertyId}` ); }; // ========================================== // EDIT // ========================================== const handleEdit = ( propertyId: number ) => { console.log( "Edit Property ID:", propertyId ); // Future: // navigate(`/property/edit/${propertyId}`); }; // ========================================== // OPEN DELETE MODAL // ========================================== const handleDeleteClick = ( propertyId: number ) => { setDeletePropertyId(propertyId); }; // ========================================== // CLOSE DELETE MODAL // ========================================== const handleCancelDelete = () => { if (isDeleting) return; setDeletePropertyId(null); }; // ========================================== // CONFIRM DELETE // ========================================== const handleConfirmDelete = async () => { if (deletePropertyId === null) { return; } try { const result = await deleteProperty( deletePropertyId ).unwrap(); console.log( result.response ); // Close modal setDeletePropertyId(null); /* * Because deleteProperty has: * * invalidatesTags: ["Property"] * * RTK Query automatically refetches * the property list. */ } catch (error) { console.error( "Failed to delete property:", error ); } }; // ========================================== // STATUS STYLE // ========================================== const getStatusStyle = ( status: string ) => { switch ( status.toLowerCase() ) { case "available": return "bg-green-100 text-green-700"; case "rented": return "bg-red-100 text-red-700"; case "onhold": return "bg-yellow-100 text-yellow-700"; default: return "bg-gray-200 text-gray-700"; } }; // ========================================== // LOADING // ========================================== if (isLoading) { return ( <div className="flex justify-center py-10"> <p className="text-gray-700"> Loading properties... </p> </div> ); } // ========================================== // ERROR // ========================================== if (isError) { return ( <div className="py-10 text-center text-red-600"> Failed to load properties. </div> ); } return ( <> {/* ======================================== PROPERTY TABLE ======================================== */} <div className="w-full rounded-xl border border-gray-300 bg-white shadow-sm"> {/* HEADER */} <div className="flex flex-col gap-4 border-b border-gray-300 px-6 py-4 md:flex-row md:items-center md:justify-between" > <h2 className="text-xl font-semibold text-gray-900"> Properties </h2> {/* SEARCH */} <div className="relative w-full md:w-80"> <FaSearch className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-500" size={14} /> <input type="text" value={searchInput} onChange={(e) => setSearchInput( e.target.value ) } placeholder="Search properties..." className="w-full rounded-lg border border-gray-400 bg-white py-2 pl-9 pr-3 text-gray-900 outline-none placeholder:text-gray-500 focus:border-blue-500 focus:ring-2 focus:ring-blue-200" /> </div> {/* STATUS FILTER */} <select value={statusFilter} onChange={(e) => { setStatusFilter( e.target.value ); setPageNumber(1); }} className="rounded-lg border border-gray-400 bg-white px-3 py-2 text-gray-800 outline-none focus:border-blue-500 focus:ring-2 focus:ring-blue-200" > <option value=""> All Status </option> <option value="Available"> Available </option> <option value="Rented"> Rented </option> <option value="OnHold"> OnHold </option> </select> </div> {/* TABLE */} <div className="overflow-x-auto"> <table className="w-full text-left text-sm"> {/* TABLE HEADER */} <thead className="bg-gray-300 text-gray-900"> <tr> <th className="whitespace-nowrap px-6 py-4 font-semibold"> Property Name </th> <th className="whitespace-nowrap px-6 py-4 font-semibold"> Monthly Rent </th> <th className="whitespace-nowrap px-6 py-4 font-semibold"> Location </th> <th className="whitespace-nowrap px-6 py-4 font-semibold"> Units Available </th> <th className="whitespace-nowrap px-6 py-4 font-semibold"> Application Deadline </th> <th className="whitespace-nowrap px-6 py-4 font-semibold"> Status </th> {isTenant && ( <th className="px-6 py-4 text-center font-semibold"> Apply </th> )} {!isTenant && ( <th className="px-6 py-4 text-center font-semibold"> Action </th> )} </tr> </thead> {/* TABLE BODY */} <tbody className="divide-y divide-gray-300"> {properties.length === 0 ? ( <tr> <td colSpan={7} className="px-6 py-12 text-center" > <p className="text-gray-700"> No properties found. </p> </td> </tr> ) : ( properties.map( (property) => ( <tr key={property.id} className="transition hover:bg-gray-100" > {/* PROPERTY NAME */} <td className="px-6 py-4 font-medium text-gray-900"> {property.propertyTitle} </td> {/* RENT */} <td className="px-6 py-4 font-medium text-gray-800"> ₹ {property.monthlyRent.toLocaleString( "en-IN" )} </td> {/* LOCATION */} <td className="px-6 py-4 text-gray-700"> {property.location} </td> {/* UNITS */} <td className="px-6 py-4 text-gray-700"> {property.unitsAvailable} </td> {/* DEADLINE */} <td className="px-6 py-4 text-gray-700"> {new Date( property.applicationDeadline ).toLocaleDateString( "en-IN" )} </td> {/* STATUS */} <td className="px-6 py-4"> <span className={`inline-flex rounded-full px-3 py-1 text-xs font-semibold ${getStatusStyle( property.status )}`} > {property.status} </span> </td> {/* TENANT */} {isTenant && ( <td className="px-6 py-4 text-center"> <button type="button" onClick={() => handleApply( property.id ) } className="rounded-lg bg-blue-600 px-4 py-2 font-medium text-white transition hover:bg-blue-700" > Apply </button> </td> )} {/* ADMIN / MANAGER */} {!isTenant && ( <td className="px-6 py-4"> <div className="flex items-center justify-center gap-4" > {/* EDIT */} <button type="button" title="Edit Property" onClick={() => handleEdit( property.id ) } className="text-blue-600 transition hover:text-blue-800" > <FaEdit size={18} /> </button> {/* DELETE */} <button type="button" title="Delete Property" onClick={() => handleDeleteClick( property.id ) } className="text-red-600 transition hover:text-red-800" > <FaTrash size={18} /> </button> </div> </td> )} </tr> ) ) )} </tbody> </table> </div> {/* PAGINATION */} <div className="flex flex-col gap-3 border-t border-gray-300 px-6 py-4 sm:flex-row sm:items-center sm:justify-between" > <p className="text-sm text-gray-600"> Page{" "} <span className="font-semibold text-gray-900"> {pageNumber} </span>{" "} of{" "} <span className="font-semibold text-gray-900"> {totalPages || 1} </span> </p> <div className="flex gap-2"> <button type="button" disabled={ pageNumber === 1 || isFetching } onClick={() => setPageNumber( (prev) => prev - 1 ) } className="rounded-lg border border-gray-400 bg-white px-4 py-2 text-sm font-medium text-gray-700 transition hover:bg-gray-100 disabled:cursor-not-allowed disabled:opacity-50" > Previous </button> <button type="button" disabled={ pageNumber >= totalPages || isFetching } onClick={() => setPageNumber( (prev) => prev + 1 ) } className="rounded-lg border border-gray-400 bg-white px-4 py-2 text-sm font-medium text-gray-700 transition hover:bg-gray-100 disabled:cursor-not-allowed disabled:opacity-50" > Next </button> </div> </div> </div> {/* ======================================== DELETE CONFIRMATION MODAL ======================================== */} {deletePropertyId !== null && ( <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 px-4" > <div className="w-full max-w-md rounded-xl bg-white p-6 shadow-2xl" > {/* MODAL HEADER */} <div className="flex items-start justify-between"> <div className="flex items-center gap-3"> <div className="flex h-10 w-10 items-center justify-center rounded-full bg-red-100" > <FaExclamationTriangle className="text-red-600" size={18} /> </div> <h3 className="text-lg font-semibold text-gray-900"> Delete Property </h3> </div> {/* CLOSE */} <button type="button" disabled={isDeleting} onClick={ handleCancelDelete } className="text-gray-500 transition hover:text-gray-800 disabled:opacity-50" > <FaTimes size={18} /> </button> </div> {/* MESSAGE */} <div className="mt-5"> <p className="text-sm leading-6 text-gray-600"> Are you sure you want to delete this property? </p> <p className="mt-2 text-sm font-medium text-red-600"> This action cannot be undone. </p> </div> {/* BUTTONS */} <div className="mt-6 flex justify-end gap-3" > {/* CANCEL */} <button type="button" disabled={isDeleting} onClick={ handleCancelDelete } className="rounded-lg border border-gray-400 bg-white px-4 py-2 text-sm font-medium text-gray-700 transition hover:bg-gray-100 disabled:cursor-not-allowed disabled:opacity-50" > Cancel </button> {/* CONFIRM DELETE */} <button type="button" disabled={isDeleting} onClick={ handleConfirmDelete } className="rounded-lg bg-red-600 px-4 py-2 text-sm font-medium text-white transition hover:bg-red-700 disabled:cursor-not-allowed disabled:opacity-50" > {isDeleting ? "Deleting..." : "Delete"} </button> </div> </div> </div> )} </> ); }; export default PropertyTable;```
dev.to
August 2, 2026 at 4:39 PM
o your property records? 🏡

#HomeSecurity #FloridaHomeowners #TitleFraud #PropertyProtection #HomeSafety #PropertyTitle #HomeownerTips #ProtectYourHome #PublicRecords #PeaceOfMind #RealEstateTips #TitleMonitoring #HomeAwareness #FraudPrevention #PropertyWatch #SecureHome #Homeownership #LegalProtec
August 18, 2025 at 3:30 PM
proactive with your home’s security? Share your strategies! 👇

#HomeOwnership #FloridaHomes #ProtectYourLegacy #HomeSecurity #PropertyTitle #HomeEquity #FamilyLegacy #TitleProtection #RealEstateTips #HomeSafety #FloridaRealEstate #SecureYourHome #FraudAwareness #Homeowners #StayProtected #Property
August 15, 2025 at 2:40 PM
We helped by updating the owner’s Land Registry details and providing independent evidence that the adverse possession requirements hadn’t been met.

#PropertyUK #HMLandRegistry #Conveyancing #PropertyLaw #AdversePossession #LandOwnership #PropertyTitle #Manchester #UKProperty
March 16, 2026 at 6:36 PM
When the owner later found a legitimate buyer, the buyer visited… and found the land fenced off by someone else.

#PropertyUK #HMLandRegistry #Conveyancing #PropertyLaw #AdversePossession #LandOwnership #PropertyTitle #Manchester #UKProperty #LandSale
March 16, 2026 at 6:35 PM
A developer wanted the plot, couldn’t reach him, and attempted adverse possession (claiming ownership by occupation + applying to HM Land Registry).

#PropertyUK #HMLandRegistry #Conveyancing #PropertyLaw #AdversePossession #PropertyTitle #Manchester #UKProperty
March 16, 2026 at 6:34 PM
The owner used the land address as his contact address in 2005. Years passed. He was in and out of hospital. The land sat quiet.

#PropertyUK #HMLandRegistry #Conveyancing #PropertyLaw #AdversePossession #LandOwnership #PropertyTitle #Manchester #UKProperty
March 16, 2026 at 6:34 PM
A true Manchester land story: an auction plot, a wrong address on the title, and a developer who tried adverse possession.

#PropertyUK #HMLandRegistry #Conveyancing #PropertyLaw #AdversePossession #LandOwnership #PropertyTitle #Manchester #UKProperty #LandSale
March 16, 2026 at 6:33 PM