Skip to content

Commit

Permalink
add: new way to define required setting, new pages for crud
Browse files Browse the repository at this point in the history
  • Loading branch information
matfire committed Jan 14, 2024
1 parent 147dad2 commit 750bc66
Show file tree
Hide file tree
Showing 17 changed files with 306 additions and 102 deletions.
10 changes: 9 additions & 1 deletion src/lib/articles/platforms/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { UserPreferences } from '$lib/server/drizzle';

export const supportedPlatforms: (new () => IBasePlatform<any>)[] = [];
export function RegisterPlatform(constructor: new () => IBasePlatform<any>) {
if (supportedPlatforms.find((e) => e == constructor)) return;
supportedPlatforms.push(constructor);
}

Expand All @@ -10,7 +11,14 @@ export interface IBasePlatform<T> {
frontmatter: Record<string, any>;
publish(content: string): void;
setSettings(settings: UserPreferences[]): T;
getRequiredSettings(): string[];
getRequiredSettings(): IPlatformSetting[];
setFrontmatter(data: Record<string, any>): T;
getPlatformName(): string;
}

export interface IPlatformSetting {
type: string;
name: string;
label: { htmlFor: string; value: string };
settings: Record<string, unknown>;
}
13 changes: 10 additions & 3 deletions src/lib/articles/platforms/dev.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
import type { UserPreferences } from '$lib/server/drizzle';
import { RegisterPlatform, type IBasePlatform } from './base';
import { RegisterPlatform, type IBasePlatform, type IPlatformSetting } from './base';

