-
Notifications
You must be signed in to change notification settings - Fork 0
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
base: development
Are you sure you want to change the base?
Changes from all commits
d84764e
41c6a5e
55f5148
4f1b8af
3b5b208
f647c69
6076fad
af654da
0e58d7b
6b1460b
02d95cb
a8732a4
034134c
da39ce3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
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 = { | ||
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} /> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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> | ||
); | ||
} |
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> | ||
); | ||
} |
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[]>([ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please remember to define the |
||
"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" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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}> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the outer div is unnecessary, just |
||
<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> | ||
); | ||
} |
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> | ||
); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove unused props type