This commit is contained in:
Jordi Enric
2024-10-21 00:22:54 +02:00
parent 0a343ab695
commit ff55a8fa15
9 changed files with 294 additions and 531 deletions
+15 -13
View File
@@ -359,19 +359,21 @@ export const ZendoEditor = (props: Props) => {
align="start"
className="-mt-1 max-w-[240px]"
>
{postsQuery.data?.map((post) => (
<DropdownMenuItem key={post.post_id} asChild>
<Link
href={`/blogs/${blogId}/post/${post.slug}`}
className="flex gap-2 px-2 py-1 hover:bg-zinc-100"
>
<span className="text-xs">
{post.published ? "🟢" : "🟠"}
</span>
<span>{post.title}</span>
</Link>
</DropdownMenuItem>
))}
{postsQuery.data?.pages
.flatMap((page) => page.data)
.map((post) => (
<DropdownMenuItem key={post.post_id} asChild>
<Link
href={`/blogs/${blogId}/post/${post.slug}`}
className="flex gap-2 px-2 py-1 hover:bg-zinc-100"
>
<span className="text-xs">
{post.published ? "🟢" : "🟠"}
</span>
<span>{post.title}</span>
</Link>
</DropdownMenuItem>
))}
</DropdownMenuContent>
</DropdownMenu>
)}
+1 -1
View File
@@ -22,7 +22,7 @@ const UserButton = (props: Props) => {
<DropdownMenuTrigger className="flex items-center gap-2 rounded-full">
<div
className={cn(
"flex h-7 w-7 items-center justify-center rounded-full bg-zinc-800 font-bold text-white"
"flex h-7 w-7 items-center justify-center rounded-full bg-gradient-to-b from-slate-700 to-slate-900 font-bold text-white transition-all hover:brightness-125"
)}
>
{user?.email?.slice(0, 1).toUpperCase()}
+24 -19
View File
@@ -77,16 +77,21 @@ export default function AppLayout({
<div className={`flex min-h-screen flex-col border-b bg-zinc-50 font-sans`}>
<TooltipProvider>
<AppChecks>
<nav className="mx-auto w-full bg-white">
<nav
className={cn("mx-auto w-full bg-white", {
"border-b shadow-sm": !selectedBlog,
})}
>
<div className="mx-auto flex h-full max-w-5xl items-center justify-between px-2">
<div className="flex h-full items-center py-3">
{!selectedBlog && (
<Link
tabIndex={-1}
href="/blogs"
className="flex items-center justify-center rounded-xl py-4 text-lg font-medium"
className="flex items-center justify-center rounded-xl px-1 py-4 text-lg font-medium"
>
<ZendoLogo hideText />
<span className="ml-2">zenblog</span>
</Link>
)}
{selectedBlog && (
@@ -155,7 +160,7 @@ export default function AppLayout({
Skip to content
</a>
<div className="flex items-center gap-1">
<div className="flex items-center gap-1 pr-2">
<Feedback />
{/* <Link
className="rounded-full px-3 py-4 text-sm font-medium text-slate-600 hover:text-orange-600"
@@ -170,27 +175,27 @@ export default function AppLayout({
</div>
</div>
</nav>
{selectedBlog && (
<div className="border-b bg-white shadow-sm">
<div className="mx-auto flex max-w-5xl items-center px-2">
{BlogNavItems.map((item) => (
<NavItem
key={item.href}
href={item.href}
selected={item.href === router.asPath}
>
{item.label}
</NavItem>
))}
</div>
</div>
)}
{loading ? (
<div className="flex h-[600px] items-center justify-center">
<Loader className="animate-spin text-orange-500" size={32} />
</div>
) : (
<div className="">
{selectedBlog && (
<div className="border-b bg-white">
<div className="mx-auto flex max-w-5xl items-center px-2">
{BlogNavItems.map((item) => (
<NavItem
key={item.href}
href={item.href}
selected={item.href === router.asPath}
>
{item.label}
</NavItem>
))}
</div>
</div>
)}
<div className="mx-auto flex max-w-5xl items-center justify-between px-4 py-4">
<SectionTitle>{title}</SectionTitle>
<SectionActions>{actions}</SectionActions>
@@ -227,7 +232,7 @@ function NavItem({
<Link
href={href}
className={cn(
`relative flex items-center gap-1 px-2 py-2 text-sm font-medium text-zinc-600 md:px-2 md:py-1`,
`relative flex items-center gap-1 rounded-md px-2 py-2 text-sm font-medium text-zinc-600 hover:bg-zinc-50 md:px-2 md:py-1`,
{
"text-zinc-950": selected,
"after:absolute after:inset-x-0 after:bottom-[-1px] after:z-10 after:h-[2px] after:w-full after:bg-orange-500 after:content-['']":
+139 -20
View File
@@ -1,3 +1,4 @@
import { ConfirmDialog } from "@/components/confirm-dialog";
import { Button } from "@/components/ui/button";
import {
Dialog,
@@ -6,6 +7,12 @@ import {
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import {
@@ -18,8 +25,14 @@ import {
} from "@/components/ui/table";
import { useBlogId } from "@/hooks/use-blog-id";
import AppLayout, { Section } from "@/layouts/AppLayout";
import { useCategories, useCreateCategory } from "@/queries/categories";
import { Plus } from "lucide-react";
import {
useCategories,
useCategoriesWithPostCount,
useCreateCategory,
useDeleteCategoryMutation,
useUpdateCategoryMutation,
} from "@/queries/categories";
import { MoreHorizontal, Plus } from "lucide-react";
import { useState } from "react";
import { toast } from "sonner";
@@ -42,19 +55,24 @@ export function CreateCategoryDialog() {
</DialogHeader>
<form
className="[&_input]:mb-3 [&_input]:mt-1"
onSubmit={(e) => {
onSubmit={async (e) => {
e.preventDefault();
e.stopPropagation();
const formData = new FormData(e.target as HTMLFormElement);
const name = formData.get("name");
const slug = formData.get("slug");
createCategory.mutate({
name: name as string,
slug: slug as string,
blog_id: blogId,
});
toast.success("Category created");
setOpen(false);
try {
await createCategory.mutateAsync({
name: name as string,
slug: slug as string,
blog_id: blogId,
});
toast.success("Category created");
setOpen(false);
} catch (error) {
toast.error("Failed to create category. Slugs must be unique.");
}
}}
>
<Label htmlFor="name">Name</Label>
@@ -71,12 +89,25 @@ export function CreateCategoryDialog() {
}
export default function CategoriesPage() {
const categories = useCategories();
const blogId = useBlogId();
const { data: categories, isLoading } = useCategoriesWithPostCount();
const [selectedCategory, setSelectedCategory] = useState<{
category_id: number | null;
category_name: string | null;
category_slug: string | null;
} | null>(null);
const [updateCategoryOpen, setUpdateCategoryOpen] = useState(false);
const updateCategory = useUpdateCategoryMutation();
const [deleteCategoryOpen, setDeleteCategoryOpen] = useState(false);
const deleteCategory = useDeleteCategoryMutation(blogId);
return (
<AppLayout
title="Categories"
loading={categories.isLoading}
loading={isLoading}
actions={<CreateCategoryDialog />}
>
<Section>
@@ -85,21 +116,109 @@ export default function CategoriesPage() {
<TableRow>
<TableHead>Category</TableHead>
<TableHead>Slug</TableHead>
{/* <TableHead>Posts</TableHead> */}
<TableHead></TableHead>
<TableHead className="text-right">Posts with category</TableHead>
<TableHead className="text-right">
<div className="sr-only">Action</div>
</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{categories.data?.map((category) => (
<TableRow key={category.id}>
<TableCell>{category.name}</TableCell>
<TableCell>{category.slug}</TableCell>
{/* <TableCell>{category.post_count}</TableCell> */}
<TableCell></TableCell>
{categories?.data?.map((category) => (
<TableRow key={category.category_id}>
<TableCell>{category.category_name}</TableCell>
<TableCell>{category.category_slug}</TableCell>
<TableCell className="text-right">
{category.post_count}
</TableCell>
<TableCell className="text-right">
<DropdownMenu modal={false}>
<DropdownMenuTrigger asChild>
<Button variant="ghost" size="icon">
<MoreHorizontal size={16} />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem
onClick={() => {
setSelectedCategory(category);
setUpdateCategoryOpen(true);
}}
>
Edit
</DropdownMenuItem>
<DropdownMenuItem
onClick={() => {
setSelectedCategory(category);
setDeleteCategoryOpen(true);
}}
>
Delete
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
<Dialog open={updateCategoryOpen} onOpenChange={setUpdateCategoryOpen}>
<DialogContent className="!max-w-sm">
<DialogHeader>
<DialogTitle>Edit category</DialogTitle>
</DialogHeader>
<form
onSubmit={async (e) => {
e.preventDefault();
e.stopPropagation();
const formData = new FormData(e.target as HTMLFormElement);
const name = formData.get("name");
const slug = formData.get("slug");
if (!selectedCategory?.category_id) return;
await updateCategory.mutateAsync({
id: selectedCategory.category_id,
name: name as string,
slug: slug as string,
});
toast.success("Category updated");
setUpdateCategoryOpen(false);
}}
>
<Label htmlFor="name">Name</Label>
<Input
defaultValue={selectedCategory?.category_name ?? ""}
id="name"
name="name"
placeholder="Technology"
/>
<Label className="mt-4" htmlFor="slug">
Slug
</Label>
<Input
defaultValue={selectedCategory?.category_slug ?? ""}
id="slug"
name="slug"
placeholder="technology"
/>
<div className="mt-4 flex justify-end">
<Button type="submit">Update</Button>
</div>
</form>
</DialogContent>
<ConfirmDialog
open={deleteCategoryOpen}
onOpenChange={setDeleteCategoryOpen}
onConfirm={() => {
if (!selectedCategory?.category_id) return;
deleteCategory.mutate(selectedCategory.category_id.toString());
toast.success("Category deleted");
setDeleteCategoryOpen(false);
}}
/>
</Dialog>
</Section>
</AppLayout>
);
+3 -3
View File
@@ -31,7 +31,7 @@ export default function MediaPage() {
{selectedImages.length > 0 && (
<>
<Button
variant="outline"
variant="destructive"
onClick={() => {
setConfirmDeleteDialogOpen(true);
}}
@@ -73,9 +73,9 @@ export default function MediaPage() {
)}
<Dialog open={showUploadDialog} onOpenChange={setShowUploadDialog}>
<DialogTrigger asChild>
<Button variant="ghost">
<Button variant="outline">
<Upload size="16" />
Upload
Upload media
</Button>
</DialogTrigger>
<DialogContent className="md:max-w-sm">
-130
View File
@@ -124,136 +124,6 @@ export default function BlogPosts() {
</>
}
>
{/* <Tabs
value={tabValue || "posts"}
onValueChange={(tabVal) => {
onTabChange(tabVal);
}}
>
<div className="flex items-center justify-between">
<TabsList className="w-full items-center">
<TabsTrigger value="posts">Posts</TabsTrigger>
<TabsTrigger onClick={() => {}} value="media">
Media
</TabsTrigger>
<TabsTrigger onClick={() => {}} value="categories">
Categories
</TabsTrigger>
<TabsTrigger onClick={() => {}} value="tags">
Tags
</TabsTrigger>
<TabsTrigger
onClick={() => {
router.push(`/blogs/${blogId}/settings`);
}}
value="settings"
>
Settings
</TabsTrigger>
</TabsList>
</div>
<TabsContent value="tags">
<TabSection
title={tags.data?.length ? `${tags.data?.length} tags` : "Tags"}
actions={<></>}
>
<>
{tags.isLoading && (
<div className="p-12">
<Spinner />
</div>
)}
{tags.data?.length === 0 && (
<div className="p-12 py-32 text-center">
<div className="text-2xl">🏷️</div>
<div className="text-lg text-zinc-500">
Nothing here yet
</div>
</div>
)}
<div className="grid divide-y">
{tags.data?.length ? (
<div className="grid grid-cols-4 items-center p-2 text-sm font-medium text-zinc-600">
<div>Tag</div>
<div>Slug</div>
<div className="text-right">Posts with tag</div>
<div></div>
</div>
) : null}
{tags.data?.map((tag) => {
return (
<div
key={tag.tag_id}
className="grid grid-cols-4 items-center px-2 py-1.5 hover:bg-zinc-50"
>
<div className="flex items-center gap-2">
<PiTag className="text-orange-500" size="16" />
<span className="font-medium">{tag.tag_name}</span>
</div>
<div className="flex flex-col">
<p className="font-mono text-sm text-zinc-500">
{tag.slug}
</p>
</div>
<div className="text-right font-mono">
{tag.post_count}
</div>
<div className="flex items-center justify-end">
<DropdownMenu modal={false}>
<DropdownMenuTrigger asChild>
<Button size="icon" variant={"ghost"}>
<MoreVertical size="16" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem
onClick={() => {
if (!tag) {
return;
}
setSelectedTag(tag);
setUpdateTagDialogOpen(true);
}}
>
<Pen size="16" className="mr-2" />
Edit
</DropdownMenuItem>
<DropdownMenuItem
onClick={() => {
if (!tag) {
return;
}
setSelectedTag(tag);
setDeleteTagDialogOpen(true);
}}
>
<Trash size="16" className="mr-2" />
Delete
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
</div>
);
})}
</div>
</>
</TabSection>
</TabsContent>
<TabsContent asChild value="categories">
<TabSection title="Categories" actions={<></>}>
<CategoriesPage />
</TabSection>
</TabsContent>
</Tabs> */}
<Section>
{posts.pages.length === 0 && (
<div className="p-12 py-32 text-center">
+14 -14
View File
@@ -20,20 +20,20 @@ export default function Dashboard() {
const [hovering, setHovering] = useState("");
return (
<AppLayout loading={isLoading}>
<div className="mt-8 min-h-screen">
<AppLayout
loading={isLoading}
title="My blogs"
actions={
<Button variant="outline" asChild>
<Link href="/blogs/create">
<Plus />
Create blog
</Link>
</Button>
}
>
<div className="min-h-screen">
<div className="mx-auto max-w-5xl">
<div className="flex items-center justify-between p-3">
<h1 className="text-lg font-medium">My blogs</h1>
<div>
<Button variant="outline" asChild>
<Link href="/blogs/create">
<Plus />
Create blog
</Link>
</Button>
</div>
</div>
{data?.length === 0 && (
<div className="flex flex-col items-center justify-center py-12 text-center">
<div className="rounded-xl border bg-white p-2 shadow-sm">
@@ -48,7 +48,7 @@ export default function Dashboard() {
</Button>
</div>
)}
<ul className="mx-2 grid grid-cols-1 gap-4 sm:grid-cols-2">
<ul className="grid grid-cols-1 gap-4 sm:grid-cols-2">
{data?.map((blog) => {
return (
<li
+69 -1
View File
@@ -11,13 +11,26 @@ type Category = {
blog_id: string;
};
export function useCategoriesWithPostCount() {
return useQuery({
queryKey: ["categories-with-post-count"],
queryFn: async () =>
await sb
.from("category_post_count")
.select(
"category_id, category_name, category_slug, post_count, created_at"
)
.throwOnError(),
});
}
export function useCategories() {
return useQuery({
queryKey: ["categories"],
queryFn: async () => {
const { data, error } = await sb
.from("categories")
.select("*")
.select("id, slug, name, created_at")
.throwOnError();
if (error) {
throw error;
@@ -37,3 +50,58 @@ export function useCreateCategory() {
},
});
}
export function useDeleteCategoryMutation(blogId: string) {
const queryClient = useQueryClient();
const supa = createSupabaseBrowserClient();
return useMutation({
mutationFn: async (categoryId: string) => {
const res = await supa
.from("categories")
.delete()
.eq("id", categoryId)
.eq("blog_id", blogId);
if (res.error) {
throw new Error(res.error.message);
}
return res;
},
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: ["categories-with-post-count"],
});
},
});
}
export function useUpdateCategoryMutation() {
const queryClient = useQueryClient();
const supa = createSupabaseBrowserClient();
return useMutation({
mutationFn: async (category: {
id: number;
name: string;
slug: string;
}) => {
const res = await supa
.from("categories")
.update(category)
.eq("id", category.id);
if (res.error) {
throw new Error(res.error.message);
}
return res;
},
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: ["categories-with-post-count"],
});
},
});
}
+29 -330
View File
@@ -96,15 +96,7 @@ export type Database = {
user_id?: string
website?: string
}
Relationships: [
{
foreignKeyName: "blogs_user_id_fkey"
columns: ["user_id"]
isOneToOne: false
referencedRelation: "users"
referencedColumns: ["id"]
},
]
Relationships: []
}
categories: {
Row: {
@@ -237,13 +229,6 @@ export type Database = {
referencedRelation: "posts_v5"
referencedColumns: ["post_id"]
},
{
foreignKeyName: "post_tags_post_id_fkey"
columns: ["post_id"]
isOneToOne: false
referencedRelation: "posts_with_blog_and_subscription_status"
referencedColumns: ["post_id"]
},
{
foreignKeyName: "post_tags_post_id_fkey"
columns: ["post_id"]
@@ -251,41 +236,6 @@ export type Database = {
referencedRelation: "posts_with_blog_and_subscription_status_v2"
referencedColumns: ["post_id"]
},
{
foreignKeyName: "post_tags_post_id_fkey"
columns: ["post_id"]
isOneToOne: false
referencedRelation: "posts_with_tags"
referencedColumns: ["post_id"]
},
{
foreignKeyName: "post_tags_post_id_fkey"
columns: ["post_id"]
isOneToOne: false
referencedRelation: "posts_with_tags_v2"
referencedColumns: ["post_id"]
},
{
foreignKeyName: "post_tags_post_id_fkey"
columns: ["post_id"]
isOneToOne: false
referencedRelation: "posts_with_tags_v3"
referencedColumns: ["post_id"]
},
{
foreignKeyName: "post_tags_post_id_fkey"
columns: ["post_id"]
isOneToOne: false
referencedRelation: "public_posts_v2"
referencedColumns: ["post_id"]
},
{
foreignKeyName: "post_tags_post_id_fkey"
columns: ["post_id"]
isOneToOne: false
referencedRelation: "public_posts_v3"
referencedColumns: ["post_id"]
},
{
foreignKeyName: "post_tags_tag_id_fkey"
columns: ["tag_id"]
@@ -373,11 +323,11 @@ export type Database = {
referencedColumns: ["id"]
},
{
foreignKeyName: "public_posts_user_id_fkey"
columns: ["user_id"]
foreignKeyName: "posts_category_id_fkey"
columns: ["category_id"]
isOneToOne: false
referencedRelation: "users"
referencedColumns: ["id"]
referencedRelation: "category_post_count"
referencedColumns: ["category_id"]
},
]
}
@@ -445,15 +395,7 @@ export type Database = {
subscription?: Json
user_id?: string
}
Relationships: [
{
foreignKeyName: "subscriptions_user_id_fkey"
columns: ["user_id"]
isOneToOne: true
referencedRelation: "users"
referencedColumns: ["id"]
},
]
Relationships: []
}
teams: {
Row: {
@@ -477,114 +419,19 @@ export type Database = {
owner_id?: string | null
ref?: string | null
}
Relationships: [
{
foreignKeyName: "public_teams_owner_id_fkey"
columns: ["owner_id"]
isOneToOne: false
referencedRelation: "users"
referencedColumns: ["id"]
},
]
Relationships: []
}
}
Views: {
post_tags_with_tags: {
category_post_count: {
Row: {
category_id: number | null
category_name: string | null
category_slug: string | null
created_at: string | null
id: number | null
post_id: string | null
tag_description: string | null
tag_id: string | null
tag_name: string | null
tag_slug: string | null
post_count: number | null
}
Relationships: [
{
foreignKeyName: "post_tags_post_id_fkey"
columns: ["post_id"]
isOneToOne: false
referencedRelation: "posts"
referencedColumns: ["id"]
},
{
foreignKeyName: "post_tags_post_id_fkey"
columns: ["post_id"]
isOneToOne: false
referencedRelation: "posts_v4"
referencedColumns: ["post_id"]
},
{
foreignKeyName: "post_tags_post_id_fkey"
columns: ["post_id"]
isOneToOne: false
referencedRelation: "posts_v5"
referencedColumns: ["post_id"]
},
{
foreignKeyName: "post_tags_post_id_fkey"
columns: ["post_id"]
isOneToOne: false
referencedRelation: "posts_with_blog_and_subscription_status"
referencedColumns: ["post_id"]
},
{
foreignKeyName: "post_tags_post_id_fkey"
columns: ["post_id"]
isOneToOne: false
referencedRelation: "posts_with_blog_and_subscription_status_v2"
referencedColumns: ["post_id"]
},
{
foreignKeyName: "post_tags_post_id_fkey"
columns: ["post_id"]
isOneToOne: false
referencedRelation: "posts_with_tags"
referencedColumns: ["post_id"]
},
{
foreignKeyName: "post_tags_post_id_fkey"
columns: ["post_id"]
isOneToOne: false
referencedRelation: "posts_with_tags_v2"
referencedColumns: ["post_id"]
},
{
foreignKeyName: "post_tags_post_id_fkey"
columns: ["post_id"]
isOneToOne: false
referencedRelation: "posts_with_tags_v3"
referencedColumns: ["post_id"]
},
{
foreignKeyName: "post_tags_post_id_fkey"
columns: ["post_id"]
isOneToOne: false
referencedRelation: "public_posts_v2"
referencedColumns: ["post_id"]
},
{
foreignKeyName: "post_tags_post_id_fkey"
columns: ["post_id"]
isOneToOne: false
referencedRelation: "public_posts_v3"
referencedColumns: ["post_id"]
},
{
foreignKeyName: "post_tags_tag_id_fkey"
columns: ["tag_id"]
isOneToOne: false
referencedRelation: "blog_tags"
referencedColumns: ["id"]
},
{
foreignKeyName: "post_tags_tag_id_fkey"
columns: ["tag_id"]
isOneToOne: false
referencedRelation: "tag_usage_count_v2"
referencedColumns: ["tag_id"]
},
]
Relationships: []
}
posts_v4: {
Row: {
@@ -648,32 +495,6 @@ export type Database = {
},
]
}
posts_with_blog_and_subscription_status: {
Row: {
blog_id: string | null
content: Json | null
cover_image: string | null
created_at: string | null
deleted: boolean | null
metadata: Json[] | null
post_id: string | null
published: boolean | null
slug: string | null
subscription_status: string | null
tags: string[] | null
title: string | null
updated_at: string | null
}
Relationships: [
{
foreignKeyName: "posts_blog_id_fkey"
columns: ["blog_id"]
isOneToOne: false
referencedRelation: "blogs"
referencedColumns: ["id"]
},
]
}
posts_with_blog_and_subscription_status_v2: {
Row: {
blog_id: string | null
@@ -701,143 +522,6 @@ export type Database = {
},
]
}
posts_with_tags: {
Row: {
blog_id: string | null
content: Json | null
cover_image: string | null
created_at: string | null
deleted: boolean | null
metadata: Json[] | null
post_id: string | null
published: boolean | null
slug: string | null
tags: string[] | null
title: string | null
updated_at: string | null
}
Relationships: [
{
foreignKeyName: "posts_blog_id_fkey"
columns: ["blog_id"]
isOneToOne: false
referencedRelation: "blogs"
referencedColumns: ["id"]
},
]
}
posts_with_tags_v2: {
Row: {
blog_id: string | null
content: Json | null
cover_image: string | null
created_at: string | null
deleted: boolean | null
metadata: Json[] | null
post_id: string | null
published: boolean | null
published_at: string | null
slug: string | null
tags: string[] | null
title: string | null
updated_at: string | null
}
Relationships: [
{
foreignKeyName: "posts_blog_id_fkey"
columns: ["blog_id"]
isOneToOne: false
referencedRelation: "blogs"
referencedColumns: ["id"]
},
]
}
posts_with_tags_v3: {
Row: {
abstract: string | null
blog_id: string | null
content: Json | null
cover_image: string | null
created_at: string | null
deleted: boolean | null
metadata: Json[] | null
post_id: string | null
published: boolean | null
published_at: string | null
slug: string | null
tags: string[] | null
title: string | null
updated_at: string | null
}
Relationships: [
{
foreignKeyName: "posts_blog_id_fkey"
columns: ["blog_id"]
isOneToOne: false
referencedRelation: "blogs"
referencedColumns: ["id"]
},
]
}
public_posts_v2: {
Row: {
blog_id: string | null
blog_slug: string | null
content: Json | null
cover_image: string | null
created_at: string | null
deleted: boolean | null
html_content: string | null
metadata: Json[] | null
post_id: string | null
published: boolean | null
published_at: string | null
slug: string | null
subscription_status: string | null
tags: string[] | null
title: string | null
updated_at: string | null
}
Relationships: [
{
foreignKeyName: "posts_blog_id_fkey"
columns: ["blog_id"]
isOneToOne: false
referencedRelation: "blogs"
referencedColumns: ["id"]
},
]
}
public_posts_v3: {
Row: {
abstract: string | null
blog_id: string | null
blog_slug: string | null
content: Json | null
cover_image: string | null
created_at: string | null
deleted: boolean | null
html_content: string | null
metadata: Json[] | null
post_id: string | null
published: boolean | null
published_at: string | null
slug: string | null
subscription_status: string | null
tags: string[] | null
title: string | null
updated_at: string | null
}
Relationships: [
{
foreignKeyName: "posts_blog_id_fkey"
columns: ["blog_id"]
isOneToOne: false
referencedRelation: "blogs"
referencedColumns: ["id"]
},
]
}
tag_usage_count_v2: {
Row: {
blog_id: string | null
@@ -879,7 +563,7 @@ export type Database = {
}
Returns: {
title: string
html_content: Json
html_content: string
slug: string
category_name: string
category_slug: string
@@ -998,3 +682,18 @@ export type Enums<
: PublicEnumNameOrOptions extends keyof PublicSchema["Enums"]
? PublicSchema["Enums"][PublicEnumNameOrOptions]
: never
export type CompositeTypes<
PublicCompositeTypeNameOrOptions extends
| keyof PublicSchema["CompositeTypes"]
| { schema: keyof Database },
CompositeTypeName extends PublicCompositeTypeNameOrOptions extends {
schema: keyof Database
}
? keyof Database[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"]
: never = never,
> = PublicCompositeTypeNameOrOptions extends { schema: keyof Database }
? Database[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"][CompositeTypeName]
: PublicCompositeTypeNameOrOptions extends keyof PublicSchema["CompositeTypes"]
? PublicSchema["CompositeTypes"][PublicCompositeTypeNameOrOptions]
: never