@RegisterPlatform
export class DevPlatform implements IBasePlatform<DevPlatform> {
settings: Record<string, string> = {};
frontmatter: Record<string, any> = {};
public getRequiredSettings(): string[] {
return ['dev'];
public getRequiredSettings(): IPlatformSetting[] {
return [
{
type: 'input',
name: 'api_token',
label: { htmlFor: 'api_key', value: 'API Key' },
settings: { type: 'text', required: true }
}
];
}

getPlatformName(): string {
Expand Down
2 changes: 1 addition & 1 deletion src/lib/articles/platforms/hashnode.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { UserPreferences } from '$lib/server/drizzle';
import { RegisterPlatform, type IBasePlatform } from './base';

@RegisterPlatform
//@RegisterPlatform
export class HashnodePlatform implements IBasePlatform<HashnodePlatform> {
settings: Record<string, string> = {};
frontmatter: Record<string, any> = {};
Expand Down
2 changes: 1 addition & 1 deletion src/lib/components/header.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
</DropdownMenu.Group>
<DropdownMenu.Separator />
<DropdownMenu.Group>
<DropdownMenu.Item href="/app/settings">Publishers</DropdownMenu.Item>
<DropdownMenu.Item href="/app/publications">Publishers</DropdownMenu.Item>
</DropdownMenu.Group>
</DropdownMenu.Content>
</DropdownMenu.Root>
Expand Down
34 changes: 34 additions & 0 deletions src/lib/components/ui/select/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Select as SelectPrimitive } from "bits-ui";

import Label from "./select-label.svelte";
import Item from "./select-item.svelte";
import Content from "./select-content.svelte";
import Trigger from "./select-trigger.svelte";
import Separator from "./select-separator.svelte";

const Root = SelectPrimitive.Root;
const Group = SelectPrimitive.Group;
const Input = SelectPrimitive.Input;
const Value = SelectPrimitive.Value;

export {
Root,
Group,
Input,
Label,
Item,
Value,
Content,
Trigger,
Separator,
//
Root as Select,
Group as SelectGroup,
Input as SelectInput,
Label as SelectLabel,
Item as SelectItem,
Value as SelectValue,
Content as SelectContent,
Trigger as SelectTrigger,
Separator as SelectSeparator
};
39 changes: 39 additions & 0 deletions src/lib/components/ui/select/select-content.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<script lang="ts">
import { Select as SelectPrimitive } from "bits-ui";
import { cn, flyAndScale } from "$lib/utils";
import { scale } from "svelte/transition";
type $$Props = SelectPrimitive.ContentProps;
type $$Events = SelectPrimitive.ContentEvents;
export let sideOffset: $$Props["sideOffset"] = 4;
export let inTransition: $$Props["inTransition"] = flyAndScale;
export let inTransitionConfig: $$Props["inTransitionConfig"] = undefined;
export let outTransition: $$Props["outTransition"] = scale;
export let outTransitionConfig: $$Props["outTransitionConfig"] = {
start: 0.95,
opacity: 0,
duration: 50
};
let className: $$Props["class"] = undefined;
export { className as class };
</script>

<SelectPrimitive.Content
{inTransition}
{inTransitionConfig}
{outTransition}
{outTransitionConfig}
{sideOffset}
class={cn(
"relative z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md outline-none",
className
)}
{...$$restProps}
on:keydown
>
<div class="w-full p-1">
<slot />
</div>
</SelectPrimitive.Content>
38 changes: 38 additions & 0 deletions src/lib/components/ui/select/select-item.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<script lang="ts">
import { cn } from "$lib/utils";
import { Select as SelectPrimitive } from "bits-ui";
import { Check } from "lucide-svelte";
type $$Props = SelectPrimitive.ItemProps;
type $$Events = SelectPrimitive.ItemEvents;
let className: $$Props["class"] = undefined;
export let value: $$Props["value"];
export let label: $$Props["label"] = undefined;
export let disabled: $$Props["disabled"] = undefined;
export { className as class };
</script>

<SelectPrimitive.Item
{value}
{disabled}
{label}
class={cn(
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className
)}
{...$$restProps}
on:click
on:keydown
on:focusin
on:focusout
on:pointerleave
on:pointermove
>
<span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<SelectPrimitive.ItemIndicator>
<Check class="h-4 w-4" />
</SelectPrimitive.ItemIndicator>
</span>
<slot />
</SelectPrimitive.Item>
16 changes: 16 additions & 0 deletions src/lib/components/ui/select/select-label.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<script lang="ts">
import { Select as SelectPrimitive } from "bits-ui";
import { cn } from "$lib/utils";
type $$Props = SelectPrimitive.LabelProps;
let className: $$Props["class"] = undefined;
export { className as class };
</script>

<SelectPrimitive.Label
class={cn("py-1.5 pl-8 pr-2 text-sm font-semibold", className)}
{...$$restProps}
>
<slot />
</SelectPrimitive.Label>
11 changes: 11 additions & 0 deletions src/lib/components/ui/select/select-separator.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<script lang="ts">
import { Select as SelectPrimitive } from "bits-ui";
import { cn } from "$lib/utils";
type $$Props = SelectPrimitive.SeparatorProps;
let className: $$Props["class"] = undefined;
export { className as class };
</script>

<SelectPrimitive.Separator class={cn("-mx-1 my-1 h-px bg-muted", className)} {...$$restProps} />
27 changes: 27 additions & 0 deletions src/lib/components/ui/select/select-trigger.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<script lang="ts">
import { Select as SelectPrimitive } from "bits-ui";
import { ChevronDown } from "lucide-svelte";
import { cn } from "$lib/utils";
type $$Props = SelectPrimitive.TriggerProps;
type $$Events = SelectPrimitive.TriggerEvents;
let className: $$Props["class"] = undefined;
export { className as class };
</script>

<SelectPrimitive.Trigger
class={cn(
"flex h-10 w-full items-center justify-between rounded-md border border-input bg-transparent px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
className
)}
{...$$restProps}
let:builder
on:click
on:keydown
>
<slot {builder} />
<div>
<ChevronDown class="h-4 w-4 opacity-50" />
</div>
</SelectPrimitive.Trigger>
16 changes: 10 additions & 6 deletions src/lib/server/drizzle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,14 @@ const userImages = sqliteTable('user_image', {
.references(() => user.id)
});

const userPreferences = sqliteTable('user_preference', {
const userPublications = sqliteTable('user_publications', {
id: integer('id').primaryKey({ autoIncrement: true }).notNull(),
key: text('key').notNull().unique(),
value: text('value').notNull()
name: text('name').notNull(),
userId: text('user_id')
.notNull()
.references(() => user.id),
publisherName: text('publisher_name').notNull(),
publisherData: text('publisherData', { mode: 'json' }).notNull()
});

const userArticles = sqliteTable('user_article', {
Expand All @@ -51,7 +55,7 @@ const userArticles = sqliteTable('user_article', {
.references(() => user.id)
});

type UserPreferences = InferSelectModel<typeof userPreferences>;
type UserPublications = InferSelectModel<typeof userPublications>;

export { user, userKey, userSession, userImages, userPreferences, userArticles };
export type { UserPreferences };
export { user, userKey, userSession, userImages, userPublications, userArticles };
export type { UserPublications };
17 changes: 17 additions & 0 deletions src/routes/app/publications/+page.server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { db } from '$lib/server/db';
import { userPublications } from '$lib/server/drizzle';
import { eq, like } from 'drizzle-orm';
import type { PageServerLoad } from './$types';
import { redirect, type Actions, fail } from '@sveltejs/kit';

export const load: PageServerLoad = async ({ locals }) => {
const session = await locals.auth.validate();
if (!session) {
throw redirect(301, '/login');
}
const publications = await db
.select()
.from(userPublications)
.where(eq(userPublications.userId, session.user?.userId));
return { publications };
};
22 changes: 22 additions & 0 deletions src/routes/app/publications/+page.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<script lang="ts">
import * as Card from '$lib/components/ui/card';
import { Button } from '$lib/components/ui/button';
export let data;
</script>

<div class="w-full flex justify-center">
<Button href="/app/publications/new">Add Publication</Button>
</div>
<div class="w-full h-full grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3">
{#each data.publications as publication}
<Card.Root>
<Card.Header>
<Card.Title>{publication.name}</Card.Title>
</Card.Header>
<Card.Footer>
<Button href={`/app/publications/${publication.id}`}>Button</Button>
</Card.Footer>
</Card.Root>
{/each}
</div>
36 changes: 36 additions & 0 deletions src/routes/app/publications/new/+page.server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { db } from '$lib/server/db';
import { userPublications } from '$lib/server/drizzle';
import { eq, like } from 'drizzle-orm';
import type { PageServerLoad } from './$types';
import { redirect, type Actions, fail } from '@sveltejs/kit';
import { supportedPlatforms } from '$lib/articles/platforms/base';
import PlatformList from '$lib/articles/platforms';

export const load: PageServerLoad = async ({ locals }) => {
const session = await locals.auth.validate();
if (!session) {
throw fail(500, { message: 'not connected' });
}
const platforms = supportedPlatforms;
return {
platforms:
platforms.map((e) => ({
platformName: new e().getPlatformName(),
platformSettings: new e().getRequiredSettings(),
platformId: e.name
})) ?? []
};
};

export const actions: Actions = {
default: async ({ locals, request }) => {
const session = await locals.auth.validate();
if (!session) {
throw fail(500, { message: 'not connected' });
}
const formData = await request.formData();
const data = Object.fromEntries(formData);
//TODO implement save logic
return { message: 'ok' };
}
};
35 changes: 35 additions & 0 deletions src/routes/app/publications/new/+page.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<script lang="ts">
import * as Select from '$lib/components/ui/select';
import { Input } from '$lib/components/ui/input';
import { Label } from '$lib/components/ui/label';
import { Button } from '$lib/components/ui/button';
export let data;
let selectedPlatform: string | null = null;
console.log(data);
</script>

<form action="" method="post">
<Select.Root onSelectedChange={(v) => (selectedPlatform = String(v?.value))}>
<Select.Trigger>
<Select.Value placeholder="Platform" />
</Select.Trigger>
<Select.Content>
{#each data.platforms as platform}
<Select.Item value={platform.platformId}>{platform.platformName}</Select.Item>
{/each}
</Select.Content>
</Select.Root>

{#if selectedPlatform}
{#each data.platforms?.find((e) => e.platformId === selectedPlatform)?.platformSettings as setting}
{#if setting.type === 'input'}
{#if setting.settings['label']}
<Label for={setting.settings['label'].htmlFor}>{setting.settings['label'].value}</Label>
{/if}
<Input id={setting.name} name={setting.name} {...setting.settings} />
{/if}
{/each}
{/if}
<Button type="submit" disabled={!selectedPlatform}>Create</Button>
</form>
Loading

0 comments on commit 750bc66

Please sign in to comment.