feat: events page in dashboard

This commit is contained in:
DIYgod 2023-03-08 01:09:34 +00:00
parent 8324beaa09
commit 20ae6a2091
No known key found for this signature in database
5 changed files with 239 additions and 3 deletions

View File

@ -6,6 +6,7 @@
"Unread notifications": "未读通知",
"Settings": "设置",
"Events": "活动",
"New Events": "新活动",
"Site Stats": "站点统计",
"Total Posts": "文章总数",
"Total Comments": "评论总数",
@ -131,5 +132,12 @@
"Published!" : "已发布!",
"Title goes here...": "标题在这里...",
"Start writing...": "开始写作...",
"Slug can only contain letters, numbers, hyphens, and underscores." : "短链接只能包含字母、数字、连字符和下划线。"
"Slug can only contain letters, numbers, hyphens, and underscores." : "短链接只能包含字母、数字、连字符和下划线。",
"Date": "日期",
"Prize": "奖金",
"Winners": "获奖者",
"Learn more": "了解更多",
"Ended": "已结束",
"Upcoming": "即将开始",
"Ongoing": "进行中"
}

View File

@ -98,5 +98,7 @@
"0xcavin",
"shuxinfeng",
"tea-24",
"revery"
"revery",
"hsinyau",
"rootless"
]

View File

