From 6a2bcaff162bad5db565400fb906366a3c87907b Mon Sep 17 00:00:00 2001 From: Jordi Enric Date: Sun, 31 Mar 2024 17:02:17 +0200 Subject: [PATCH] wip --- apps/zendo/package.json | 1 + .../src/components/Editor/EditorSettings.tsx | 176 ++++++- .../src/components/Editor/ZendoEditor.tsx | 2 +- apps/zendo/src/components/UserButton.tsx | 11 +- apps/zendo/src/components/ZendoLogo.tsx | 2 +- apps/zendo/src/components/is-dev-mode.tsx | 18 + apps/zendo/src/components/ui/button.tsx | 2 +- apps/zendo/src/components/ui/select.tsx | 158 ++++++ apps/zendo/src/pages/account/index.tsx | 13 + apps/zendo/src/pages/blogs/[blogId]/posts.tsx | 4 +- apps/zendo/src/queries/subscription.ts | 2 +- demos/nextjs/app/blog/[slug]/page.tsx | 9 +- demos/nextjs/app/layout.tsx | 10 +- demos/nextjs/app/page.tsx | 14 +- package-lock.json | 468 +++++++++++++++++- package.json | 3 +- ...emote_schema.sql => 20240328172813_v1.sql} | 55 ++ ...s_with_blog_and_subscription_status_v2.sql | 27 + .../20240331095320_remote_schema.sql | 5 + .../migrations/20240331095515_testfix.sql | 27 + .../20240331150057_post_with_tags_v2.sql | 25 + supabase/seed.sql | 298 ----------- 22 files changed, 959 insertions(+), 371 deletions(-) create mode 100644 apps/zendo/src/components/is-dev-mode.tsx create mode 100644 apps/zendo/src/components/ui/select.tsx rename supabase/migrations/{20240313224531_remote_schema.sql => 20240328172813_v1.sql} (90%) create mode 100644 supabase/migrations/20240331095233_posts_with_blog_and_subscription_status_v2.sql create mode 100644 supabase/migrations/20240331095320_remote_schema.sql create mode 100644 supabase/migrations/20240331095515_testfix.sql create mode 100644 supabase/migrations/20240331150057_post_with_tags_v2.sql diff --git a/apps/zendo/package.json b/apps/zendo/package.json index b406392..6f22168 100644 --- a/apps/zendo/package.json +++ b/apps/zendo/package.json @@ -19,6 +19,7 @@ "@radix-ui/react-dropdown-menu": "^2.0.6", "@radix-ui/react-label": "^2.0.2", "@radix-ui/react-popover": "1.0.5", + "@radix-ui/react-select": "^2.0.0", "@radix-ui/react-slot": "^1.0.2", "@radix-ui/react-switch": "^1.0.2", "@radix-ui/react-tabs": "^1.0.4", diff --git a/apps/zendo/src/components/Editor/EditorSettings.tsx b/apps/zendo/src/components/Editor/EditorSettings.tsx index 1a344f7..10df66a 100644 --- a/apps/zendo/src/components/Editor/EditorSettings.tsx +++ b/apps/zendo/src/components/Editor/EditorSettings.tsx @@ -1,5 +1,5 @@ /* eslint-disable @next/next/no-img-element */ -import React, { useState } from "react"; +import React, { useEffect, useState } from "react"; import { Input } from "../ui/input"; import { Button } from "../ui/button"; import { Code, Info, Plus, Trash } from "lucide-react"; @@ -12,6 +12,13 @@ import "react-calendar/dist/Calendar.css"; import "react-clock/dist/Clock.css"; import Image from "next/image"; import { CopyCell } from "../copy-cell"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "../ui/select"; type MetadataItem = { key: string; @@ -45,19 +52,55 @@ const EditorSettings = (props: Props) => { props.selectedTags || [] ); const today = new Date().toISOString().split("T")[0] || ""; - const [publishedAt, setPublishedAt] = useState( - props.published_at || today - ); - function formatPublishedDate() { + function formatDate(date: string) { try { - const date = new Date(publishedAt).toISOString().split(".")[0]; - return date; + return new Date(date).toISOString().split(".")[0]; } catch (error) { + toast.error("Invalid date"); // do nothing } } - const publishedAtValue = formatPublishedDate(); + + const [publishedAt, setPublishedAt] = useState<{ + day: number; + month: number; + year: number; + hour: number; + minute: number; + }>({ + day: new Date(props.published_at || today).getDate(), + month: new Date(props.published_at || today).getMonth(), + year: new Date(props.published_at || today).getFullYear(), + hour: new Date(props.published_at || today).getHours(), + minute: new Date(props.published_at || today).getMinutes(), + }); + + function publishedAtToDate() { + return new Date( + publishedAt.year, + publishedAt.month, + publishedAt.day, + publishedAt.hour, + publishedAt.minute + ).toISOString(); + } + + useEffect(() => { + try { + const date = publishedAtToDate(); + + props.onChange({ + tags: selectedTags, + metadata, + published_at: date, + }); + } catch (error) { + toast.error("Invalid date"); + } + }, [publishedAt]); + + const publishedAtValue = formatDate(publishedAtToDate()); function addMetadata() { setMetadata([...metadata, { key: "", value: "" }]); @@ -90,22 +133,107 @@ const EditorSettings = (props: Props) => {

Published date

- false} - value={new Date(publishedAt)} - onChange={(date) => { - if (!date) return; - setPublishedAt(date.toISOString()); - props.onChange({ - tags: selectedTags, - metadata, - published_at: date.toISOString(), - }); - }} - /> +
+
+ +
+ { + setPublishedAt({ + ...publishedAt, + day: parseInt(e.target.value), + }); + }} + /> + + { + setPublishedAt({ + ...publishedAt, + year: parseInt(e.target.value), + }); + }} + /> +
+
+
+ +
+ { + setPublishedAt({ + ...publishedAt, + hour: parseInt(e.target.value), + }); + }} + /> + { + setPublishedAt({ + ...publishedAt, + minute: parseInt(e.target.value), + }); + }} + /> +
+
+
{/* { slug: data.slug, cover_image: data.cover_image || "", published: data.published, - published_at: publishedAt, + published_at: publishedAt || new Date().toISOString(), metadata, tags, }); diff --git a/apps/zendo/src/components/UserButton.tsx b/apps/zendo/src/components/UserButton.tsx index 0895504..f966b52 100644 --- a/apps/zendo/src/components/UserButton.tsx +++ b/apps/zendo/src/components/UserButton.tsx @@ -8,17 +8,24 @@ import { } from "./ui/dropdown-menu"; import Link from "next/link"; import { useUser } from "@/utils/supabase/browser"; +import { useIsSubscribed, useSubscriptionQuery } from "@/queries/subscription"; +import { cn } from "@/lib/utils"; type Props = {}; const UserButton = (props: Props) => { const user = useUser(); + const isSubbed = useIsSubscribed(); return ( <> - -
+ +
{user?.email?.slice(0, 1).toUpperCase()}
diff --git a/apps/zendo/src/components/ZendoLogo.tsx b/apps/zendo/src/components/ZendoLogo.tsx index 0de8b5b..2581ff5 100644 --- a/apps/zendo/src/components/ZendoLogo.tsx +++ b/apps/zendo/src/components/ZendoLogo.tsx @@ -7,5 +7,5 @@ const Logo = () => ( ); export default function ZendoLogo() { - return
zenblog
; + return
zenblog
; } diff --git a/apps/zendo/src/components/is-dev-mode.tsx b/apps/zendo/src/components/is-dev-mode.tsx new file mode 100644 index 0000000..3796505 --- /dev/null +++ b/apps/zendo/src/components/is-dev-mode.tsx @@ -0,0 +1,18 @@ +import React, { PropsWithChildren } from "react"; + +export const IsDevMode = ({ children }: PropsWithChildren) => { + const isDev = process.env.NODE_ENV === "development"; + + if (!isDev) { + return null; + } + + return ( +
+ + Development Tip: + + {children} +
+ ); +}; diff --git a/apps/zendo/src/components/ui/button.tsx b/apps/zendo/src/components/ui/button.tsx index 1153e9a..ee71d9b 100644 --- a/apps/zendo/src/components/ui/button.tsx +++ b/apps/zendo/src/components/ui/button.tsx @@ -23,7 +23,7 @@ const buttonVariants = cva( }, size: { default: "h-10 px-3 py-2", - sm: "h-8 px-3 text-xs", + sm: "h-[30px] px-3 text-xs", lg: "h-11 px-8", icon: "h-8 w-8 text-zinc-500 dark:text-zinc-50 [&>svg]:w-[18px]", }, diff --git a/apps/zendo/src/components/ui/select.tsx b/apps/zendo/src/components/ui/select.tsx new file mode 100644 index 0000000..6a8f034 --- /dev/null +++ b/apps/zendo/src/components/ui/select.tsx @@ -0,0 +1,158 @@ +import * as React from "react"; +import * as SelectPrimitive from "@radix-ui/react-select"; +import { Check, ChevronDown, ChevronUp } from "lucide-react"; + +import { cn } from "@/lib/utils"; + +const Select = SelectPrimitive.Root; + +const SelectGroup = SelectPrimitive.Group; + +const SelectValue = SelectPrimitive.Value; + +const SelectTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + span]:line-clamp-1", + className + )} + {...props} + > + {children} + + + + +)); +SelectTrigger.displayName = SelectPrimitive.Trigger.displayName; + +const SelectScrollUpButton = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + +)); +SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName; + +const SelectScrollDownButton = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + +)); +SelectScrollDownButton.displayName = + SelectPrimitive.ScrollDownButton.displayName; + +const SelectContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, position = "popper", ...props }, ref) => ( + + + + + {children} + + + + +)); +SelectContent.displayName = SelectPrimitive.Content.displayName; + +const SelectLabel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +SelectLabel.displayName = SelectPrimitive.Label.displayName; + +const SelectItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + + + + + + {children} + +)); +SelectItem.displayName = SelectPrimitive.Item.displayName; + +const SelectSeparator = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +SelectSeparator.displayName = SelectPrimitive.Separator.displayName; + +export { + Select, + SelectGroup, + SelectValue, + SelectTrigger, + SelectContent, + SelectLabel, + SelectItem, + SelectSeparator, + SelectScrollUpButton, + SelectScrollDownButton, +}; diff --git a/apps/zendo/src/pages/account/index.tsx b/apps/zendo/src/pages/account/index.tsx index 6788909..d816f15 100644 --- a/apps/zendo/src/pages/account/index.tsx +++ b/apps/zendo/src/pages/account/index.tsx @@ -1,3 +1,4 @@ +import { IsDevMode } from "@/components/is-dev-mode"; import { Button } from "@/components/ui/button"; import { Tabs, TabsTrigger, TabsList } from "@/components/ui/tabs"; import AppLayout from "@/layouts/AppLayout"; @@ -107,6 +108,18 @@ export const SubscribeSection = () => {

Pricing

Cancel anytime

+ +
+          Run this to sync stripe with the local database:
+          
+ `npm run stripe:webhook` // listen for stripe events +
+ `npm run stripe:sync` // sync stripe products and prices +
+ Then refresh the page and subscribe to a plan +
+
+
-

+

{blog.emoji} {blog.title}

@@ -153,7 +153,7 @@ export default function BlogPosts() {
-

+

{post.title}

diff --git a/apps/zendo/src/queries/subscription.ts b/apps/zendo/src/queries/subscription.ts index 5e34cf2..b79d656 100644 --- a/apps/zendo/src/queries/subscription.ts +++ b/apps/zendo/src/queries/subscription.ts @@ -14,7 +14,7 @@ export function useSubscriptionQuery() { .select("*") .limit(1); - if (error) { + if (error || !data[0]) { console.error(error); return { status: "inactive", diff --git a/demos/nextjs/app/blog/[slug]/page.tsx b/demos/nextjs/app/blog/[slug]/page.tsx index 05d52a1..3769d9c 100644 --- a/demos/nextjs/app/blog/[slug]/page.tsx +++ b/demos/nextjs/app/blog/[slug]/page.tsx @@ -16,13 +16,16 @@ export default async function Home({
{post.cover_image && ( {post.title} )} -
-

{post.title}

+
+ + Back to blog + +

{post.title}

{JSON.stringify(post.content)}
diff --git a/demos/nextjs/app/layout.tsx b/demos/nextjs/app/layout.tsx index a6a7e6d..2d3d99f 100644 --- a/demos/nextjs/app/layout.tsx +++ b/demos/nextjs/app/layout.tsx @@ -1,10 +1,10 @@ import type { Metadata } from "next"; -import { IBM_Plex_Mono } from "next/font/google"; +import { Inter } from "next/font/google"; import "./globals.css"; import Link from "next/link"; -const ibmPlexMono = IBM_Plex_Mono({ - weight: ["500", "600"], +const inter = Inter({ + weight: ["400", "500", "600"], subsets: ["latin"], }); @@ -20,8 +20,8 @@ export default function RootLayout({ }) { return ( - -