update temp landing page
This commit is contained in:
parent
1732d61e46
commit
85c0019d22
|
|
@ -68,6 +68,7 @@
|
|||
"@types/sortablejs": "^1.13.0",
|
||||
"@types/ua-parser-js": "^0.7.36",
|
||||
"@types/uuid": "^8.3.4",
|
||||
"autoprefixer": "^10.4.7",
|
||||
"eslint": "^8.15.0",
|
||||
"eslint-config-next": "^12.1.6",
|
||||
"postcss-import": "^14.1.0",
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ specifiers:
|
|||
'@types/uuid': ^8.3.4
|
||||
'@web-std/form-data': ^3.0.2
|
||||
async-busboy: ^1.1.0
|
||||
autoprefixer: ^10.4.7
|
||||
busboy: ^1.6.0
|
||||
clsx: ^1.1.1
|
||||
cookie: ^0.5.0
|
||||
|
|
@ -119,6 +120,7 @@ devDependencies:
|
|||
'@types/sortablejs': 1.13.0
|
||||
'@types/ua-parser-js': 0.7.36
|
||||
'@types/uuid': 8.3.4
|
||||
autoprefixer: 10.4.7
|
||||
eslint: 8.15.0
|
||||
eslint-config-next: 12.1.6_6vdljae35l7howjttefzzsim3e
|
||||
postcss-import: 14.1.0
|
||||
|
|
@ -1085,6 +1087,21 @@ packages:
|
|||
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
|
||||
dev: false
|
||||
|
||||
/autoprefixer/10.4.7:
|
||||
resolution: {integrity: sha512-ypHju4Y2Oav95SipEcCcI5J7CGPuvz8oat7sUtYj3ClK44bldfvtvcxK6IEK++7rqB7YchDGzweZIBG+SD0ZAA==}
|
||||
engines: {node: ^10 || ^12 || >=14}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
postcss: ^8.1.0
|
||||
dependencies:
|
||||
browserslist: 4.20.3
|
||||
caniuse-lite: 1.0.30001338
|
||||
fraction.js: 4.2.0
|
||||
normalize-range: 0.1.2
|
||||
picocolors: 1.0.0
|
||||
postcss-value-parser: 4.2.0
|
||||
dev: true
|
||||
|
||||
/available-typed-arrays/1.0.5:
|
||||
resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
|
@ -1189,7 +1206,6 @@ packages:
|
|||
escalade: 3.1.1
|
||||
node-releases: 2.0.4
|
||||
picocolors: 1.0.0
|
||||
dev: false
|
||||
|
||||
/busboy/0.3.1:
|
||||
resolution: {integrity: sha512-y7tTxhGKXcyBxRKAni+awqx8uqaJKrSFSNFSeRG5CsWNdmy2BIK+6VGWEW7TZnIO/533mtMEA4rOevQV815YJw==}
|
||||
|
|
@ -1223,7 +1239,6 @@ packages:
|
|||
|
||||
/caniuse-lite/1.0.30001338:
|
||||
resolution: {integrity: sha512-1gLHWyfVoRDsHieO+CaeYe7jSo/MT7D7lhaXUiwwbuR5BwQxORs0f1tAwUSQr3YbxRXJvxHM/PA5FfPQRnsPeQ==}
|
||||
dev: false
|
||||
|
||||
/chalk/2.4.2:
|
||||
resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
|
||||
|
|
@ -1469,7 +1484,6 @@ packages:
|
|||
|
||||
/electron-to-chromium/1.4.137:
|
||||
resolution: {integrity: sha512-0Rcpald12O11BUogJagX3HsCN3FE83DSqWjgXoHo5a72KUKMSfI39XBgJpgNNxS9fuGzytaFjE06kZkiVFy2qA==}
|
||||
dev: false
|
||||
|
||||
/emoji-regex/9.2.2:
|
||||
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
|
||||
|
|
@ -1525,7 +1539,6 @@ packages:
|
|||
/escalade/3.1.1:
|
||||
resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==}
|
||||
engines: {node: '>=6'}
|
||||
dev: false
|
||||
|
||||
/escape-string-regexp/1.0.5:
|
||||
resolution: {integrity: sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=}
|
||||
|
|
@ -1896,6 +1909,10 @@ packages:
|
|||
mime-types: 2.1.35
|
||||
dev: false
|
||||
|
||||
/fraction.js/4.2.0:
|
||||
resolution: {integrity: sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==}
|
||||
dev: true
|
||||
|
||||
/fs.realpath/1.0.0:
|
||||
resolution: {integrity: sha1-FQStJSMVjKpA20onh8sBQRmU6k8=}
|
||||
|
||||
|
|
@ -2500,13 +2517,17 @@ packages:
|
|||
|
||||
/node-releases/2.0.4:
|
||||
resolution: {integrity: sha512-gbMzqQtTtDz/00jQzZ21PQzdI9PyLYqUSvD0p3naOhX4odFji0ZxYdnVwPTxmSwkmxhcFImpozceidSG+AgoPQ==}
|
||||
dev: false
|
||||
|
||||
/normalize-path/3.0.0:
|
||||
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: true
|
||||
|
||||
/normalize-range/0.1.2:
|
||||
resolution: {integrity: sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: true
|
||||
|
||||
/object-assign/4.1.1:
|
||||
resolution: {integrity: sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
|
|
|||
|
|
@ -2,5 +2,6 @@ module.exports = {
|
|||
plugins: {
|
||||
"postcss-import": {},
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,15 @@
|
|||
import Head from "next/head"
|
||||
|
||||
export const SEOHead: React.FC<{
|
||||
title: string
|
||||
siteName: string
|
||||
title: string | undefined
|
||||
description?: string | null
|
||||
image?: string | null
|
||||
}> = ({ title, description, image }) => {
|
||||
}> = ({ siteName, title, description, image }) => {
|
||||
return (
|
||||
<Head>
|
||||
<title>{title}</title>
|
||||
<title>{title ? `${title} - ${siteName}` : siteName}</title>
|
||||
<meta name="og:site_name" content={siteName} />
|
||||
<meta name="og:title" content={title} />
|
||||
<meta name="twitter:title" content={title} />
|
||||
<meta name="description" content={description || ""} />
|
||||
|
|
|
|||
|
|
@ -2,25 +2,31 @@ import clsx from "clsx"
|
|||
import Link from "next/link"
|
||||
import { useRouter } from "next/router"
|
||||
import React from "react"
|
||||
import { APP_NAME } from "~/lib/env"
|
||||
import { getSiteLink } from "~/lib/helpers"
|
||||
import { trpc } from "~/lib/trpc"
|
||||
import { SEOHead } from "../common/SEOHead"
|
||||
import { DashboardIcon } from "../icons/DashboardIcon"
|
||||
import { UniLink } from "../ui/UniLink"
|
||||
import { DashboardSidebar } from "./DashboardSidebar"
|
||||
import { SiteSwitcher } from "./SiteSwitcher"
|
||||
|
||||
export function DashboardLayout({ children }: { children: React.ReactNode }) {
|
||||
export function DashboardLayout({
|
||||
children,
|
||||
title,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
title: string
|
||||
}) {
|
||||
const router = useRouter()
|
||||
const subdomain = router.query.subdomain as string
|
||||
|
||||
const subscriptionsQuery = trpc.useQuery(
|
||||
const { data: subscriptions } = trpc.useQuery(
|
||||
["user.getSubscriptions", { canManage: true }],
|
||||
{}
|
||||
)
|
||||
const subscriptions = subscriptionsQuery.data
|
||||
|
||||
const viewerResult = trpc.useQuery(["auth.viewer"])
|
||||
const viewer = viewerResult.data
|
||||
const { data: viewer } = trpc.useQuery(["auth.viewer"])
|
||||
|
||||
const links: {
|
||||
href: string
|
||||
|
|
@ -108,6 +114,7 @@ export function DashboardLayout({ children }: { children: React.ReactNode }) {
|
|||
|
||||
return (
|
||||
<>
|
||||
<SEOHead title={title} siteName={APP_NAME} />
|
||||
<div className="flex">
|
||||
<DashboardSidebar>
|
||||
<div className="mb-2">
|
||||
|
|
|
|||
|
|
@ -1,84 +1,77 @@
|
|||
import Link from "next/link"
|
||||
import { logout } from "~/lib/auth.client"
|
||||
import { APP_NAME } from "~/lib/env"
|
||||
import { APP_DESCRIPTION, APP_NAME } from "~/lib/env"
|
||||
import { useStore } from "~/lib/store"
|
||||
import { truthy } from "~/lib/utils"
|
||||
import { SEOHead } from "../common/SEOHead"
|
||||
import { UniLink } from "../ui/UniLink"
|
||||
|
||||
export function MainLayout({
|
||||
isLoggedIn,
|
||||
children,
|
||||
region,
|
||||
title,
|
||||
}: {
|
||||
isLoggedIn: boolean
|
||||
children?: React.ReactNode
|
||||
region: string | null
|
||||
title?: string
|
||||
}) {
|
||||
const setLoginModalOpened = useStore((store) => store.setLoginModalOpened)
|
||||
|
||||
const links: Array<{ text: string; href?: string; onClick?: () => void }> =
|
||||
isLoggedIn
|
||||
? [
|
||||
{
|
||||
text: "Dashboard",
|
||||
href: "/dashboard",
|
||||
},
|
||||
{
|
||||
text: "Log out",
|
||||
onClick() {
|
||||
logout()
|
||||
},
|
||||
},
|
||||
]
|
||||
: [
|
||||
{
|
||||
text: "Log in",
|
||||
onClick() {
|
||||
setLoginModalOpened(true)
|
||||
},
|
||||
},
|
||||
]
|
||||
return (
|
||||
<>
|
||||
<div className="h-screen flex items-center justify-center">
|
||||
<div className="-mt-20">
|
||||
<div className="max-w-screen-md mx-auto px-5">
|
||||
<div>
|
||||
<span className="inline-block text-3xl lg:text-7xl px-3 rounded-lg py-2 font-bold bg-indigo-600 text-white">
|
||||
{APP_NAME}
|
||||
</span>
|
||||
</div>
|
||||
<div className="italic text-zinc-500 text-sm mt-1">
|
||||
..is under early testing, don{`'`}t use this app in production.
|
||||
</div>
|
||||
<div className="space-x-5 mt-10">
|
||||
{links.map((link) => {
|
||||
if (link.href) {
|
||||
return (
|
||||
<Link href={link.href} key={link.text}>
|
||||
<a className="font-bold nav-link underline">
|
||||
{link.text}
|
||||
</a>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
className="font-bold nav-link underline"
|
||||
onClick={link.onClick}
|
||||
key={link.text}
|
||||
>
|
||||
{link.text}
|
||||
</button>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
<div className="mt-10 text-zinc-300 text-xs">
|
||||
<span className="uppercase">Region: {region}</span>
|
||||
</div>
|
||||
<SEOHead
|
||||
title={title}
|
||||
siteName={APP_NAME}
|
||||
description={APP_DESCRIPTION}
|
||||
/>
|
||||
<header className="py-10">
|
||||
<div className="max-w-screen-md px-5 mx-auto flex justify-between items-center">
|
||||
<h1 className="inline-block text-2xl font-extrabold">{APP_NAME}</h1>
|
||||
<div className="space-x-5">
|
||||
{isLoggedIn ? (
|
||||
<UniLink
|
||||
onClick={logout}
|
||||
className="space-x-2 rounded-full px-4 h-8 inline-flex items-center text-sm text-indigo-500 font-medium hover:bg-indigo-50"
|
||||
>
|
||||
<span>Sign Out</span>
|
||||
<svg
|
||||
className="w-5 h-5"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 20 20"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M3 3a1 1 0 00-1 1v12a1 1 0 102 0V4a1 1 0 00-1-1zm10.293 9.293a1 1 0 001.414 1.414l3-3a1 1 0 000-1.414l-3-3a1 1 0 10-1.414 1.414L14.586 9H7a1 1 0 100 2h7.586l-1.293 1.293z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</UniLink>
|
||||
) : (
|
||||
<UniLink
|
||||
onClick={() => {
|
||||
setLoginModalOpened(true)
|
||||
}}
|
||||
className="bg-indigo-100 rounded-full px-4 h-8 inline-flex items-center text-sm text-indigo-500 font-medium"
|
||||
>
|
||||
Sign In
|
||||
</UniLink>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
{children}
|
||||
<footer className="mt-10 text-sm font-medium text-zinc-500">
|
||||
<div className="max-w-screen-md px-5 mx-auto">
|
||||
<span>© {APP_NAME}</span>
|
||||
<span className="mx-3">·</span>
|
||||
<span>
|
||||
Region: <span className="uppercase">{region}</span>
|
||||
</span>
|
||||
</div>
|
||||
</footer>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,9 +68,6 @@ export const SiteHeader: React.FC<{
|
|||
{
|
||||
label: "Sign out",
|
||||
onClick() {
|
||||
if (!confirm(`Are you sure you want to log out?`)) {
|
||||
return
|
||||
}
|
||||
logout()
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -28,12 +28,11 @@ export const SiteLayout: React.FC<SiteLayoutProps> = ({
|
|||
children,
|
||||
title,
|
||||
}) => {
|
||||
const docTitle = title ? `${title} - ${site.name}` : site.name
|
||||
|
||||
return (
|
||||
<>
|
||||
<SEOHead
|
||||
title={docTitle}
|
||||
title={title || ""}
|
||||
siteName={site.name}
|
||||
description={site.description}
|
||||
image={getUserContentsUrl(site.icon)}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@
|
|||
|
||||
.prose a {
|
||||
@apply text-indigo-600;
|
||||
@apply underline;
|
||||
@apply hover:underline;
|
||||
}
|
||||
|
||||
.prose pre {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import { OUR_DOMAIN } from "./env"
|
||||
|
||||
export const logout = () => {
|
||||
if (!confirm(`Are you sure you want to log out?`)) return
|
||||
|
||||
const url = new URL(`${location.protocol}//${OUR_DOMAIN}/api/logout`)
|
||||
if (location.host !== OUR_DOMAIN) {
|
||||
url.searchParams.set(
|
||||
|
|
|
|||
|
|
@ -4,3 +4,4 @@ export const APP_NAME = process.env.NEXT_PUBLIC_APP_NAME
|
|||
export const OUR_DOMAIN = process.env.NEXT_PUBLIC_OUR_DOMAIN
|
||||
export const SITE_URL = `${IS_PROD ? "https" : "http"}://${OUR_DOMAIN}`
|
||||
export const R2_URL = process.env.NEXT_PUBLIC_R2_URL
|
||||
export const APP_DESCRIPTION = process.env.APP_DESCRIPTION
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ export default function AccountProfilePage() {
|
|||
}, [viewer.data, setValue])
|
||||
|
||||
return (
|
||||
<DashboardLayout>
|
||||
<DashboardLayout title="Account">
|
||||
<SettingsLayout title="Account" type="account">
|
||||
{viewer.data && (
|
||||
<div>
|
||||
|
|
|
|||
|
|
@ -167,7 +167,7 @@ export default function SubdomainEditor() {
|
|||
}, [page])
|
||||
|
||||
return (
|
||||
<DashboardLayout>
|
||||
<DashboardLayout title="Editor">
|
||||
<DashboardMain fullWidth>
|
||||
<header className="flex justify-between absolute top-0 left-0 right-0 z-10 px-5 h-14 border-b items-center text-sm">
|
||||
<div></div>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,48 @@
|
|||
import { useRouter } from "next/router"
|
||||
import { DashboardLayout } from "~/components/dashboard/DashboardLayout"
|
||||
import { DashboardMain } from "~/components/dashboard/DashboardMain"
|
||||
import { UniLink } from "~/components/ui/UniLink"
|
||||
|
||||
export default function SubdomainIndex() {
|
||||
const router = useRouter()
|
||||
const subdomain = router.query.subdomain as string
|
||||
return (
|
||||
<DashboardLayout>
|
||||
<DashboardMain>hi</DashboardMain>
|
||||
<DashboardLayout title="Dashboard">
|
||||
<DashboardMain>
|
||||
<div className="prose max-w-screen-md">
|
||||
<p>
|
||||
<svg className="w-8 h-8" viewBox="0 0 24 24">
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M7.03 4.95L3.5 8.5c-3.33 3.31-3.33 8.69 0 12s8.69 3.33 12 0l6-6c1-.97 1-2.56 0-3.54c-.1-.12-.23-.23-.37-.32l.37-.39c1-.97 1-2.56 0-3.54c-.14-.16-.33-.3-.5-.41c.38-.92.21-2.02-.54-2.77c-.87-.87-2.22-.96-3.2-.28a2.517 2.517 0 0 0-3.88-.42l-2.51 2.51c-.09-.14-.2-.27-.32-.39a2.53 2.53 0 0 0-3.52 0m1.41 1.42c.2-.2.51-.2.71 0s.2.51 0 .71l-3.18 3.18a3 3 0 0 1 0 4.24l1.41 1.41a5.004 5.004 0 0 0 1.12-5.36l6.3-6.3c.2-.2.51-.2.7 0s.21.51 0 .71l-4.59 4.6l1.41 1.41l6.01-6.01c.2-.2.51-.2.71 0c.2.2.2.51 0 .71l-6.01 6.01l1.41 1.41l4.95-4.95c.2-.2.51-.2.71 0c.2.2.2.51 0 .71l-5.66 5.65l1.41 1.42l3.54-3.54c.2-.2.51-.2.71 0c.2.2.2.51 0 .71l-6 6.01c-2.54 2.54-6.65 2.54-9.19 0s-2.54-6.65 0-9.19l3.53-3.54M23 17c0 3.31-2.69 6-6 6v-1.5c2.5 0 4.5-2 4.5-4.5H23M1 7c0-3.31 2.69-6 6-6v1.5c-2.5 0-4.5 2-4.5 4.5H1Z"
|
||||
></path>
|
||||
</svg>
|
||||
</p>
|
||||
<p>Hello there,</p>
|
||||
<p>
|
||||
This page is quite empty for now, but we will polish it very soon!
|
||||
Maybe some useful analytics!
|
||||
</p>
|
||||
<p>Here{`'`}re some useful links:</p>
|
||||
<ul>
|
||||
<li>
|
||||
<UniLink href={`/dashboard/${subdomain}/editor?type=post`}>
|
||||
Create a Post
|
||||
</UniLink>
|
||||
</li>
|
||||
<li>
|
||||
<UniLink href={`/dashboard/${subdomain}/settings/general`}>
|
||||
Change Site Name or Icon
|
||||
</UniLink>
|
||||
</li>
|
||||
<li>
|
||||
<UniLink href={`/dashboard/${subdomain}/account/profile`}>
|
||||
Profile Settings
|
||||
</UniLink>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</DashboardMain>
|
||||
</DashboardLayout>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { PagesManager } from "~/components/dashboard/PagesManager"
|
|||
|
||||
export default function SubdomainPages() {
|
||||
return (
|
||||
<DashboardLayout>
|
||||
<DashboardLayout title="Pages">
|
||||
<PagesManager isPost={false} />
|
||||
</DashboardLayout>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { PagesManager } from "~/components/dashboard/PagesManager"
|
|||
|
||||
export default function SubdomainPosts() {
|
||||
return (
|
||||
<DashboardLayout>
|
||||
<DashboardLayout title="Posts">
|
||||
<PagesManager isPost={true} />
|
||||
</DashboardLayout>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -52,9 +52,10 @@ export default function SettingsDomainsPage() {
|
|||
}
|
||||
}, [form, siteResult.data])
|
||||
|
||||
const title = "Site Settings"
|
||||
return (
|
||||
<DashboardLayout>
|
||||
<SettingsLayout title="Site Settings" type="site">
|
||||
<DashboardLayout title={"Domains"}>
|
||||
<SettingsLayout title={"Site Settings"} type="site">
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="">
|
||||
<Input
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ export default function SiteSettingsGeneralPage() {
|
|||
}, [updateSite])
|
||||
|
||||
return (
|
||||
<DashboardLayout>
|
||||
<DashboardLayout title="Site Settings">
|
||||
<SettingsLayout title="Site Settings" type="site">
|
||||
{site && (
|
||||
<div>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { Button } from "~/components/ui/Button"
|
||||
import { Input } from "~/components/ui/Input"
|
||||
import { FormEvent, useEffect, useMemo, useState } from "react"
|
||||
import { ChangeEvent, FormEvent, useEffect, useMemo, useState } from "react"
|
||||
import toast from "react-hot-toast"
|
||||
import { SettingsLayout } from "~/components/dashboard/SettingsLayout"
|
||||
import { useRouter } from "next/router"
|
||||
|
|
@ -40,7 +40,9 @@ const SortableNavigationItem: React.FC<{
|
|||
required
|
||||
id={`${item.id}-label`}
|
||||
value={item.label}
|
||||
onChange={(e) => updateItem(item.id, { label: e.target.value })}
|
||||
onChange={(e: ChangeEvent<HTMLInputElement>) =>
|
||||
updateItem(item.id, { label: e.target.value })
|
||||
}
|
||||
/>
|
||||
<Input
|
||||
label="URL"
|
||||
|
|
@ -50,7 +52,9 @@ const SortableNavigationItem: React.FC<{
|
|||
value={item.url}
|
||||
pattern="(https?://|/).+"
|
||||
title="URL must start with / or http:// or https://"
|
||||
onChange={(e) => updateItem(item.id, { url: e.target.value })}
|
||||
onChange={(e: ChangeEvent<HTMLInputElement>) =>
|
||||
updateItem(item.id, { url: e.target.value })
|
||||
}
|
||||
/>
|
||||
<div className="flex items-end relative -top-[5px]">
|
||||
<Button onClick={() => removeItem(item.id)} variantColor="red">
|
||||
|
|
@ -122,7 +126,7 @@ export default function SiteSettingsNavigationPage() {
|
|||
}, [updateSite, trpcContext, subdomain])
|
||||
|
||||
return (
|
||||
<DashboardLayout>
|
||||
<DashboardLayout title="Navigation">
|
||||
<SettingsLayout title="Site Settings" type="site">
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="bg-zinc-50 rounded-lg overflow-hidden">
|
||||
|
|
|
|||
|
|
@ -3,9 +3,10 @@ import { useRouter } from "next/router"
|
|||
import { useEffect } from "react"
|
||||
import { useForm } from "react-hook-form"
|
||||
import toast from "react-hot-toast"
|
||||
import { SEOHead } from "~/components/common/SEOHead"
|
||||
import { Button } from "~/components/ui/Button"
|
||||
import { Input } from "~/components/ui/Input"
|
||||
import { OUR_DOMAIN } from "~/lib/env"
|
||||
import { APP_NAME, OUR_DOMAIN } from "~/lib/env"
|
||||
import { trpc } from "~/lib/trpc"
|
||||
|
||||
export default function NewSitePage() {
|
||||
|
|
@ -35,62 +36,65 @@ export default function NewSitePage() {
|
|||
}, [createSite, router])
|
||||
|
||||
return (
|
||||
<div>
|
||||
<header className="px-5 text-sm md:px-14 flex justify-between items-start py-10">
|
||||
<Link href="/dashboard">
|
||||
<a className="flex space-x-1 items-center">
|
||||
<svg
|
||||
className="w-4 h-4"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 20 20"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M12.707 5.293a1 1 0 010 1.414L9.414 10l3.293 3.293a1 1 0 01-1.414 1.414l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 0z"
|
||||
clipRule="evenodd"
|
||||
<>
|
||||
<SEOHead title="New Site" siteName={APP_NAME} />
|
||||
<div>
|
||||
<header className="px-5 text-sm md:px-14 flex justify-between items-start py-10">
|
||||
<Link href="/dashboard">
|
||||
<a className="flex space-x-1 items-center">
|
||||
<svg
|
||||
className="w-4 h-4"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 20 20"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M12.707 5.293a1 1 0 010 1.414L9.414 10l3.293 3.293a1 1 0 01-1.414 1.414l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 0z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
<span>Back to dashboard</span>
|
||||
</a>
|
||||
</Link>
|
||||
<div>
|
||||
<div className="text-zinc-400">Logged in as:</div>
|
||||
<div>{viewer.data?.email}</div>
|
||||
</div>
|
||||
</header>
|
||||
<div className="max-w-sm mx-auto mt-20">
|
||||
<h2 className="text-3xl mb-10 text-center">Create a new site</h2>
|
||||
<form className="space-y-5" onSubmit={handleSubmit}>
|
||||
<div>
|
||||
<Input
|
||||
id="name"
|
||||
label="Site Name"
|
||||
isBlock
|
||||
required
|
||||
maxLength={30}
|
||||
{...form.register("name", {})}
|
||||
/>
|
||||
</svg>
|
||||
<span>Back to dashboard</span>
|
||||
</a>
|
||||
</Link>
|
||||
<div>
|
||||
<div className="text-zinc-400">Logged in as:</div>
|
||||
<div>{viewer.data?.email}</div>
|
||||
</div>
|
||||
<div>
|
||||
<Input
|
||||
id="subdomain"
|
||||
label="Subdomain"
|
||||
isBlock
|
||||
required
|
||||
addon={`.${OUR_DOMAIN}`}
|
||||
minLength={3}
|
||||
maxLength={26}
|
||||
{...form.register("subdomain", {})}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Button type="submit" isBlock isLoading={createSite.isLoading}>
|
||||
Create
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</header>
|
||||
<div className="max-w-sm mx-auto mt-20">
|
||||
<h2 className="text-3xl mb-10 text-center">Create a new site</h2>
|
||||
<form className="space-y-5" onSubmit={handleSubmit}>
|
||||
<div>
|
||||
<Input
|
||||
id="name"
|
||||
label="Site Name"
|
||||
isBlock
|
||||
required
|
||||
maxLength={30}
|
||||
{...form.register("name", {})}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Input
|
||||
id="subdomain"
|
||||
label="Subdomain"
|
||||
isBlock
|
||||
required
|
||||
addon={`.${OUR_DOMAIN}`}
|
||||
minLength={3}
|
||||
maxLength={26}
|
||||
{...form.register("subdomain", {})}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Button type="submit" isBlock isLoading={createSite.isLoading}>
|
||||
Create
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,15 @@
|
|||
import { GetServerSideProps } from "next"
|
||||
import { DashboardIcon } from "~/components/icons/DashboardIcon"
|
||||
import { MainLayout } from "~/components/main/MainLayout"
|
||||
import { UniLink } from "~/components/ui/UniLink"
|
||||
import { getAuthUser } from "~/lib/auth.server"
|
||||
import { FLY_REGION } from "~/lib/env.server"
|
||||
import { useStore } from "~/lib/store"
|
||||
|
||||
export const getServerSideProps: GetServerSideProps = async (ctx) => {
|
||||
const user = await getAuthUser(ctx.req)
|
||||
const isLoggedIn = !!user
|
||||
|
||||
return {
|
||||
props: {
|
||||
isLoggedIn,
|
||||
|
|
@ -21,5 +25,47 @@ export default function Home({
|
|||
isLoggedIn: boolean
|
||||
region: string | null
|
||||
}) {
|
||||
return <MainLayout isLoggedIn={isLoggedIn} region={region}></MainLayout>
|
||||
const setLoginModalOpened = useStore((store) => store.setLoginModalOpened)
|
||||
|
||||
return (
|
||||
<MainLayout isLoggedIn={isLoggedIn} region={region}>
|
||||
<section>
|
||||
<div className="max-w-screen-md px-5 mx-auto">
|
||||
<div className="bg-zinc-50 rounded-xl p-10">
|
||||
<h2 className="text-5xl font-extrabold">
|
||||
Content Creating for
|
||||
<br />
|
||||
Absolutely Everyone
|
||||
</h2>
|
||||
<h3 className="mt-5 text-zinc-500">
|
||||
Meet Proselog, the open-source blogging and
|
||||
<br />
|
||||
newsletter platform for everyone. Focus on writing, not
|
||||
distractions.
|
||||
</h3>
|
||||
<div className="mt-10">
|
||||
{isLoggedIn ? (
|
||||
<UniLink
|
||||
href="/dashboard"
|
||||
className="text-indigo-500 inline-flex items-center space-x-2 hover:text-indigo-600"
|
||||
>
|
||||
<DashboardIcon />
|
||||
<span>Dashboard</span>
|
||||
</UniLink>
|
||||
) : (
|
||||
<UniLink
|
||||
onClick={() => {
|
||||
setLoginModalOpened(true)
|
||||
}}
|
||||
className="bg-indigo-500 hover:bg-indigo-600 rounded-full px-4 h-8 inline-flex items-center text-sm text-white font-medium"
|
||||
>
|
||||
Getting Started
|
||||
</UniLink>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</MainLayout>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { z } from "zod"
|
|||
import { prismaPrimary } from "~/lib/db.server"
|
||||
import { sendLoginEmail } from "~/lib/mailgun.server"
|
||||
import { createRouter } from "~/lib/trpc.server"
|
||||
import { getViewer } from "~/lib/viewer"
|
||||
|
||||
export const authRouter = createRouter()
|
||||
.query("viewer", {
|
||||
|
|
@ -17,7 +18,7 @@ export const authRouter = createRouter()
|
|||
})
|
||||
.nullable(),
|
||||
async resolve({ ctx }) {
|
||||
return ctx.user || null
|
||||
return getViewer(ctx.user)
|
||||
},
|
||||
})
|
||||
.mutation("requestLoginLink", {
|
||||
|
|
|
|||
|
|
@ -1,24 +1,18 @@
|
|||
declare interface ServerEnv {
|
||||
declare interface CustomEnv {
|
||||
// Additional environment variables
|
||||
APP_NAME: string
|
||||
NEXT_PUBLIC_APP_NAME: string
|
||||
NEXT_PUBLIC_R2_URL: string
|
||||
|
||||
DATABASE_URL: string
|
||||
AUTH_COOKIE_NAME: string
|
||||
OUR_DOMAIN: string
|
||||
NEXT_PUBLIC_OUR_DOMAIN: string
|
||||
MAILGUN_APIKEY: string
|
||||
MAILGUN_DOMAIN: string
|
||||
FLY_REGION?: string
|
||||
PRIMARY_REGION?: string
|
||||
ENCRYPT_SECRET: string
|
||||
NEXT_PUBLIC_R2_URL: string
|
||||
R2_API_TOKEN: string
|
||||
}
|
||||
|
||||
declare interface BrowserEnv {
|
||||
APP_NAME: string
|
||||
OUR_DOMAIN: string
|
||||
R2_URL: string
|
||||
}
|
||||
|
||||
declare namespace NodeJS {
|
||||
interface ProcessEnv extends ServerEnv {}
|
||||
interface ProcessEnv extends CustomEnv {}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue