Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Finish author viewpoint page #11

Open
wants to merge 14 commits into
base: development
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 0 additions & 7 deletions src/app/authorviewpoint/[id]/page.tsx

This file was deleted.

4 changes: 4 additions & 0 deletions src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,7 @@

@tailwind components;
@tailwind utilities;

.scrollbar-gutter-stable-both-edges {
scrollbar-gutter: stable both-edges;
}
48 changes: 48 additions & 0 deletions src/app/issues/[id]/author/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import ViewpointCard from "@/components/AuthorViewpoint/ViewpointCard";
import Link from "next/link";
import { ArrowLongLeftIcon } from "@heroicons/react/24/outline";
import FactListCard from "@/components/AuthorViewpoint/FactListCard";
import { mockEmptyIssue, mockIssue } from "@/mock/conversationMock";
import { Metadata } from "next";

type AuthorViewPointProps = {
params: {
id: string;
};
};

type MetadataProps = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove unused props type

params: { id: string };
};

export async function generateMetadata({}: MetadataProps): Promise<Metadata> {
return {
title: `CommonGround - 撰寫觀點`,
keywords: "social-issues, viewpoints, rational-discussion",
};
}

export default function AuthorViewPoint({ params }: AuthorViewPointProps) {
const { id } = params;
const issue = id == "1" ? mockIssue : mockEmptyIssue;
return (
<main className="mx-auto my-8 w-full max-w-7xl">
<Link
href={`/issues/${id}`}
className="mb-2 ml-7 flex w-[100px] items-center text-lg font-semibold text-neutral-500 duration-300 hover:text-emerald-500"
>
<ArrowLongLeftIcon className="mr-1 inline-block h-6 w-6" />
返回議題
</Link>
<div className="flex h-[calc(100hv-157px)] w-full items-stretch gap-7">
{/* 157px = 56px(header) + 69px(margin-top between header and this div) + 32px(padding-bottom of main)*/}
<div className="w-2/3">
<ViewpointCard />
</div>
<div className="w-1/3">
<FactListCard facts={issue.facts} />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since there may be hundreds or thousands of facts per issue, we by default will show only an empty list of facts when the user loads the author viewpoint page. Any fact that appears in the reference area must either be there because (a) a user searched for & added it via the search bar or (b) a user created a fact when authoring this viewpoint

</div>
</div>
</main>
);
}
5 changes: 1 addition & 4 deletions src/app/issues/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,11 @@ export default function IssueView({ params }: IssueViewProps) {
console.log(id);

return (
<div className="flex min-h-screen flex-col bg-neutral-200">
<div>
<main className="flex flex-grow flex-col items-center p-8 pb-16">
{/* issue */}
<IssueCard issueId={id} />
{/* view */}
<ViewPointList issueId={id} />
</main>
{/* textbar */}
<AddViewPointBar id={id} />
</div>
);
Expand Down
3 changes: 2 additions & 1 deletion src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ export default function RootLayout({
<body className="fixed inset-0 bg-neutral-200 pt-14 antialiased">
<MantineProvider>
<Header />
<div className="h-[calc(100vh-56px)] overflow-y-auto">
<div className="scrollbar-gutter-stable-both-edges h-[calc(100vh-56px)] overflow-y-auto">
{/* 56px for header */}
{children}
</div>
</MantineProvider>
Expand Down
14 changes: 6 additions & 8 deletions src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,11 @@ export default function Page() {
const user = mockUser;

return (
<div className="flex min-h-screen flex-col bg-neutral-200">
<main className="flex flex-grow flex-col items-center p-8">
<h1 className="w-full max-w-3xl pb-3 text-2xl font-semibold text-neutral-900">
{user.username}, 歡迎來到 CommonGround
</h1>
<HomePageCard issues={issues} />
</main>
</div>
<main className="flex flex-grow flex-col items-center p-8">
<h1 className="w-full max-w-3xl pb-3 text-2xl font-semibold text-neutral-900">
{user.username}, 歡迎來到 CommonGround
</h1>
<HomePageCard issues={issues} />
</main>
);
}
32 changes: 32 additions & 0 deletions src/components/AuthorViewpoint/EditViewpointFact.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Fact } from "@/types/conversations.types";
import Link from "next/link";

type FactCardProps = {
fact: Fact;
};

export default function EditViewpointFact({ fact }: FactCardProps) {
return (
<div>
<h1 className="text-lg font-normal text-black">{fact.title}</h1>
{fact.references.map((reference) => (
<div key={reference.id} className="mt-1">
<Link
href={reference.url}
passHref
target="_blank"
className="inline-flex items-center gap-2 rounded-full bg-neutral-200 px-3"
>
<img className="h-4 w-4" src={reference.icon} alt="" />
<h1 className="text-sm font-normal text-neutral-500">
{new URL(reference.url).hostname}
</h1>
<h1 className="text-sm font-normal text-neutral-500">
{reference.title}
</h1>
</Link>
</div>
))}
</div>
);
}
62 changes: 62 additions & 0 deletions src/components/AuthorViewpoint/FactListCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
"use client";
import { Fact } from "@/types/conversations.types";
import EditViewpointFact from "@/components/AuthorViewpoint/EditViewpointFact";
import { MagnifyingGlassIcon, PlusIcon } from "@heroicons/react/24/outline";
import { Select, Button } from "@mantine/core";
import { useState } from "react";

type FactListCardProps = {
facts: Fact[];
};

export default function FactListCard({ facts }: FactListCardProps) {
const [searchData] = useState<string[]>([
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remember to define the setSearchData returned as well. If eslint warns you for unused variables, consider disabling that rule for this line temporarily (guide to how), or better, move the mock data to the server response for keyword search, and implement the "select fact to reference" function in this PR.

"This development could disrupt the EV market",
"Google.com",
"CommonGround",
]);
return (
<div className="h-full rounded-lg bg-neutral-100 px-7 py-4">
<h1 className="mb-1 text-lg font-semibold text-neutral-700">
事實
</h1>
<div className="mb-2 flex w-full items-center py-1 pr-[52px]">
burnedinthesky marked this conversation as resolved.
Show resolved Hide resolved
<MagnifyingGlassIcon className="inline-block h-5 w-5 stroke-neutral-500" />
<Select
variant="unstyled"
searchable
data={searchData}
checkIconPosition="right"
radius={0}
w="100%"
classNames={{
input: "ml-2 bg-transparent text-lg font-normal text-neutral-500 focus-within:outline-b-2 focus-within:border-b-emerald-500 focus-within:outline-none",
}}
placeholder="搜尋 CommonGround"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add the option to import a fact when nothing is found (Mantine reference)

/>
</div>
<div className="h-[calc(100vh-265px)] overflow-auto">
{/* 265px = 56px(header) + 69px(margin-top between header and this div) + 32px(padding-bottom of main)
+ 92px(FactListCard title and search box) + 16px(FactListCard padding-bottom)*/}
<div className="flex flex-col gap-3 pl-7 pr-4">
{facts.map((fact) => (
<div key={fact.id}>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the outer div is unnecessary, just <EditViewpointFact key={fact.id} fact={fact}> should do

<EditViewpointFact fact={fact} />
</div>
))}
</div>

<Button
variant="transparent"
leftSection={<PlusIcon className="h-6 w-6" />}
classNames={{
root: "px-0 ml-7 mt-2 text-neutral-600 text-base font-normal hover:text-emerald-500 duration-300",
section: "mr-2",
}}
>
引入一條事實
</Button>
</div>
</div>
);
}
104 changes: 104 additions & 0 deletions src/components/AuthorViewpoint/ViewpointCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
"use client";
import { TrashIcon, PlusIcon } from "@heroicons/react/24/outline";
import { Button, TextInput } from "@mantine/core";
import { useState, useRef, useEffect } from "react";

export default function ViewpointCard() {
const [viewpointTitle, setViewpointTitle] = useState("");
const [contentEmpty, setContentEmpty] = useState<boolean>(true);
const inputRef = useRef<HTMLDivElement>(null);

useEffect(() => {
if (inputRef?.current === null || inputRef.current.innerHTML !== "")
return;
const placeholderElement = document.createElement("p");
placeholderElement.className = "text-neutral-500";
placeholderElement.textContent =
"開始打字,或選取一段文字來新增引注資料";
inputRef.current.appendChild(placeholderElement);
}, [inputRef]);

return (
<div className="flex h-full flex-col gap-2 overflow-auto rounded-lg bg-neutral-100 px-7 py-4">
<h1 className="text-lg font-semibold text-neutral-700">觀點</h1>
<TextInput
onChange={(e) => setViewpointTitle(e.currentTarget.value)}
variant="unstyled"
radius={0}
placeholder="用一句話簡述你的觀點"
style={{ width: "100%" }}
classNames={{
input: "border-none bg-transparent text-2xl font-semibold text-neutral-700 placeholder:text-neutral-500 focus:outline-none",
}}
/>
<div
contentEditable="true"
className="h-full min-h-7 w-full resize-none bg-transparent text-lg font-normal text-neutral-700 placeholder:text-neutral-500 focus:outline-none"
ref={inputRef}
onInput={(e) => {
Array.from(e.currentTarget.children).forEach((node) => {
if (node.className.includes("pt-1.5")) return;
node.classList.add("pt-1.5");
});
}}
onFocus={() => {
if (!contentEmpty || !inputRef?.current) return;
inputRef.current.innerHTML = "";
}}
onBlur={() => {
if (inputRef?.current === null) return;
const isEmpty = Array.from(
inputRef.current.childNodes,
).every(
(node) =>
(node.nodeType === Node.ELEMENT_NODE &&
(node as HTMLElement).tagName === "BR") ||
(node.nodeType === Node.TEXT_NODE &&
node.textContent?.trim() === ""),
);
setContentEmpty(isEmpty);
if (isEmpty) {
inputRef.current.innerHTML = "";
const placeholderElement = document.createElement("p");
placeholderElement.className = "text-neutral-500";
placeholderElement.textContent =
"開始打字,或選取一段文字來新增引注資料";
inputRef.current.appendChild(placeholderElement);
}
}}
/>
<div className="flex justify-end gap-3">
<Button
variant="outline"
color="#525252"
leftSection={<TrashIcon className="h-5 w-5" />}
classNames={{
root: "px-0 h-8 w-[76px] text-sm font-normal text-neutral-600",
section: "mr-1",
}}
>
刪除
</Button>
<Button
variant="filled"
color="#2563eb"
leftSection={<PlusIcon className="h-5 w-5" />}
classNames={{
root: "px-0 h-8 w-[76px] text-sm font-normal text-white",
section: "mr-1",
}}
onClick={() => {
console.log(
"Title= ",
viewpointTitle,
"\nContent= ",
inputRef.current?.innerText,
);
}}
>
發表
</Button>
</div>
</div>
);
}
4 changes: 2 additions & 2 deletions src/components/Conversation/ViewPoints/AddViewPointBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ type AddViewPointBarProps = {
export default function AddViewPointBar({ id }: AddViewPointBarProps) {
console.log(`Try to add Viewpoint on issue ${id}`);
return (
<div className="fixed bottom-0 flex w-full justify-center px-8 pb-3">
<div className="fixed bottom-0 left-0 right-0 flex justify-center px-8 pb-3">
<Link
href={`/authorviewpoint/${id}`}
href={`/issues/${id}/author`}
className="left-4 z-20 flex w-full max-w-3xl items-center rounded-full border-[1px] border-zinc-500 bg-neutral-50 py-2"
>
<PlusIcon className="ml-[21px] inline size-6 fill-none stroke-neutral-500 stroke-[1.5] duration-300 hover:stroke-emerald-500" />
Expand Down
27 changes: 15 additions & 12 deletions src/mock/conversationMock.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,6 @@
import { Fact, Issue, ViewPoint } from "@/types/conversations.types";
import { UserRepresentation } from "@/types/users.types";

export const mockIssue: Issue = {
id: 1,
title: "Breakthrough in Electric Vehicle Battery Technology Announced",
summary:
"San Francisco, CA — In a significant leap forward for electric vehicle (EV) technology, researchers at GreenTech Innovations announced today the development of a new battery that could revolutionize the industry. The new design promises to double the range of EVs while reducing charging time to under 15 minutes.The breakthrough was made possible by a novel combination of advanced materials that increase energy density while ensuring battery stability. GreenTech CEO, Michael Foster, stated, “We believe this advancement will accelerate the mass adoption of electric vehicles and contribute significantly to reducing carbon emissions globally.”Experts have pointed out that while the technology shows promise, it will take time to scale up production and integrate it into the existing infrastructure. Additionally, questions remain about the long-term environmental impact of mining the rare materials used in the batteries.",
};
export const mockEmptyIssue: Issue = {
id: 2,
title: "CommonGround, A New Social Media Platform Game Changer!",
summary: "",
};

export const mockFact: Fact = {
id: 1,
title: "This development could disrupt the EV market",
Expand All @@ -32,6 +20,21 @@ export const mockFact: Fact = {
],
};

export const mockIssue: Issue = {
id: 1,
title: "Breakthrough in Electric Vehicle Battery Technology Announced",
summary:
"San Francisco, CA — In a significant leap forward for electric vehicle (EV) technology, researchers at GreenTech Innovations announced today the development of a new battery that could revolutionize the industry. The new design promises to double the range of EVs while reducing charging time to under 15 minutes.The breakthrough was made possible by a novel combination of advanced materials that increase energy density while ensuring battery stability. GreenTech CEO, Michael Foster, stated, “We believe this advancement will accelerate the mass adoption of electric vehicles and contribute significantly to reducing carbon emissions globally.”Experts have pointed out that while the technology shows promise, it will take time to scale up production and integrate it into the existing infrastructure. Additionally, questions remain about the long-term environmental impact of mining the rare materials used in the batteries.",
facts: [mockFact, mockFact, mockFact, mockFact],
};

export const mockEmptyIssue: Issue = {
id: 2,
title: "CommonGround, A New Social Media Platform Game Changer!",
summary: "",
facts: [],
};

export const mockUser: UserRepresentation = {
username: "Sarah Fields",
avatar: "/favicon.ico",
Expand Down
1 change: 1 addition & 0 deletions src/types/conversations.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export interface Issue {
id: number;
title: string;
summary: string;
facts: Fact[];
}

export interface FactReference {
Expand Down