Skip to content

Commit ea9ca30

Browse files
wharfeclaude
andcommitted
feat: add Copyright Act data and OpenGIKAI cross-link (#1, #2)
- Add 著作権法 (Copyright Act) diff data, timeline, AI annotations, proposer info, and law summary (closes #2) - Add OpenGIKAI cross-link component with static mapping file (partial progress on #1) - Show OpenGIKAI thread links in law page sidebar when available - Add 著作権・創作 life theme - Update sitemap Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 731b091 commit ea9ca30

7 files changed

Lines changed: 1936 additions & 5 deletions

File tree

frontend/app/law/[lawId]/page.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import type { Metadata } from "next";
22
import Link from "next/link";
3-
import { getTimelineIds, getTimelineData } from "@/lib/data";
3+
import { getTimelineIds, getTimelineData, getOpenGikaiLinks } from "@/lib/data";
44
import { Timeline } from "@/components/timeline";
55
import { Icon } from "@/components/icon";
6+
import { OpenGikaiLinks } from "@/components/opengikai-link";
67
import { getThemesForLaw } from "@/lib/life-themes";
78
import { BreadcrumbJsonLd } from "@/components/breadcrumb-jsonld";
89

@@ -36,6 +37,7 @@ export default async function LawPage({
3637
const { lawId } = await params;
3738
const data = getTimelineData(lawId);
3839
const themes = getThemesForLaw(lawId);
40+
const gikaiLinks = getOpenGikaiLinks(lawId);
3941

4042
return (
4143
<div className="flex flex-col gap-6">
@@ -158,9 +160,11 @@ export default async function LawPage({
158160
</div>
159161
</div>
160162

161-
{/* Sidebar — Contributors */}
162-
{data.contributors && data.contributors.length > 0 && (
163-
<div className="lg:w-[260px] shrink-0">
163+
{/* Sidebar */}
164+
{(data.contributors?.length || gikaiLinks) && (
165+
<div className="lg:w-[260px] shrink-0 space-y-4">
166+
{gikaiLinks && <OpenGikaiLinks mapping={gikaiLinks} />}
167+
{data.contributors && data.contributors.length > 0 && (
164168
<div className="border border-[var(--border)] rounded-lg overflow-hidden">
165169
<div className="px-4 py-3 bg-[var(--muted)] border-b border-[var(--border)] text-[14px] font-medium">
166170
Contributors
@@ -189,6 +193,7 @@ export default async function LawPage({
189193
</p>
190194
</div>
191195
</div>
196+
)}
192197
</div>
193198
)}
194199
</div>
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { Icon } from "./icon";
2+
import { OpenGikaiMapping } from "@/lib/data";
3+
4+
export function OpenGikaiLinks({
5+
mapping,
6+
}: {
7+
mapping: OpenGikaiMapping;
8+
}) {
9+
if (!mapping.threads.length) return null;
10+
11+
return (
12+
<div className="border border-[var(--border)] rounded-lg overflow-hidden">
13+
<div className="px-4 py-3 bg-[var(--muted)] border-b border-[var(--border)] flex items-center gap-2 text-[14px] font-medium">
14+
<Icon name="gavel" size={16} className="opacity-40" />
15+
国会での審議(OpenGIKAI)
16+
</div>
17+
<div className="px-4 py-3 space-y-2">
18+
{mapping.threads.map((thread) => (
19+
<a
20+
key={thread.thread_id}
21+
href={thread.url}
22+
target="_blank"
23+
rel="noopener noreferrer"
24+
className="flex items-start gap-3 rounded-lg p-2 -mx-2 hover:bg-[var(--muted)] transition-colors"
25+
>
26+
<div className="min-w-0 flex-1">
27+
<div className="text-[14px] font-medium text-[var(--diff-hunk-text)]">
28+
{thread.title}
29+
<Icon
30+
name="open_in_new"
31+
size={12}
32+
className="ml-1 opacity-50"
33+
/>
34+
</div>
35+
<div className="text-[12px] opacity-50 mt-0.5">
36+
{thread.date} · {thread.committee}
37+
</div>
38+
</div>
39+
</a>
40+
))}
41+
<p className="text-[11px] opacity-30 pt-1">
42+
OpenGIKAIで審議の詳細を確認できます
43+
</p>
44+
</div>
45+
</div>
46+
);
47+
}

frontend/lib/data.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,12 @@ const TIMELINE_DIR = path.join(DATA_DIR, "timelines");
88
export function getDiffIds(): string[] {
99
const files = fs.readdirSync(DATA_DIR);
1010
return files
11-
.filter((f) => f.endsWith(".json") && !f.includes("index"))
11+
.filter(
12+
(f) =>
13+
f.endsWith(".json") &&
14+
!f.includes("index") &&
15+
!f.includes("mapping")
16+
)
1217
.map((f) => f.replace(".json", ""));
1318
}
1419

@@ -31,3 +36,26 @@ export function getTimelineData(lawId: string): LawTimeline {
3136
const raw = fs.readFileSync(filePath, "utf-8");
3237
return JSON.parse(raw) as LawTimeline;
3338
}
39+
40+
export interface OpenGikaiThread {
41+
thread_id: string;
42+
title: string;
43+
url: string;
44+
date: string;
45+
committee: string;
46+
}
47+
48+
export interface OpenGikaiMapping {
49+
law_title: string;
50+
threads: OpenGikaiThread[];
51+
}
52+
53+
export function getOpenGikaiLinks(
54+
lawId: string
55+
): OpenGikaiMapping | null {
56+
const mappingPath = path.join(DATA_DIR, "opengikai-mapping.json");
57+
if (!fs.existsSync(mappingPath)) return null;
58+
const raw = fs.readFileSync(mappingPath, "utf-8");
59+
const mapping = JSON.parse(raw) as Record<string, OpenGikaiMapping>;
60+
return mapping[lawId] || null;
61+
}

frontend/lib/life-themes.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ const CIVIL_CODE = "129AC0000000089";
1212
const APPI = "415AC0000000057";
1313
const ROAD_TRAFFIC = "335AC0000000105";
1414
const LABOR_STANDARDS = "322AC0000000049";
15+
const COPYRIGHT = "345AC0000000048";
1516

1617
export const LIFE_THEMES: LifeTheme[] = [
1718
{
@@ -68,6 +69,15 @@ export const LIFE_THEMES: LifeTheme[] = [
6869
"法定相続分、遺言の方式、遺産分割、特別寄与料など、相続に関わる法律の改正を追えます。",
6970
lawIds: [CIVIL_CODE],
7071
},
72+
{
73+
id: "creative",
74+
label: "著作権・創作",
75+
icon: "description",
76+
description: "著作物の利用、引用、SNS投稿、AI学習など",
77+
longDescription:
78+
"著作権の保護範囲、引用のルール、デジタルコンテンツの利用、AI学習データの取り扱いなど、創作活動に関わる法律の改正を追えます。",
79+
lawIds: [COPYRIGHT],
80+
},
7181
];
7282

7383
export function getThemeById(id: string): LifeTheme | undefined {

0 commit comments

Comments
 (0)