Skip to content

Commit cc6603f

Browse files
FEAT: add ability to remove referrals (#105)
1 parent 6dc8dfd commit cc6603f

6 files changed

Lines changed: 547 additions & 40 deletions

File tree

frontend/src/app/[locale]/generate-referrals/page.tsx

Lines changed: 83 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,11 @@ import {
2727
ClientDetailsInput,
2828
resourceCategories,
2929
} from "@/components/ClientDetailsInput";
30+
import { RemoveResourceNotification } from "@/components/RemoveResourceNotification";
3031

3132
export default function Page() {
3233
const [clientDescription, setClientDescription] = useState("");
34+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
3335
const [result, setResult] = useState<Resource[] | null>(null);
3436
const [resultId, setResultId] = useState("");
3537
const [loading, setLoading] = useState(false);
@@ -54,6 +56,13 @@ export default function Page() {
5456
const [userEmail, setUserEmail] = useState("");
5557
const [isCheckingUser, setIsCheckingUser] = useState(true);
5658

59+
// Remove functionality state
60+
const [retainedResources, setRetainedResources] = useState<Resource[]>();
61+
const [recentlyRemoved, setRecentlyRemoved] = useState<Resource | null>(null);
62+
const [removedResourceIndex, setRemovedResourceIndex] = useState<
63+
number | null
64+
>(null);
65+
5766
// Check if user has provided info on first load
5867
useEffect(() => {
5968
const storedUserName = localStorage.getItem("userName");
@@ -88,6 +97,7 @@ export default function Page() {
8897

8998
const onResources = (resources: Resource[]) => {
9099
setResult(resources);
100+
setRetainedResources(resources); // this creates a copy of the original list of resources, may be edited by the user
91101
setReadyToPrint(true);
92102
};
93103

@@ -145,11 +155,11 @@ export default function Page() {
145155
}
146156

147157
function handleSelectAllResources() {
148-
if (!result) return;
149-
if (selectedResources.length === result.length) {
158+
if (!retainedResources) return;
159+
if (selectedResources.length === retainedResources.length) {
150160
setSelectedResources([]);
151161
} else {
152-
setSelectedResources(result);
162+
setSelectedResources(retainedResources);
153163
}
154164
}
155165

@@ -211,6 +221,67 @@ export default function Page() {
211221
return null;
212222
}
213223

224+
// Remove resource handler
225+
const handleRemoveResource = (resourceToRemove: Resource) => {
226+
// Find and store the index before removing
227+
const index = retainedResources?.findIndex(
228+
(r) => r.name === resourceToRemove.name,
229+
);
230+
if (index !== undefined && index !== -1) {
231+
setRemovedResourceIndex(index);
232+
}
233+
234+
setRecentlyRemoved(resourceToRemove);
235+
236+
// Immediately remove from retainedResources
237+
setRetainedResources((current) =>
238+
current?.filter((r) => r.name !== resourceToRemove.name),
239+
);
240+
241+
// Remove from selectedResources as well
242+
setSelectedResources((current) =>
243+
current.filter((r) => r.name !== resourceToRemove.name),
244+
);
245+
246+
// Auto-clear the undo notification after 7.5 seconds
247+
setTimeout(() => {
248+
setRecentlyRemoved((current) => {
249+
// If the resource is still marked as recently removed, clear it
250+
if (current === resourceToRemove) {
251+
return null;
252+
}
253+
return current;
254+
});
255+
setRemovedResourceIndex(null);
256+
}, 7500);
257+
};
258+
259+
// Undo remove handler
260+
const handleUndoRemove = () => {
261+
if (recentlyRemoved) {
262+
// Add the resource back to retainedResources at its original index
263+
setRetainedResources((current) => {
264+
if (!current) {
265+
return [recentlyRemoved];
266+
}
267+
268+
// If we have a stored index, insert at that position
269+
if (removedResourceIndex !== null && removedResourceIndex >= 0) {
270+
const newArray = [...current];
271+
newArray.splice(removedResourceIndex, 0, recentlyRemoved);
272+
return newArray;
273+
}
274+
275+
// Fallback: append to the end
276+
return [...current, recentlyRemoved];
277+
});
278+
279+
// Clear the recently removed state
280+
setRecentlyRemoved(null);
281+
setRemovedResourceIndex(null);
282+
}
283+
};
284+
214285
return (
215286
<>
216287
{!userName || !userEmail ? (
@@ -306,12 +377,13 @@ export default function Page() {
306377
clientDescription={clientDescription}
307378
/>
308379
<ResourcesList
309-
resources={result ?? []}
380+
resources={retainedResources ?? []}
310381
errorMessage={errorMessage}
382+
handleRemoveResource={handleRemoveResource}
311383
/>
312-
{result && result.length > 0 && (
384+
{retainedResources && retainedResources.length > 0 && (
313385
<ActionPlanSection
314-
resources={result}
386+
resources={retainedResources}
315387
selectedResources={selectedResources}
316388
actionPlan={actionPlan}
317389
isGeneratingActionPlan={isGeneratingActionPlan}
@@ -326,10 +398,14 @@ export default function Page() {
326398
</div>
327399
)}
328400

401+
{recentlyRemoved && (
402+
<RemoveResourceNotification handleUndoRemove={handleUndoRemove} />
403+
)}
404+
329405
{/* ----- Print-only section ----- */}
330406
<div className="hidden print:block">
331407
<PrintableReferralsReport
332-
resources={result ?? []}
408+
resources={retainedResources ?? []}
333409
clientDescription={clientDescription}
334410
actionPlan={actionPlan}
335411
/>
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { RotateCcw } from "lucide-react";
2+
3+
type RemoveResourceNotificationProps = {
4+
handleUndoRemove: () => void;
5+
};
6+
export function RemoveResourceNotification({
7+
handleUndoRemove,
8+
}: RemoveResourceNotificationProps) {
9+
return (
10+
<div className="fixed bottom-6 left-1/2 transform -translate-x-1/2 z-50 animate-in slide-in-from-bottom-5 duration-300">
11+
<div className="bg-gray-900 text-white px-4 py-3 rounded-lg shadow-lg flex items-center gap-3">
12+
<div className="flex items-center gap-2">
13+
<div className="w-5 h-5 rounded-full bg-gray-700 flex items-center justify-center">
14+
<span className="text-xs">ℹ️</span>
15+
</div>
16+
<span className="text-sm font-medium">Resource removed</span>
17+
</div>
18+
<button
19+
onClick={handleUndoRemove}
20+
aria-label="Undo resource removal"
21+
className="flex items-center gap-1.5 px-3 py-1.5 bg-white text-gray-900 rounded-md hover:bg-gray-100 transition-colors text-sm font-medium"
22+
>
23+
<RotateCcw className="w-3.5 h-3.5" />
24+
Undo
25+
</button>
26+
</div>
27+
</div>
28+
);
29+
}

0 commit comments

Comments
 (0)