Compare commits
4 Commits
feat/relat
...
10a63fb9e0
| Author | SHA1 | Date | |
|---|---|---|---|
| 10a63fb9e0 | |||
| f94201c577 | |||
| 026457dac4 | |||
| 75493ce233 |
@@ -2052,8 +2052,18 @@ async def list_external_case_law(
|
|||||||
offset: int = 0,
|
offset: int = 0,
|
||||||
source_kind: str = "external_upload",
|
source_kind: str = "external_upload",
|
||||||
) -> list[dict]:
|
) -> list[dict]:
|
||||||
"""List chair-uploaded precedents, with simple filters."""
|
"""List chair-uploaded precedents, with simple filters.
|
||||||
|
|
||||||
|
source_kind="all_committees" expands to: source_kind='internal_committee'
|
||||||
|
OR (source_kind='external_upload' AND source_type='appeals_committee').
|
||||||
|
"""
|
||||||
pool = await get_pool()
|
pool = await get_pool()
|
||||||
|
if source_kind == "all_committees":
|
||||||
|
conditions = [
|
||||||
|
"(source_kind = 'internal_committee' OR "
|
||||||
|
"(source_kind = 'external_upload' AND source_type = 'appeals_committee'))"
|
||||||
|
]
|
||||||
|
else:
|
||||||
conditions = [f"source_kind = '{source_kind}'"]
|
conditions = [f"source_kind = '{source_kind}'"]
|
||||||
params: list = []
|
params: list = []
|
||||||
idx = 1
|
idx = 1
|
||||||
@@ -2488,19 +2498,17 @@ async def precedent_library_stats() -> dict:
|
|||||||
pool = await get_pool()
|
pool = await get_pool()
|
||||||
async with pool.acquire() as conn:
|
async with pool.acquire() as conn:
|
||||||
total = await conn.fetchval(
|
total = await conn.fetchval(
|
||||||
"SELECT COUNT(*) FROM case_law WHERE source_kind = 'external_upload'"
|
"SELECT COUNT(*) FROM case_law"
|
||||||
)
|
)
|
||||||
by_practice = await conn.fetch(
|
by_practice = await conn.fetch(
|
||||||
"""SELECT practice_area, COUNT(*) AS n
|
"""SELECT practice_area, COUNT(*) AS n
|
||||||
FROM case_law
|
FROM case_law
|
||||||
WHERE source_kind = 'external_upload'
|
|
||||||
GROUP BY practice_area
|
GROUP BY practice_area
|
||||||
ORDER BY n DESC"""
|
ORDER BY n DESC"""
|
||||||
)
|
)
|
||||||
by_level = await conn.fetch(
|
by_level = await conn.fetch(
|
||||||
"""SELECT precedent_level, COUNT(*) AS n
|
"""SELECT precedent_level, COUNT(*) AS n
|
||||||
FROM case_law
|
FROM case_law
|
||||||
WHERE source_kind = 'external_upload'
|
|
||||||
GROUP BY precedent_level
|
GROUP BY precedent_level
|
||||||
ORDER BY n DESC"""
|
ORDER BY n DESC"""
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
import Link from "next/link";
|
||||||
import { Trash2, Plus, Pencil, Wand2 } from "lucide-react";
|
import { Trash2, Plus, Pencil, Wand2 } from "lucide-react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import {
|
import {
|
||||||
@@ -151,7 +152,9 @@ function CourtRow({ p, onEdit }: { p: Precedent; onEdit: (id: string) => void })
|
|||||||
className="font-semibold text-navy text-right whitespace-normal break-words min-w-[280px] max-w-[420px] py-3"
|
className="font-semibold text-navy text-right whitespace-normal break-words min-w-[280px] max-w-[420px] py-3"
|
||||||
dir="rtl"
|
dir="rtl"
|
||||||
>
|
>
|
||||||
<span dir="auto">{cleanCitation(p.case_number)}</span>
|
<Link href={`/precedents/${p.id}`} className="hover:underline hover:text-gold-deep" dir="auto">
|
||||||
|
{cleanCitation(p.case_number)}
|
||||||
|
</Link>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className="text-ink whitespace-normal break-words max-w-[260px] py-3">
|
<TableCell className="text-ink whitespace-normal break-words max-w-[260px] py-3">
|
||||||
<div className="font-medium">{cleanCitation(p.case_name)}</div>
|
<div className="font-medium">{cleanCitation(p.case_name)}</div>
|
||||||
@@ -233,7 +236,9 @@ function CommitteeRow({ p, onEdit }: { p: Precedent; onEdit: (id: string) => voi
|
|||||||
className="font-semibold text-navy text-right whitespace-normal break-words min-w-[200px] max-w-[320px] py-3"
|
className="font-semibold text-navy text-right whitespace-normal break-words min-w-[200px] max-w-[320px] py-3"
|
||||||
dir="rtl"
|
dir="rtl"
|
||||||
>
|
>
|
||||||
<span dir="auto">{cleanCitation(p.case_number)}</span>
|
<Link href={`/precedents/${p.id}`} className="hover:underline hover:text-gold-deep" dir="auto">
|
||||||
|
{cleanCitation(p.case_number)}
|
||||||
|
</Link>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className="text-ink whitespace-normal break-words max-w-[220px] py-3">
|
<TableCell className="text-ink whitespace-normal break-words max-w-[220px] py-3">
|
||||||
<div className="font-medium">{cleanCitation(p.case_name)}</div>
|
<div className="font-medium">{cleanCitation(p.case_name)}</div>
|
||||||
@@ -308,8 +313,8 @@ export function LibraryListPanel() {
|
|||||||
limit: 200,
|
limit: 200,
|
||||||
};
|
};
|
||||||
|
|
||||||
const courts = usePrecedents({ ...sharedFilters, sourceKind: "external_upload" });
|
const courts = usePrecedents({ ...sharedFilters, sourceKind: "external_upload", sourceType: "court_ruling" });
|
||||||
const committee = usePrecedents({ ...sharedFilters, sourceKind: "internal_committee" });
|
const committee = usePrecedents({ ...sharedFilters, sourceKind: "all_committees" });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-8">
|
<div className="space-y-8">
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useEffect, useState } from "react";
|
import { useState } from "react";
|
||||||
import { Save, Sparkles } from "lucide-react";
|
import { Save, Sparkles } from "lucide-react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import {
|
import {
|
||||||
@@ -65,10 +65,12 @@ export function PrecedentEditSheet({ caseLawId, onOpenChange }: Props) {
|
|||||||
|
|
||||||
const [form, setForm] = useState<FormState>(EMPTY);
|
const [form, setForm] = useState<FormState>(EMPTY);
|
||||||
|
|
||||||
// Hydrate form when the record loads.
|
// React-approved derived-state pattern: sync form whenever a different
|
||||||
useEffect(() => {
|
// record arrives (including after save+refetch). Using setState during
|
||||||
if (!record) return;
|
// render avoids the one-frame flash that useEffect would produce.
|
||||||
// eslint-disable-next-line react-hooks/set-state-in-effect
|
const [syncedRecordId, setSyncedRecordId] = useState<string | null>(null);
|
||||||
|
if (record && record.id !== syncedRecordId) {
|
||||||
|
setSyncedRecordId(record.id as string);
|
||||||
setForm({
|
setForm({
|
||||||
citation: record.case_number || "",
|
citation: record.case_number || "",
|
||||||
case_name: record.case_name || "",
|
case_name: record.case_name || "",
|
||||||
@@ -84,7 +86,7 @@ export function PrecedentEditSheet({ caseLawId, onOpenChange }: Props) {
|
|||||||
headnote: record.headnote || "",
|
headnote: record.headnote || "",
|
||||||
key_quote: (record as { key_quote?: string }).key_quote || "",
|
key_quote: (record as { key_quote?: string }).key_quote || "",
|
||||||
});
|
});
|
||||||
}, [record]);
|
}
|
||||||
|
|
||||||
const onSubmit = async (e: React.FormEvent) => {
|
const onSubmit = async (e: React.FormEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|||||||
Reference in New Issue
Block a user