@ -21,6 +21,8 @@ import { useAccountState, useConnectModal } from "@crossbell/connect-kit"
import { useTranslation } from "react-i18next"
import { Logo } from "~/components/common/Logo"
import { QuestionMarkCircleIcon } from "@heroicons/react/24/outline"
import { getStorage } from "~/lib/storage"
import { useGetPagesBySite } from "~/queries/page"
export function DashboardLayout({
children,
@ -64,6 +66,26 @@ export function DashboardLayout({
const showNotificationModal = useShowNotificationModal()
const { isAllRead } = useNotifications()
const pages = useGetPagesBySite({
type: "post",
site: "xlog-events",
take: 1,
})
const [isEventsAllRead, setIsEventsAllRead] = React.useState(true)
const latestEventRead = getStorage("latestEventRead")?.value || 0
useEffect(() => {
if (pages.isSuccess) {
if (
new Date(pages.data.pages[0].list?.[0].date_created) >
new Date(latestEventRead)
) {
setIsEventsAllRead(false)
} else {
setIsEventsAllRead(true)
}
}
}, [pages.isSuccess, pages.data?.pages, latestEventRead])
const links: {
href?: string
onClick?: () => void
@ -95,6 +117,12 @@ export function DashboardLayout({
icon: isAllRead ? "i-bi:bell" : "i-bi:bell-fill",
text: isAllRead ? "Notifications" : "Unread notifications",
},
{
href: `/dashboard/${subdomain}/events`,
isActive: ({ href, pathname }) => href === pathname,
icon: isEventsAllRead ? "i-bi-gift" : "i-bi-gift-fill",
text: isEventsAllRead ? "Events" : "New Events",
},
{
href: `/dashboard/${subdomain}/settings/general`,
isActive: ({ pathname }) =>
@ -195,7 +223,7 @@ export function DashboardLayout({
<div className="absolute bottom-5 left-0 right-0 flex items-center px-4 flex-col">
<UniLink
href={DISCORD_LINK}
className="space-x-2 text-zinc-500 hover:text-zinc-800 flex w-full h-12 items-center justify-center transition-colors mb-2"
className="space-x-1 text-zinc-500 hover:text-zinc-800 flex w-full h-12 items-center justify-center transition-colors mb-2"
>
<QuestionMarkCircleIcon className="w-5 h-5" />
{isOpen && <span>{t("Need help?")}</span>}
@ -225,6 +253,9 @@ export function DashboardLayout({
<div className="mt-4">
Please switch account or request the owner to set you as the operator.
</div>
<UniLink href="/dashboard" className="mt-4 text-accent">
Take me to my own dashboard
</UniLink>
</div>
)
) : (

View File

@ -141,6 +141,7 @@ const expandPage = async (page: Note, useStat?: boolean) => {
}
}
page.cover = rendered.cover
page.metadata!.frontMatter = rendered.frontMatter
}
page.slug = encodeURIComponent(
page.attributes?.find((a) => a.trait_type === "xlog_slug")?.value ||

View File

@ -0,0 +1,194 @@
import { DashboardLayout } from "~/components/dashboard/DashboardLayout"
import { DashboardMain } from "~/components/dashboard/DashboardMain"
import { UniLink } from "~/components/ui/UniLink"
import { Image } from "~/components/ui/Image"
import type { ReactElement } from "react"
import { useGetSite } from "~/queries/site"
import { useDate } from "~/hooks/useDate"
import { useTranslation } from "next-i18next"
import { getServerSideProps as getLayoutServerSideProps } from "~/components/dashboard/DashboardLayout.server"
import { GetServerSideProps } from "next"
import { serverSidePropsHandler } from "~/lib/server-side-props"
import { useGetPagesBySite } from "~/queries/page"
import { CharacterFloatCard } from "~/components/common/CharacterFloatCard"
import { useEffect, useState } from "react"
import { Avatar } from "~/components/ui/Avatar"
import { cn } from "~/lib/utils"
import { ChevronRightIcon } from "@heroicons/react/20/solid"
import { getSiteLink } from "~/lib/helpers"
import { getStorage, setStorage } from "~/lib/storage"
export const getServerSideProps: GetServerSideProps = serverSidePropsHandler(
async (ctx) => {
const { props: layoutProps } = await getLayoutServerSideProps(ctx)
return {
props: {
...layoutProps,
},
}
},
)
function SiteAvatar({ siteId }: { siteId: string }) {
const site = useGetSite(siteId)
return (
<div className="inline-block w-10 h-10 relative">
<CharacterFloatCard siteId={siteId}>
<UniLink
href={getSiteLink({
subdomain: siteId,
})}
>
<Avatar
images={site.data?.avatars || []}
name={site.data?.name || ""}
size={40}
/>
</UniLink>
</CharacterFloatCard>
</div>
)
}
export default function EventsPage() {
const date = useDate()
const { t } = useTranslation("dashboard")
const pages = useGetPagesBySite({
type: "post",
site: "xlog-events",
take: 100,
})
const [latestEventRead, setLatestEventRead] = useState<Date>()
const [isMounted, setIsMounted] = useState(false)
useEffect(() => {
setIsMounted(true)
setLatestEventRead(new Date(getStorage("latestEventRead")?.value || 0))
setStorage("latestEventRead", {
value: new Date().toISOString(),
})
}, [])
pages.data?.pages[0]?.list.forEach((item) => {
item.metadata?.frontMatter?.Winners
})
return (
<DashboardMain title="Events">
<div className="min-w-[270px] flex flex-col xl:flex-row space-y-8 xl:space-y-0">
<div className="grid gap-4 grid-cols-2 sm:grid-cols-4">
{pages.data?.pages[0]?.list.map((item) => {
let status
if (item.metadata?.frontMatter?.EndTime < new Date()) {
status = "Ended"
} else if (item.metadata?.frontMatter?.StartTime > new Date()) {
status = "Upcoming"
} else {
status = "Ongoing"
}
let isUnread = false
if (
latestEventRead &&
new Date(item.date_created) > latestEventRead
) {
isUnread = true
}
return (
<div
className={cn("bg-slate-100 rounded-lg relative", {
"opacity-70": status === "Ended",
})}
key={item.id}
>
{isUnread && (
<div>
<span className="absolute -left-2 -top-2 bg-red-500 rounded-full w-4 h-4"></span>
</div>
)}
<div className="flex flex-col p-7 justify-between h-full">
<div>
<div className="font-bold mb-4">
<span
className={cn(
"border py-1 px-4 rounded-full text-white",
{
"bg-gray-500": status === "Ended",
"bg-green-500": status === "Upcoming",
"bg-yellow-500": status === "Ongoing",
},
)}
>
{t(status)}
</span>
</div>
{item.cover && (
<div className="w-full h-24 mb-4">
<Image
className="object-cover rounded"
alt="cover"
fill={true}
src={item.cover}
></Image>
</div>
)}
<div className="font-bold text-xl text-zinc-800 leading-tight">
{item.title}
</div>
</div>
<div className="mt-4 space-y-4 text-sm">
<div>
<span className="font-bold">{t("Date")}:</span>{" "}
{date.formatDate(
item.metadata?.frontMatter?.StartTime,
"lll",
isMounted ? undefined : "America/Los_Angeles",
)}{" "}
-{" "}
{date.formatDate(
item.metadata?.frontMatter?.EndTime,
"lll",
isMounted ? undefined : "America/Los_Angeles",
)}
</div>
<div>
<span className="font-bold">{t("Prize")}:</span>{" "}
{item.metadata?.frontMatter?.Prize}
</div>
<div>
<span className="font-bold">{t("Winners")}:</span>
<div className="flex items-center space-x-2 mt-2">
{item.metadata?.frontMatter?.Winners.map(
(winner: string) => (
<SiteAvatar key={winner} siteId={winner} />
),
)}
</div>
</div>
</div>
<UniLink
className="mt-6 font-bold flex items-center leading-none"
href={
item.metadata?.frontMatter?.ExtraLink ||
item.related_urls?.[0]
}
>
{t("Learn more")}{" "}
<ChevronRightIcon className="w-5 h-5 inline ml-1" />
</UniLink>
</div>
</div>
)
})}
</div>
</div>
</DashboardMain>
)
}
EventsPage.getLayout = (page: ReactElement) => {
return <DashboardLayout title="Dashboard">{page}</DashboardLayout>
}