published toggle
This commit is contained in:
@@ -2,22 +2,7 @@ import { Show } from "solid-js";
|
||||
import CardLinks from "./CardLinks";
|
||||
import DeletePostButton from "./DeletePostButton";
|
||||
import { Fire } from "~/components/icons/Fire";
|
||||
|
||||
export interface Post {
|
||||
id: number;
|
||||
title: string;
|
||||
subtitle: string | null;
|
||||
body: string | null;
|
||||
banner_photo: string | null;
|
||||
date: string;
|
||||
published: boolean;
|
||||
category: string;
|
||||
author_id: string;
|
||||
reads: number;
|
||||
attachments: string | null;
|
||||
total_likes: number;
|
||||
total_comments: number;
|
||||
}
|
||||
import { Post } from "~/db/types";
|
||||
|
||||
export interface CardProps {
|
||||
post: Post;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { For, Show, createMemo } from "solid-js";
|
||||
import Card, { Post } from "./Card";
|
||||
import Card from "./Card";
|
||||
import { Post } from "~/db/types";
|
||||
|
||||
export interface Tag {
|
||||
value: string;
|
||||
@@ -13,10 +14,22 @@ export interface PostSortingProps {
|
||||
filters?: string;
|
||||
sort?: string;
|
||||
include?: string;
|
||||
status?: string;
|
||||
}
|
||||
|
||||
export default function PostSorting(props: PostSortingProps) {
|
||||
const filteredPosts = createMemo(() => {
|
||||
let filtered = props.posts;
|
||||
|
||||
// Apply publication status filter (admin only)
|
||||
if (props.privilegeLevel === "admin" && props.status) {
|
||||
if (props.status === "published") {
|
||||
filtered = filtered.filter((post) => post.published === 1);
|
||||
} else if (props.status === "unpublished") {
|
||||
filtered = filtered.filter((post) => post.published === 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Build map of post_id -> tags for that post
|
||||
const postTags = new Map<number, Set<string>>();
|
||||
props.tags.forEach((tag) => {
|
||||
@@ -41,7 +54,7 @@ export default function PostSorting(props: PostSortingProps) {
|
||||
|
||||
const includeSet = new Set(includeList);
|
||||
|
||||
return props.posts.filter((post) => {
|
||||
return filtered.filter((post) => {
|
||||
const tags = postTags.get(post.id);
|
||||
if (!tags || tags.size === 0) return false;
|
||||
|
||||
@@ -61,12 +74,12 @@ export default function PostSorting(props: PostSortingProps) {
|
||||
|
||||
// Empty blacklist means show everything
|
||||
if (filterList.length === 0) {
|
||||
return props.posts;
|
||||
return filtered;
|
||||
}
|
||||
|
||||
const filterSet = new Set(filterList);
|
||||
|
||||
return props.posts.filter((post) => {
|
||||
return filtered.filter((post) => {
|
||||
const tags = postTags.get(post.id);
|
||||
if (!tags || tags.size === 0) return true; // Show posts with no tags
|
||||
|
||||
@@ -81,7 +94,7 @@ export default function PostSorting(props: PostSortingProps) {
|
||||
}
|
||||
|
||||
// No filters: show all posts
|
||||
return props.posts;
|
||||
return filtered;
|
||||
});
|
||||
|
||||
const sortedPosts = createMemo(() => {
|
||||
|
||||
@@ -40,7 +40,7 @@ export default function PostSortingSelect(props: PostSortingSelectProps) {
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setIsOpen(!isOpen())}
|
||||
class="focus-visible:border-peach focus-visible:ring-offset-peach bg-surface0 focus-visible:ring-opacity-75 relative w-full cursor-default rounded-lg py-2 pr-10 pl-3 text-left shadow-md focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 sm:text-sm"
|
||||
class="focus-visible:border-blue focus-visible:ring-offset-blue bg-surface0 focus-visible:ring-opacity-75 relative w-full cursor-default rounded-lg py-2 pr-10 pl-3 text-left shadow-md focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 sm:text-sm"
|
||||
>
|
||||
<span class="block truncate">{selected().label}</span>
|
||||
<span class="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
|
||||
@@ -62,7 +62,7 @@ export default function PostSortingSelect(props: PostSortingSelectProps) {
|
||||
onClick={() => handleSelect(sort)}
|
||||
class={`relative w-full cursor-default py-2 pr-4 pl-10 text-left select-none ${
|
||||
selected().val === sort.val
|
||||
? "bg-peach text-base brightness-75"
|
||||
? "bg-blue text-base brightness-75"
|
||||
: "text-text hover:brightness-125"
|
||||
}`}
|
||||
>
|
||||
@@ -74,12 +74,12 @@ export default function PostSortingSelect(props: PostSortingSelectProps) {
|
||||
{sort.label}
|
||||
</span>
|
||||
<Show when={selected().val === sort.val}>
|
||||
<span class="text-peach absolute inset-y-0 left-0 flex items-center pl-3">
|
||||
<span class="text-blue absolute inset-y-0 left-0 flex items-center pl-3">
|
||||
<Check
|
||||
strokeWidth={1}
|
||||
height={24}
|
||||
width={24}
|
||||
class="stroke-text"
|
||||
class="stroke-base"
|
||||
/>
|
||||
</span>
|
||||
</Show>
|
||||
|
||||
45
src/components/blog/PublishStatusToggle.tsx
Normal file
45
src/components/blog/PublishStatusToggle.tsx
Normal file
@@ -0,0 +1,45 @@
|
||||
import { useSearchParams } from "@solidjs/router";
|
||||
|
||||
type PublishStatus = "all" | "published" | "unpublished";
|
||||
|
||||
export default function PublishStatusToggle() {
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
|
||||
const currentStatus = (): PublishStatus => {
|
||||
return (searchParams.status as PublishStatus) || "all";
|
||||
};
|
||||
|
||||
const handleStatusChange = (status: PublishStatus) => {
|
||||
setSearchParams({ status });
|
||||
};
|
||||
|
||||
const buttonClass = (status: PublishStatus) =>
|
||||
`px-4 py-2 transition-all duration-300 ${
|
||||
currentStatus() === status
|
||||
? "bg-text text-base font-semibold"
|
||||
: "bg-transparent hover:brightness-125"
|
||||
}`;
|
||||
|
||||
return (
|
||||
<div class="border-text mx-auto mt-2 flex overflow-hidden rounded border">
|
||||
<button
|
||||
onClick={() => handleStatusChange("all")}
|
||||
class={buttonClass("all")}
|
||||
>
|
||||
All
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleStatusChange("published")}
|
||||
class={`${buttonClass("published")} border-text border-r border-l`}
|
||||
>
|
||||
Published
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleStatusChange("unpublished")}
|
||||
class={buttonClass("unpublished")}
|
||||
>
|
||||
Unpublished
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -46,7 +46,7 @@ export interface Post {
|
||||
body: string;
|
||||
banner_photo?: string;
|
||||
date?: string | null;
|
||||
published: boolean;
|
||||
published: number; // 0 or 1 (sqlite)
|
||||
author_id: string;
|
||||
reads: number;
|
||||
attachments?: string;
|
||||
|
||||
@@ -6,6 +6,7 @@ import { getRequestEvent } from "solid-js/web";
|
||||
import PostSortingSelect from "~/components/blog/PostSortingSelect";
|
||||
import TagSelector from "~/components/blog/TagSelector";
|
||||
import PostSorting from "~/components/blog/PostSorting";
|
||||
import PublishStatusToggle from "~/components/blog/PublishStatusToggle";
|
||||
import { TerminalSplash } from "~/components/TerminalSplash";
|
||||
|
||||
const getPosts = query(async () => {
|
||||
@@ -79,6 +80,8 @@ export default function BlogIndex() {
|
||||
"filter" in searchParams ? searchParams.filter : undefined;
|
||||
const include = () =>
|
||||
"include" in searchParams ? searchParams.include : undefined;
|
||||
const status = () =>
|
||||
"status" in searchParams ? searchParams.status : undefined;
|
||||
|
||||
const data = createAsync(() => getPosts(), { deferStream: true });
|
||||
|
||||
@@ -90,13 +93,17 @@ export default function BlogIndex() {
|
||||
<Show when={data()} fallback={<TerminalSplash />}>
|
||||
{(loadedData) => (
|
||||
<>
|
||||
<div class="flex flex-row justify-around gap-4 px-4">
|
||||
<div class="flex flex-col items-center gap-4 px-4 md:flex-row md:justify-around">
|
||||
<PostSortingSelect />
|
||||
|
||||
<Show when={Object.keys(loadedData().tagMap).length > 0}>
|
||||
<TagSelector tagMap={loadedData().tagMap} />
|
||||
</Show>
|
||||
|
||||
<Show when={loadedData().privilegeLevel === "admin"}>
|
||||
<PublishStatusToggle />
|
||||
</Show>
|
||||
|
||||
<Show when={loadedData().privilegeLevel === "admin"}>
|
||||
<div class="mt-2 flex justify-center md:mt-0 md:justify-end">
|
||||
<A
|
||||
@@ -121,6 +128,7 @@ export default function BlogIndex() {
|
||||
filters={filters()}
|
||||
sort={sort()}
|
||||
include={include()}
|
||||
status={status()}
|
||||
/>
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
Reference in New Issue
Block a user