mirror of
https://github.com/Hestia-Homes/assessment-model.git
synced 2026-06-30 12:55:02 +00:00
finsihed
This commit is contained in:
parent
8f78f64de6
commit
25b826c42c
3 changed files with 155 additions and 87 deletions
|
|
@ -281,18 +281,28 @@ export default function PortfolioSettings({
|
|||
setIsDeleteModalOpen(true);
|
||||
}
|
||||
|
||||
function handleDeleteConfirmation() {
|
||||
if (deleteConfirmationByName === portfolioSettingsData.name) {
|
||||
mutateDelete({
|
||||
async function handleDeleteConfirmation() {
|
||||
if (deleteConfirmationByName !== portfolioSettingsData.name) {
|
||||
console.warn("Delete confirmation name does not match");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
console.log("[DELETE] starting delete mutation");
|
||||
|
||||
await mutateDelete({
|
||||
userId,
|
||||
portfolioId,
|
||||
});
|
||||
console.log("succesfully called the mututate function");
|
||||
|
||||
console.log("[DELETE] mutation completed successfully");
|
||||
// Refresh table / page data
|
||||
router.refresh();
|
||||
} catch (err) {
|
||||
console.error("[DELETE] mutation failed", err);
|
||||
}
|
||||
}
|
||||
|
||||
// RENAMING FUNCTIONS
|
||||
|
||||
// Change NAME functionality - changing state
|
||||
|
||||
function handlePortfolioNameChange(e: React.ChangeEvent<HTMLInputElement>) {
|
||||
|
|
@ -460,7 +470,7 @@ export default function PortfolioSettings({
|
|||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
<UsersPermissionsCard portfolioId={portfolioId}/>
|
||||
<UsersPermissionsCard portfolioId={portfolioId} />
|
||||
<div className="rounded-md border border-red-500 mt-2">
|
||||
<Table>
|
||||
<TableHead className="text-lg text-brandblue">Danger Zone:</TableHead>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
"use client";
|
||||
|
||||
import { useState, useMemo, useEffect } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useProperties } from "./useProperties";
|
||||
import DataTable from "./dataTable";
|
||||
import PropertyFilters, { PropertyFilterValues } from "./PropertyFilters";
|
||||
|
|
@ -86,6 +87,8 @@ export default function PropertyTable({
|
|||
}: {
|
||||
portfolioId: string;
|
||||
}) {
|
||||
const router = useRouter();
|
||||
|
||||
const [filters, setFilters] = useState<PropertyFilterValues>({
|
||||
address: "",
|
||||
postcode: "",
|
||||
|
|
@ -101,6 +104,7 @@ export default function PropertyTable({
|
|||
isLoading,
|
||||
isFetching,
|
||||
isError,
|
||||
refetch,
|
||||
} = useProperties({
|
||||
portfolioId,
|
||||
filters: parsedFilters,
|
||||
|
|
@ -123,8 +127,6 @@ export default function PropertyTable({
|
|||
useEffect(() => {
|
||||
if (!deletePropertyId) return;
|
||||
|
||||
console.log("[PREVIEW] fetching delete preview for:", deletePropertyId);
|
||||
|
||||
setPreviewLoading(true);
|
||||
setPreviewError(null);
|
||||
setDeletePreview(null);
|
||||
|
|
@ -133,50 +135,37 @@ export default function PropertyTable({
|
|||
method: "POST",
|
||||
})
|
||||
.then(async (res) => {
|
||||
if (!res.ok) {
|
||||
throw new Error("Failed to fetch delete preview");
|
||||
}
|
||||
if (!res.ok) throw new Error("Failed to fetch delete preview");
|
||||
return res.json();
|
||||
})
|
||||
.then((data) => {
|
||||
console.log("[PREVIEW] result:", data.preview);
|
||||
setDeletePreview(data.preview);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error("[PREVIEW] error:", err);
|
||||
setPreviewError(err.message);
|
||||
})
|
||||
.finally(() => {
|
||||
setPreviewLoading(false);
|
||||
});
|
||||
.then((data) => setDeletePreview(data.preview))
|
||||
.catch((err) => setPreviewError(err.message))
|
||||
.finally(() => setPreviewLoading(false));
|
||||
}, [deletePropertyId]);
|
||||
|
||||
/* ----------------------------------------
|
||||
Confirm delete
|
||||
----------------------------------------- */
|
||||
const handleConfirmDelete = async () => {
|
||||
if (!deletePropertyId) return;
|
||||
|
||||
setDeleteLoading(true);
|
||||
|
||||
try {
|
||||
console.log("[CONFIRM DELETE] sending request…");
|
||||
await fetch(`/api/properties/${deletePropertyId}/delete/confirm`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ confirm: true }),
|
||||
});
|
||||
|
||||
const res = await fetch(
|
||||
`/api/properties/${deletePropertyId}/delete/confirm`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ confirm: true }),
|
||||
}
|
||||
);
|
||||
|
||||
const data = await res.json();
|
||||
|
||||
console.log("[CONFIRM DELETE] response:", data);
|
||||
|
||||
// TEMP: just close modal after backend confirms
|
||||
// Close modal
|
||||
setDeletePropertyId(null);
|
||||
setDeletePreview(null);
|
||||
|
||||
// ✅ THIS FIXES THE GHOST ROW
|
||||
await refetch();
|
||||
} catch (err) {
|
||||
console.error("[CONFIRM DELETE] error:", err);
|
||||
console.error("[DELETE] failed", err);
|
||||
} finally {
|
||||
setDeleteLoading(false);
|
||||
}
|
||||
|
|
@ -186,24 +175,20 @@ export default function PropertyTable({
|
|||
<div className="flex justify-center">
|
||||
<div className="grid grid-cols-11 w-full max-w-8xl">
|
||||
<div className="col-span-11 bg-white rounded-md border">
|
||||
{/* Filters */}
|
||||
<PropertyFilters onApply={setFilters} />
|
||||
|
||||
{/* Loading bar */}
|
||||
{isFetching && (
|
||||
<div className="h-1 w-full bg-gray-100 overflow-hidden">
|
||||
<div className="h-full w-1/3 bg-black animate-[loading_1.2s_infinite]" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Filter info */}
|
||||
{hasActiveFilters && !isFetching && (
|
||||
<div className="px-4 py-2 text-xs text-gray-500 border-b">
|
||||
Filters applied ({parsedFilters.length})
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Content */}
|
||||
{isLoading ? (
|
||||
<div className="p-6 text-gray-400">Loading properties…</div>
|
||||
) : isError ? (
|
||||
|
|
@ -231,10 +216,7 @@ export default function PropertyTable({
|
|||
<DataTable
|
||||
data={data}
|
||||
columns={columns}
|
||||
onDeleteProperty={(propertyId) => {
|
||||
console.log("[META] onDeleteProperty fired:", propertyId);
|
||||
setDeletePropertyId(propertyId);
|
||||
}}
|
||||
onDeleteProperty={(id) => setDeletePropertyId(id)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -263,7 +245,6 @@ export default function PropertyTable({
|
|||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
{/* Loading */}
|
||||
{previewLoading && (
|
||||
<div className="flex items-center gap-3 py-6">
|
||||
<div className="h-5 w-5 animate-spin rounded-full border-2 border-muted border-t-foreground" />
|
||||
|
|
@ -273,12 +254,10 @@ export default function PropertyTable({
|
|||
</div>
|
||||
)}
|
||||
|
||||
{/* Error */}
|
||||
{previewError && (
|
||||
<p className="text-sm text-red-600">{previewError}</p>
|
||||
)}
|
||||
|
||||
{/* Preview result */}
|
||||
{deletePreview && !previewLoading && (
|
||||
<ul className="space-y-1 text-sm">
|
||||
{deletePreview.map((row) => (
|
||||
|
|
@ -302,19 +281,13 @@ export default function PropertyTable({
|
|||
>
|
||||
Cancel
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
variant="destructive"
|
||||
disabled={!deletePreview || previewLoading || deleteLoading}
|
||||
onClick={handleConfirmDelete}
|
||||
>
|
||||
{deleteLoading ? (
|
||||
<span className="flex items-center gap-2">
|
||||
<span className="h-4 w-4 animate-spin rounded-full border-2 border-white border-t-transparent" />
|
||||
Deleting…
|
||||
</span>
|
||||
) : (
|
||||
"Confirm delete"
|
||||
)}
|
||||
{deleteLoading ? "Deleting…" : "Confirm delete"}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
|
|
|
|||
|
|
@ -5,17 +5,22 @@ export async function previewPropertyDeletion(propertyId: number) {
|
|||
const id = sql`${propertyId}`;
|
||||
|
||||
const result = await db.execute(sql`
|
||||
-- ---------------------------------
|
||||
-- Recommendation chain
|
||||
-- ---------------------------------
|
||||
SELECT 'recommendation_materials' AS table, COUNT(*)::int AS count
|
||||
FROM recommendation_materials rm
|
||||
JOIN recommendation r ON rm.recommendation_id = r.id
|
||||
WHERE r.property_id = ${id}
|
||||
|
||||
UNION ALL
|
||||
SELECT 'plan_recommendations', COUNT(*)
|
||||
FROM plan_recommendations pr
|
||||
JOIN plan p ON pr.plan_id = p.id
|
||||
WHERE p.property_id = ${id}
|
||||
SELECT 'recommendation', COUNT(*)
|
||||
FROM recommendation
|
||||
WHERE property_id = ${id}
|
||||
|
||||
-- ---------------------------------
|
||||
-- Funding / plan chain
|
||||
-- ---------------------------------
|
||||
UNION ALL
|
||||
SELECT 'funding_package_measures', COUNT(*)
|
||||
FROM funding_package_measures fpm
|
||||
|
|
@ -24,20 +29,53 @@ export async function previewPropertyDeletion(propertyId: number) {
|
|||
WHERE p.property_id = ${id}
|
||||
|
||||
UNION ALL
|
||||
SELECT 'inspections', COUNT(*)
|
||||
FROM inspections
|
||||
WHERE property_id = ${id}
|
||||
SELECT 'funding_package', COUNT(*)
|
||||
FROM funding_package fp
|
||||
JOIN plan p ON fp.plan_id = p.id
|
||||
WHERE p.property_id = ${id}
|
||||
|
||||
UNION ALL
|
||||
SELECT 'recommendation', COUNT(*)
|
||||
FROM recommendation
|
||||
WHERE property_id = ${id}
|
||||
SELECT 'plan_recommendations', COUNT(*)
|
||||
FROM plan_recommendations pr
|
||||
JOIN plan p ON pr.plan_id = p.id
|
||||
WHERE p.property_id = ${id}
|
||||
|
||||
UNION ALL
|
||||
SELECT 'plan', COUNT(*)
|
||||
FROM plan
|
||||
WHERE property_id = ${id}
|
||||
|
||||
-- ---------------------------------
|
||||
-- Property direct children
|
||||
-- ---------------------------------
|
||||
UNION ALL
|
||||
SELECT 'property_details_epc', COUNT(*)
|
||||
FROM property_details_epc
|
||||
WHERE property_id = ${id}
|
||||
|
||||
UNION ALL
|
||||
SELECT 'property_targets', COUNT(*)
|
||||
FROM property_targets
|
||||
WHERE property_id = ${id}
|
||||
|
||||
UNION ALL
|
||||
SELECT 'property_status_tracker', COUNT(*)
|
||||
FROM property_status_tracker
|
||||
WHERE property_id = ${id}
|
||||
|
||||
UNION ALL
|
||||
SELECT 'inspections', COUNT(*)
|
||||
FROM inspections
|
||||
WHERE property_id = ${id}
|
||||
|
||||
UNION ALL
|
||||
SELECT 'files_from_surveyor', COUNT(*)
|
||||
FROM files_from_surveyor
|
||||
WHERE property_id = ${id}
|
||||
|
||||
-- ---------------------------------
|
||||
-- Root
|
||||
-- ---------------------------------
|
||||
UNION ALL
|
||||
SELECT 'property', COUNT(*)
|
||||
FROM property
|
||||
|
|
@ -49,6 +87,47 @@ export async function previewPropertyDeletion(propertyId: number) {
|
|||
|
||||
export async function deleteProperty(propertyId: number) {
|
||||
await db.transaction(async (tx) => {
|
||||
// -----------------------------
|
||||
// LEAF TABLES FIRST
|
||||
// -----------------------------
|
||||
|
||||
await tx.execute(sql`
|
||||
DELETE FROM funding_package_measures fpm
|
||||
USING funding_package fp, plan p
|
||||
WHERE fpm.funding_package_id = fp.id
|
||||
AND fp.plan_id = p.id
|
||||
AND p.property_id = ${propertyId};
|
||||
`);
|
||||
|
||||
await tx.execute(sql`
|
||||
DELETE FROM recommendation_materials rm
|
||||
USING recommendation r
|
||||
WHERE rm.recommendation_id = r.id
|
||||
AND r.property_id = ${propertyId};
|
||||
`);
|
||||
|
||||
// -----------------------------
|
||||
// PLAN CHILDREN
|
||||
// -----------------------------
|
||||
|
||||
await tx.execute(sql`
|
||||
DELETE FROM funding_package fp
|
||||
USING plan p
|
||||
WHERE fp.plan_id = p.id
|
||||
AND p.property_id = ${propertyId};
|
||||
`);
|
||||
|
||||
await tx.execute(sql`
|
||||
DELETE FROM plan_recommendations pr
|
||||
USING plan p
|
||||
WHERE pr.plan_id = p.id
|
||||
AND p.property_id = ${propertyId};
|
||||
`);
|
||||
|
||||
// -----------------------------
|
||||
// PROPERTY DIRECT CHILDREN
|
||||
// -----------------------------
|
||||
|
||||
await tx.execute(sql`
|
||||
DELETE FROM property_details_epc
|
||||
WHERE property_id = ${propertyId};
|
||||
|
|
@ -60,25 +139,8 @@ export async function deleteProperty(propertyId: number) {
|
|||
`);
|
||||
|
||||
await tx.execute(sql`
|
||||
DELETE FROM recommendation_materials rm
|
||||
USING recommendation r
|
||||
WHERE rm.recommendation_id = r.id
|
||||
AND r.property_id = ${propertyId};
|
||||
`);
|
||||
|
||||
await tx.execute(sql`
|
||||
DELETE FROM plan_recommendations pr
|
||||
USING plan p
|
||||
WHERE pr.plan_id = p.id
|
||||
AND p.property_id = ${propertyId};
|
||||
`);
|
||||
|
||||
await tx.execute(sql`
|
||||
DELETE FROM funding_package_measures fpm
|
||||
USING funding_package fp, plan p
|
||||
WHERE fpm.funding_package_id = fp.id
|
||||
AND fp.plan_id = p.id
|
||||
AND p.property_id = ${propertyId};
|
||||
DELETE FROM property_status_tracker
|
||||
WHERE property_id = ${propertyId};
|
||||
`);
|
||||
|
||||
await tx.execute(sql`
|
||||
|
|
@ -86,6 +148,11 @@ export async function deleteProperty(propertyId: number) {
|
|||
WHERE property_id = ${propertyId};
|
||||
`);
|
||||
|
||||
await tx.execute(sql`
|
||||
DELETE FROM files_from_surveyor
|
||||
WHERE property_id = ${propertyId};
|
||||
`);
|
||||
|
||||
await tx.execute(sql`
|
||||
DELETE FROM recommendation
|
||||
WHERE property_id = ${propertyId};
|
||||
|
|
@ -96,9 +163,27 @@ export async function deleteProperty(propertyId: number) {
|
|||
WHERE property_id = ${propertyId};
|
||||
`);
|
||||
|
||||
// -----------------------------
|
||||
// PROPERTY LAST (ALWAYS)
|
||||
// -----------------------------
|
||||
|
||||
await tx.execute(sql`
|
||||
DELETE FROM property
|
||||
WHERE id = ${propertyId};
|
||||
`);
|
||||
});
|
||||
}
|
||||
|
||||
// Find All foregin keys used for 'property' table with id via this command
|
||||
// SELECT
|
||||
// tc.table_name,
|
||||
// kcu.column_name,
|
||||
// ccu.table_name AS foreign_table_name,
|
||||
// ccu.column_name AS foreign_column_name
|
||||
// FROM information_schema.table_constraints AS tc
|
||||
// JOIN information_schema.key_column_usage AS kcu
|
||||
// ON tc.constraint_name = kcu.constraint_name
|
||||
// JOIN information_schema.constraint_column_usage AS ccu
|
||||
// ON ccu.constraint_name = tc.constraint_name
|
||||
// WHERE tc.constraint_type = 'FOREIGN KEY'
|
||||
// AND ccu.table_name = 'property';
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue