Skip to content
This repository was archived by the owner on Feb 9, 2026. It is now read-only.

Commit d3476ed

Browse files
authored
Merge pull request #135 from u-hossy/feature/update-top-page
frontend:TopPageのヘッダーを追加した
2 parents 7f1de4e + 694ef83 commit d3476ed

15 files changed

+469
-196
lines changed

frontend/mock/db.json

Lines changed: 0 additions & 34 deletions
This file was deleted.

frontend/public/arrow-down.svg

Lines changed: 1 addition & 0 deletions
Loading

frontend/public/book-open.svg

Lines changed: 1 addition & 0 deletions
Loading

frontend/public/lightbulb.svg

Lines changed: 1 addition & 0 deletions
Loading

frontend/src/components/CardWrapper.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
import { cn } from "@/lib/utils";
1010

1111
interface CardWrapperProps extends React.ComponentProps<typeof Card> {
12-
title: string;
12+
title?: string;
1313
description?: string;
1414
children: React.ReactNode;
1515
nextButton?: React.ReactNode;
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
2+
import { Input } from "@/components/ui/input";
3+
import {
4+
Select,
5+
SelectContent,
6+
SelectItem,
7+
SelectTrigger,
8+
SelectValue,
9+
} from "@/components/ui/select";
10+
import { sampleMembers, samplePayments } from "@/data/sampleData";
11+
import type { Member } from "@/types/member";
12+
import { Button } from "./ui/button";
13+
14+
interface ExampleBillingDetailCardProps {
15+
paidBy: Member;
16+
}
17+
18+
export default function ExampleBillingDetailCard({
19+
paidBy,
20+
}: ExampleBillingDetailCardProps) {
21+
const payerPayments = samplePayments.filter((p) => p.paidBy === paidBy.id);
22+
23+
return (
24+
<Card className="h-[330px] overflow-y-auto shadow-md">
25+
<CardHeader>
26+
<CardTitle>{paidBy.name}さんが受け取る金額の入力</CardTitle>
27+
</CardHeader>
28+
<CardContent className="space-y-0">
29+
{payerPayments.map((detail, index) => {
30+
const isSelectable = detail.paidFor !== -1;
31+
return (
32+
<div
33+
key={detail.id !== -1 ? detail.id : `new-${index}`}
34+
className="border-b py-3 last:border-b-0"
35+
>
36+
<div className="flex flex-col gap-3 sm:flex-row sm:items-center">
37+
<div className="flex items-center gap-2">
38+
<Select
39+
value={detail.paidFor === -1 ? "" : String(detail.paidFor)}
40+
>
41+
<SelectTrigger className="w-32">
42+
<SelectValue placeholder="請求先" />
43+
</SelectTrigger>
44+
<SelectContent>
45+
{sampleMembers
46+
.filter((p) => p.id !== paidBy.id && p.name !== "")
47+
.map((p) => (
48+
<SelectItem key={p.id} value={String(p.id)}>
49+
{p.name}
50+
</SelectItem>
51+
))}
52+
</SelectContent>
53+
</Select>
54+
<span>さんから</span>
55+
</div>
56+
<div className="flex items-center gap-2">
57+
<Input
58+
type="number"
59+
disabled={!isSelectable}
60+
placeholder="金額"
61+
className="w-24"
62+
value={detail.amount}
63+
/>
64+
<span>円もらう</span>
65+
<Button variant="destructive" className="cursor-pointer">
66+
削除
67+
</Button>
68+
</div>
69+
</div>
70+
</div>
71+
);
72+
})}
73+
</CardContent>
74+
</Card>
75+
);
76+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
2+
import { sampleMembers } from "@/data/sampleData";
3+
import ExampleBillingDetailCard from "./ExampleBillingDetailCard";
4+
5+
export default function ExampleBillingTabList() {
6+
return (
7+
<Tabs defaultValue={`tab-${sampleMembers[0].id}`} className="w-full">
8+
<TabsList className="flex w-full justify-start gap-2 overflow-x-auto whitespace-nowrap">
9+
{sampleMembers.map((member) => (
10+
<TabsTrigger
11+
key={member.id}
12+
value={`tab-${member.id}`}
13+
className="min-w-20 shrink-0 px-3 py-1"
14+
>
15+
{member.name}
16+
</TabsTrigger>
17+
))}
18+
</TabsList>
19+
20+
{sampleMembers.map((member) => (
21+
<TabsContent key={member.id} value={`tab-${member.id}`}>
22+
<ExampleBillingDetailCard paidBy={member} />
23+
</TabsContent>
24+
))}
25+
</Tabs>
26+
);
27+
}

frontend/src/components/NetworkGraph.tsx

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,20 @@
11
import CytoscapeComponent from "react-cytoscapejs";
2+
import type { Member } from "@/types/member";
23
import type { Result } from "@/types/result";
34

45
interface Props {
6+
members: Member[];
57
results: Result[];
8+
height?: string;
9+
width?: string;
610
}
711

8-
export default function NetworkGraph({ results }: Props) {
12+
export default function NetworkGraph({
13+
members,
14+
results,
15+
height = "500px",
16+
width = "500px",
17+
}: Props) {
918
if (results.length === 0) {
1019
return (
1120
<div className="p-8 text-center">
@@ -16,8 +25,14 @@ export default function NetworkGraph({ results }: Props) {
1625
);
1726
}
1827

28+
const idToName = Object.fromEntries(members.map((m) => [m.id, m.name]));
29+
1930
const people = Array.from(
20-
new Set(results.map((r) => r.paidBy).concat(results.map((r) => r.paidFor))),
31+
new Set(
32+
results
33+
.map((r) => idToName[r.paidBy])
34+
.concat(results.map((r) => idToName[r.paidFor])),
35+
),
2136
);
2237

2338
const randomColor = () => {
@@ -29,7 +44,7 @@ export default function NetworkGraph({ results }: Props) {
2944
);
3045
};
3146

32-
const colorMap = new Map<number, string>();
47+
const colorMap = new Map<string, string>();
3348
people.forEach((p) => {
3449
colorMap.set(p, randomColor());
3550
});
@@ -45,10 +60,10 @@ export default function NetworkGraph({ results }: Props) {
4560
const edges = results.map((r, i) => ({
4661
data: {
4762
id: `e-${i}`,
48-
source: r.paidBy,
49-
target: r.paidFor,
63+
source: idToName[r.paidBy],
64+
target: idToName[r.paidFor],
5065
label: `${r.amount}円`,
51-
color: colorMap.get(r.paidBy),
66+
color: colorMap.get(idToName[r.paidBy]),
5267
},
5368
}));
5469

@@ -92,9 +107,10 @@ export default function NetworkGraph({ results }: Props) {
92107
return (
93108
<div
94109
style={{
95-
width: "calc(100% - 48px)",
110+
width: width,
111+
// width: "100%",
96112
margin: "0 24px",
97-
height: "500px",
113+
height: height,
98114
boxShadow: "0 4px 12px rgba(0, 0, 0, 0.25)",
99115
borderRadius: "8px",
100116
background: "#ffffff",

frontend/src/components/ResultDetailCard.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,14 @@ interface ResultDetailCardProps {
1313
personId: MemberId;
1414
members: Member[];
1515
results: Result[];
16+
height?: string;
1617
}
1718

1819
export default function ResultDetailCard({
1920
personId,
2021
members,
2122
results,
23+
height = "250px",
2224
}: ResultDetailCardProps) {
2325
const person = members.find((m) => m.id === personId);
2426
const personName = person?.name || `メンバー${personId}`;
@@ -40,7 +42,10 @@ export default function ResultDetailCard({
4042
<CardTitle className="text-xl">{personName}さんの精算結果</CardTitle>
4143
</CardHeader>
4244

43-
<CardContent className="flex h-full min-h-[250px] items-start gap-6 px-6 py-4">
45+
<CardContent
46+
className="flex h-full items-start gap-6 px-6 py-4"
47+
style={{ minHeight: height }}
48+
>
4449
<div className="flex-1">
4550
<h3 className="mb-3 font-semibold text-lg text-red-700">
4651
支払うお金 ({formatCurrency(totalPayment)}円)
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@ import ResultDetailCard from "./ResultDetailCard";
66
interface ResultProps {
77
members: Member[];
88
results: Result[];
9+
height?: string;
910
}
1011

11-
export default function ResultTab({ members, results }: ResultProps) {
12+
export default function ResultTab({ members, results, height }: ResultProps) {
1213
// 結果がない場合の表示
1314
if (results.length === 0) {
1415
return (
@@ -49,6 +50,7 @@ export default function ResultTab({ members, results }: ResultProps) {
4950
personId={person}
5051
members={members}
5152
results={results}
53+
height={height}
5254
/>
5355
</TabsContent>
5456
))}

0 commit comments

Comments
 (0)