-
📋
-
-
- {selected ? 'Edit Record' : 'New Record'}
-
-
- {selected ? 'Update FollowUp' : 'Add FollowUp'}
-
-
+
+
e.stopPropagation()}>
+
+ {/* HEADER */}
+
+
+
+
+
+
+
+
📋
+
+
+ {selected ? 'Edit Record' : 'New Record'}
+
+
+ {selected ? 'Update FollowUp' : 'Add FollowUp'}
+
-
-
- {/* Body — scrollable on small screens */}
-
-
- {/* Error */}
- {error && (
-
- ⚠️
- {error}
-
- )}
-
-
- {/* Remarks */}
-
-
-
-
-
+ {/* BODY */}
+
+
-
+
+
- >
+
)
-}
-
-// ── Numbered field label helper ──────────────────────────────
-const FuField = ({
- num, label, required, optional, children,
-}: {
- num: number
- label: string
- required?: boolean
- optional?: boolean
- children: React.ReactNode
-}) => (
-
-
- {children}
-
-)
-
-export default FollowUpForm
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/src/features/followup/components/Fufield.tsx b/src/features/followup/components/Fufield.tsx
new file mode 100644
index 0000000..429596b
--- /dev/null
+++ b/src/features/followup/components/Fufield.tsx
@@ -0,0 +1,23 @@
+import type { ReactNode } from 'react'
+
+interface FuFieldProps {
+ num: number
+ label: string
+ required?: boolean
+ optional?: boolean
+ children: ReactNode
+}
+
+export default function FuField({ num, label, required, optional, children }: FuFieldProps) {
+ return (
+
+
+ {children}
+
+ )
+}
\ No newline at end of file
diff --git a/src/features/followup/components/utilies/followup.helpers.ts b/src/features/followup/components/utilies/followup.helpers.ts
index b6445b9..8b67104 100644
--- a/src/features/followup/components/utilies/followup.helpers.ts
+++ b/src/features/followup/components/utilies/followup.helpers.ts
@@ -1,35 +1,35 @@
-export const PAGE_SIZE_OPTIONS = [5, 10, 25, 50];
+// export const PAGE_SIZE_OPTIONS = [5, 10, 25, 50];
-export const fmtDate = (iso?: string | null): string => {
- return iso
- ? new Date(iso).toLocaleDateString('en-PK', {
- day: '2-digit',
- month: 'short',
- year: 'numeric',
- })
- : '—';
-};
+// export const fmtDate = (iso?: string | null): string => {
+// return iso
+// ? new Date(iso).toLocaleDateString('en-PK', {
+// day: '2-digit',
+// month: 'short',
+// year: 'numeric',
+// })
+// : '—';
+// };
-export interface StatusStyle {
- bg: string;
- color: string;
- border: string;
- text: string;
-}
+// export interface StatusStyle {
+// bg: string;
+// color: string;
+// border: string;
+// text: string;
+// }
-export const getStatus = (hearingDate: string, nextHearingDate?: string | null): StatusStyle => {
- const today = new Date();
- const hearing = new Date(hearingDate);
- if (hearing < today) return { bg: '#FEF2F2', color: '#DC2626', border: '#FECACA', text: 'Past' };
- if (nextHearingDate) return { bg: '#FEF3C7', color: '#D97706', border: '#FDE68A', text: 'Adjourned' };
- return { bg: '#F0FDF4', color: '#15803D', border: '#BBF7D0', text: 'Scheduled' };
-};
+// export const getStatus = (hearingDate: string, nextHearingDate?: string | null): StatusStyle => {
+// const today = new Date();
+// const hearing = new Date(hearingDate);
+// if (hearing < today) return { bg: '#FEF2F2', color: '#DC2626', border: '#FECACA', text: 'Past' };
+// if (nextHearingDate) return { bg: '#FEF3C7', color: '#D97706', border: '#FDE68A', text: 'Adjourned' };
+// return { bg: '#F0FDF4', color: '#15803D', border: '#BBF7D0', text: 'Scheduled' };
+// };
-export const getPageNumbers = (current: number, total: number): (number | string)[] => {
- if (total <= 5) return Array.from({ length: total }, (_, i) => i + 1);
- const pages: (number | string)[] = [];
- if (current <= 3) pages.push(1, 2, 3, 4, '...', total);
- else if (current >= total - 2) pages.push(1, '...', total - 3, total - 2, total - 1, total);
- else pages.push(1, '...', current - 1, current, current + 1, '...', total);
- return pages;
-};
\ No newline at end of file
+// export const getPageNumbers = (current: number, total: number): (number | string)[] => {
+// if (total <= 5) return Array.from({ length: total }, (_, i) => i + 1);
+// const pages: (number | string)[] = [];
+// if (current <= 3) pages.push(1, 2, 3, 4, '...', total);
+// else if (current >= total - 2) pages.push(1, '...', total - 3, total - 2, total - 1, total);
+// else pages.push(1, '...', current - 1, current, current + 1, '...', total);
+// return pages;
+// };
\ No newline at end of file
diff --git a/src/features/followup/hooks/useFollowups.ts b/src/features/followup/hooks/useFollowups.ts
index 2237577..4f4416e 100644
--- a/src/features/followup/hooks/useFollowups.ts
+++ b/src/features/followup/hooks/useFollowups.ts
@@ -1,8 +1,7 @@
-// hooks/useFollowups.ts
-
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import { followupApi } from '../api/followupApi'
import type { CreateFollowUpDto, UpdateFollowUpDto, FollowUpPageParams } from '../types/followup.types'
+import { toastService } from '../../../lib/toast.service'
export const followupKeys = {
all: ['followups'] as const,
@@ -30,9 +29,13 @@ export const useCreateFollowUp = () => {
const qc = useQueryClient()
return useMutation({
mutationFn: (data: CreateFollowUpDto) => followupApi.create(data),
- onSuccess: () => {
+ onSuccess: (response) => {
+ toastService.success(response.message);
qc.invalidateQueries({ queryKey: followupKeys.all })
},
+ onError: (error: unknown) => {
+ toastService.error(error);
+ }
})
}
@@ -50,8 +53,12 @@ export const useDeleteFollowUp = () => {
const qc = useQueryClient()
return useMutation({
mutationFn: (id: string) => followupApi.delete(id),
- onSuccess: () => {
+ onSuccess: (response) => {
+ toastService.success(response.message);
qc.invalidateQueries({ queryKey: followupKeys.all })
},
- })
+ onError: (error: unknown) => {
+ toastService.error(error);
+ }
+ });
}
\ No newline at end of file
diff --git a/src/features/followup/styles/followUps-form.css b/src/features/followup/styles/followUps-form.css
index 1895281..0a6f342 100644
--- a/src/features/followup/styles/followUps-form.css
+++ b/src/features/followup/styles/followUps-form.css
@@ -155,4 +155,128 @@
margin-bottom: 14px;
color: #DC2626;
font-size: 12px;
+}
+/* ── Header ─────────────────────────────────────────────────── */
+.fu-header {
+ background: linear-gradient(135deg, #1B2A4A 0%, #243560 50%, #1B2A4A 100%);
+ padding: 20px 20px 0;
+ position: relative;
+ overflow: hidden;
+ flex-shrink: 0;
+}
+
+.fu-header-deco {
+ position: absolute;
+ border-radius: 50%;
+ border: 1px solid rgba(212, 168, 67, 0.15);
+}
+.fu-header-deco--lg { top: -15px; right: -15px; width: 80px; height: 80px; }
+.fu-header-deco--sm { top: 5px; right: 5px; width: 50px; height: 50px; border-color: rgba(212,168,67,0.10); }
+
+.fu-close-btn {
+ position: absolute;
+ top: 12px; right: 12px;
+ width: 28px; height: 28px;
+ border-radius: 6px;
+ background: rgba(255, 255, 255, 0.1);
+ border: 1px solid rgba(255, 255, 255, 0.15);
+ color: rgba(255, 255, 255, 0.7);
+ font-size: 16px;
+ cursor: pointer;
+ display: flex; align-items: center; justify-content: center;
+ transition: all 0.2s ease;
+ z-index: 1;
+}
+.fu-close-btn:hover { background: rgba(255,255,255,0.2); color: #fff; }
+
+.fu-header-content {
+ display: flex;
+ align-items: flex-end;
+ gap: 12px;
+ padding-bottom: 16px;
+}
+
+.fu-header-icon {
+ width: 48px; height: 48px;
+ border-radius: 12px;
+ background: linear-gradient(135deg, #D4A843 0%, #E8C05A 100%);
+ display: flex; align-items: center; justify-content: center;
+ font-size: 22px;
+ flex-shrink: 0;
+ box-shadow: 0 6px 16px rgba(212, 168, 67, 0.3);
+ animation: iconFloat 3s ease-in-out infinite;
+}
+
+.fu-header-badge {
+ margin: 0 0 2px;
+ font-size: 10px;
+ font-weight: 700;
+ letter-spacing: 0.1em;
+ color: #D4A843;
+ text-transform: uppercase;
+}
+
+.fu-header-title {
+ margin: 0;
+ font-size: 18px;
+ font-weight: 700;
+ color: #fff;
+}
+
+.fu-header-wave {
+ display: block;
+ margin-bottom: -1px;
+}
+
+/* ── Field ──────────────────────────────────────────────────── */
+.fu-field {
+ display: flex;
+ flex-direction: column;
+ gap: 6px;
+ margin-bottom: 14px;
+}
+
+.fu-label {
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ font-size: 11px;
+ font-weight: 700;
+ color: #4A5568;
+ letter-spacing: 0.05em;
+ text-transform: uppercase;
+}
+
+.fu-label-num {
+ width: 16px; height: 16px;
+ border-radius: 4px;
+ background: #1B2A4A;
+ color: #D4A843;
+ display: flex; align-items: center; justify-content: center;
+ font-size: 9px;
+ font-weight: 800;
+ flex-shrink: 0;
+}
+
+.fu-required { color: #DC2626; }
+
+.fu-optional {
+ font-size: 9px;
+ color: #A0ABBE;
+ background: #F1F5F9;
+ border-radius: 4px;
+ padding: 1px 5px;
+ font-weight: 600;
+ text-transform: none;
+ letter-spacing: 0;
+}
+
+/* ── Spinner ────────────────────────────────────────────────── */
+.fu-spinner {
+ display: inline-block;
+ width: 12px; height: 12px;
+ border-radius: 50%;
+ border: 2px solid rgba(27, 42, 74, 0.3);
+ border-top-color: #1B2A4A;
+ animation: spin 0.8s linear infinite;
}
\ No newline at end of file
diff --git a/src/features/followup/styles/followup-list.css b/src/features/followup/styles/followup-list.css
index d69360c..1bd244e 100644
--- a/src/features/followup/styles/followup-list.css
+++ b/src/features/followup/styles/followup-list.css
@@ -1,5 +1,5 @@
/* ── Animations ─────────────────────────────────────────── */
-@keyframes fadeInUp {
+/* @keyframes fadeInUp {
from { opacity: 0; transform: translateY(12px); }
to { opacity: 1; transform: translateY(0); }
}
@@ -11,10 +11,10 @@
@keyframes shimmer {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
-}
+} */
/* ── Row ─────────────────────────────────────────────────── */
-.fu-row {
+/* .fu-row {
animation: fadeInUp 0.3s ease forwards;
transition: background 0.15s;
}
@@ -24,9 +24,9 @@
.fu-row:hover .fu-act {
opacity: 1 !important;
transform: translateY(0) !important;
-}
-
-/* ── Action buttons ──────────────────────────────────────── */
+} */
+/*
+ ── Action buttons ────────────────────────────────────────
.fu-act {
opacity: 0;
transform: translateY(4px);
@@ -37,13 +37,13 @@
.fu-add:hover { background: #C49830 !important; transform: translateY(-1px); }
/* ── Search input ────────────────────────────────────────── */
-.fu-srch:focus {
+/* .fu-srch:focus {
border-color: #D4A843 !important;
box-shadow: 0 0 0 3px rgba(212, 168, 67, 0.1) !important;
-}
+} */
/* ── Pagination buttons ──────────────────────────────────── */
-.pg-btn:hover:not(:disabled) {
+/* .pg-btn:hover:not(:disabled) {
background: #1B2A4A !important;
color: #fff !important;
border-color: #1B2A4A !important;
@@ -62,35 +62,35 @@
color: #1B2A4A !important;
border-color: #D4A843 !important;
font-weight: 700 !important;
-}
+} */
/* ── Page size select ────────────────────────────────────── */
-.sz-sel:focus {
+/* .sz-sel:focus {
border-color: #D4A843 !important;
outline: none;
-}
+} */
/* ── Fetching top bar ────────────────────────────────────── */
-.fetching-bar {
+/* .fetching-bar {
position: absolute;
top: 0; left: 0; right: 0;
height: 3px;
background: linear-gradient(90deg, #D4A843, #E8C05A, #D4A843);
background-size: 200% 100%;
animation: shimmer 1.2s infinite;
-}
+} */
-/* ── Responsive: table vs cards ──────────────────────────── */
+/* ── Responsive: table vs cards ────────────────────────────
.fu-table-wrap { display: block; }
.fu-cards-wrap { display: none; }
@media (max-width: 768px) {
.fu-table-wrap { display: none; }
.fu-cards-wrap { display: flex; flex-direction: column; gap: 12px; padding: 16px; }
-}
+} */
/* ── Pagination footer ───────────────────────────────────── */
-.pg-footer {
+/* .pg-footer {
display: flex;
align-items: center;
justify-content: space-between;
@@ -100,10 +100,10 @@
}
@media (max-width: 600px) {
.pg-footer { justify-content: center; }
-}
+} */
/* ── Hide middle page numbers on very small screens ─────── */
-.pg-num-hide-sm { display: inline-flex; }
+/* .pg-num-hide-sm { display: inline-flex; }
@media (max-width: 400px) {
.pg-num-hide-sm { display: none; }
-}
\ No newline at end of file
+} */
\ No newline at end of file
diff --git a/src/lib/toast.service.ts b/src/lib/toast.service.ts
new file mode 100644
index 0000000..a91ad3e
--- /dev/null
+++ b/src/lib/toast.service.ts
@@ -0,0 +1,29 @@
+import toast from 'react-hot-toast';
+import axios from 'axios';
+
+const success = (message: string) =>
+ toast.success(message, {
+ style: {
+ background: '#22c55e',
+ color: '#fff',
+ fontWeight: '500',
+ },
+ iconTheme: { primary: '#fff', secondary: '#22c55e' },
+ });
+
+const error = (error: unknown) => {
+ const msg = axios.isAxiosError(error)
+ ? error.response?.data?.message ?? 'Something went wrong'
+ : 'Something went wrong';
+
+ toast.error(msg, {
+ style: {
+ background: '#ef4444',
+ color: '#fff',
+ fontWeight: '500',
+ },
+ iconTheme: { primary: '#fff', secondary: '#ef4444' },
+ });
+};
+
+export const toastService = { success, error };
\ No newline at end of file
diff --git a/src/router/index.tsx b/src/router/index.tsx
index 4e7b5c0..eb97459 100644
--- a/src/router/index.tsx
+++ b/src/router/index.tsx
@@ -15,7 +15,7 @@ import { ProfilePage } from '../features/UserProfile/components/ProfilePage'
import { SuperAdminDashboard } from '../features/superAdmin/components/SuperAdminDashboard'
import AlertsPage from '../features/alerts/components/AlertsPage'
import CourtsPage from '../features/courts/components/CourtsPage'
-import FollowupPage from '../features/followup/components/FollowUpsPage'
+// import FollowupPage from '../features/followup/components/FollowUpsPage'
import { SuperAdminProfilePage } from '../features/superAdmin/components/SuperAdminProfilePage'
export const router = createBrowserRouter([
@@ -48,8 +48,8 @@ export const router = createBrowserRouter([
{ path: 'documents/:caseId', element:
},
{ path: 'petitioners', element:
},
{ path: 'courts', element:
},
- { path: 'followup', element:
},
- { path: 'followup/:caseId', element:
},
+ // { path: 'followup', element:
},
+ // { path: 'followup/:caseId', element:
},
{ path: 'alerts', element:
},
{ path: 'profile', element:
},
{ path: 'Benches', element:
},
diff --git a/src/shared/components/Layout.tsx b/src/shared/components/Layout.tsx
index 1f15c77..7842279 100644
--- a/src/shared/components/Layout.tsx
+++ b/src/shared/components/Layout.tsx
@@ -9,16 +9,16 @@ const COLLAPSED_SIDEBAR_WIDTH = 84
const MOBILE_SIDEBAR_WIDTH = 76
const navItems = [
- { path: '/app/dashboard', label: 'Dashboard', icon: '⊞' },
- { path: '/app/cases', label: 'Cases', icon: '⚖' },
- { path: '/app/followup', label: 'Follow', icon: '📅' },
+ { path: '/app/dashboard', label: 'Dashboard', icon: '⊞' },
+ { path: '/app/cases', label: 'Cases', icon: '⚖' },
+ // { path: '/app/followup', label: 'Follow', icon: '📅' },
{ path: '/app/petitioners', label: 'Petitioners', icon: '👥' },
- { path: '/app/courts', label: 'Courts', icon: '🏛' },
+ { path: '/app/courts', label: 'Courts', icon: '🏛' },
{ path: '/app/departments', label: 'Departments', icon: '⊞' },
{ path: '/app/documents/3b7a40ff-13a6-45b0-b694-de99624d4f28', label: 'Documents', icon: '📄' },
- { path: '/app/alerts', label: 'Alerts', icon: '🔔' },
- { path: '/app/Benches', label: 'Benches', icon: '👨⚖️' },
- { path: '/app/reports', label: 'Reports', icon: '📊' },
+ { path: '/app/alerts', label: 'Alerts', icon: '🔔' },
+ { path: '/app/Benches', label: 'Benches', icon: '👨⚖️' },
+ { path: '/app/reports', label: 'Reports', icon: '📊' },
]
const Layout = () => {
@@ -39,8 +39,8 @@ const Layout = () => {
const nextViewport = mobileQuery.matches
? 'mobile'
: tabletQuery.matches
- ? 'tablet'
- : 'desktop'
+ ? 'tablet'
+ : 'desktop'
setViewport(nextViewport)
setIsSidebarCollapsed(nextViewport === 'mobile')
@@ -210,8 +210,8 @@ const Layout = () => {
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.45, duration: 0.35 }}
>
-
-
+
+
{
-
-
setDropdownOpen(v => !v)}
- whileHover={{ y: -1 }}
- transition={{ type: 'spring', stiffness: 260, damping: 22 }}
- style={{ cursor: 'pointer', userSelect: 'none' }}
->
- {/* Avatar */}
-
- {user?.profileImage ? (
-

{ e.currentTarget.style.display = 'none' }}
- />
- ) : (
- user?.name?.charAt(0).toUpperCase() ?? 'U'
- )}
-
-
-
- {user?.name ?? 'User'}
-
-
- ▾
-
-
-{/* Dropdown */}
-
- {dropdownOpen && (
-
- {/* Header */}
-
-
- {user?.profileImage ? (
-

{ e.currentTarget.style.display = 'none' }}
- />
- ) : (
- user?.name?.charAt(0).toUpperCase() ?? 'U'
- )}
-
-
-
- {user?.name ?? 'User'}
-
-
- {user?.email ?? ''}
-
-
- {user?.organizationName ?? ''}
-
-
-
-
- {/* Menu Items */}
-
- {[
- { icon: '👤', label: 'My Profile', path: '/app/profile', desc: 'View & edit profile' },
- { icon: '⚙️', label: 'Settings', path: '/app/settings', desc: 'App preferences' },
- { icon: '🔔', label: 'Notifications', path: '/app/alerts', desc: 'Manage alerts' },
- ].map(item => (
-
- ))}
-
-
- {/* Logout */}
-
-
-