lotsa shit

This commit is contained in:
Omer Sabic 2024-04-26 18:40:42 +02:00
parent a8b9dd0510
commit f42ba94af4
96 changed files with 3939 additions and 142 deletions

View File

@ -3,6 +3,13 @@
"singleQuote": true,
"trailingComma": "none",
"printWidth": 100,
"plugins": ["prettier-plugin-svelte"],
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
"plugins": ["prettier-plugin-svelte", "prettier-plugin-tailwindcss"],
"overrides": [
{
"files": "*.svelte",
"options": {
"parser": "svelte"
}
}
]
}

14
components.json Normal file
View File

@ -0,0 +1,14 @@
{
"$schema": "https://shadcn-svelte.com/schema.json",
"style": "default",
"tailwind": {
"config": "tailwind.config.js",
"css": "src/app.pcss",
"baseColor": "slate"
},
"aliases": {
"components": "$lib/components",
"utils": "$lib/utils"
},
"typescript": false
}

1904
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -15,12 +15,29 @@
"@sveltejs/adapter-auto": "^3.0.0",
"@sveltejs/kit": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^3.0.0",
"autoprefixer": "^10.4.16",
"postcss": "^8.4.32",
"postcss-load-config": "^5.0.2",
"prettier": "^3.1.1",
"prettier-plugin-svelte": "^3.1.2",
"prettier-plugin-tailwindcss": "^0.5.9",
"svelte": "^4.2.7",
"svelte-check": "^3.6.0",
"tailwindcss": "^3.3.6",
"typescript": "^5.0.0",
"vite": "^5.0.3"
},
"type": "module"
"type": "module",
"dependencies": {
"bits-ui": "^0.21.4",
"clsx": "^2.1.1",
"formsnap": "^1.0.0",
"lucide-svelte": "^0.373.0",
"mode-watcher": "^0.3.0",
"svelte-radix": "^1.1.0",
"sveltekit-superforms": "^2.12.6",
"tailwind-merge": "^2.3.0",
"tailwind-variants": "^0.2.1",
"zod": "^3.23.4"
}
}

13
postcss.config.cjs Normal file
View File

@ -0,0 +1,13 @@
const tailwindcss = require('tailwindcss');
const autoprefixer = require('autoprefixer');
const config = {
plugins: [
//Some plugins, like tailwindcss/nesting, need to run before Tailwind,
tailwindcss(),
//But others, like autoprefixer, need to run after,
autoprefixer
]
};
module.exports = config;

78
src/app.pcss Normal file
View File

@ -0,0 +1,78 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;
--muted: 210 40% 96.1%;
--muted-foreground: 215.4 16.3% 46.9%;
--popover: 0 0% 100%;
--popover-foreground: 222.2 84% 4.9%;
--card: 0 0% 100%;
--card-foreground: 222.2 84% 4.9%;
--border: 214.3 31.8% 91.4%;
--input: 214.3 31.8% 91.4%;
--primary: 222.2 47.4% 11.2%;
--primary-foreground: 210 40% 98%;
--secondary: 210 40% 96.1%;
--secondary-foreground: 222.2 47.4% 11.2%;
--accent: 210 40% 96.1%;
--accent-foreground: 222.2 47.4% 11.2%;
--destructive: 0 72.2% 50.6%;
--destructive-foreground: 210 40% 98%;
--ring: 222.2 84% 4.9%;
--radius: 0.5rem;
}
.dark {
--background: 222.2 84% 4.9%;
--foreground: 210 40% 98%;
--muted: 217.2 32.6% 17.5%;
--muted-foreground: 215 20.2% 65.1%;
--popover: 222.2 84% 4.9%;
--popover-foreground: 210 40% 98%;
--card: 222.2 84% 4.9%;
--card-foreground: 210 40% 98%;
--border: 217.2 32.6% 17.5%;
--input: 217.2 32.6% 17.5%;
--primary: 210 40% 98%;
--primary-foreground: 222.2 47.4% 11.2%;
--secondary: 217.2 32.6% 17.5%;
--secondary-foreground: 210 40% 98%;
--accent: 217.2 32.6% 17.5%;
--accent-foreground: 210 40% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 210 40% 98%;
--ring: hsl(212.7,26.8%,83.9);
}
}
@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
}
}

View File

@ -1,7 +1,8 @@
import { config } from '$lib';
import { config } from '$lib/index.js';
/** @type {import('@sveltejs/kit').HandleFetch} */
export async function handleFetch({ event, request, fetch }) {
console.log(event.request.headers.get("cookie"))
if (request.url.startsWith(config.api_url)) {
// @ts-ignore
request.headers.set('cookie', event.request.headers.get('cookie'));

View File

@ -0,0 +1,4 @@
<script>
import { Badge } from "$lib/components/ui/badge";
</script>
<Badge class="ml-auto bg-purple-500 text-white hover:bg-purple-700">PRO</Badge>

View File

@ -0,0 +1,17 @@
<script>
import * as Tooltip from '$lib/components/ui/tooltip'
import {Button} from '$lib/components/ui/button'
export let tip = "";
export let variant = "";
export let size = "";
</script>
<Tooltip.Root>
<Tooltip.Trigger asChild let:builder>
<Button builders={[builder]} {variant} {size}><slot /></Button>
</Tooltip.Trigger>
<Tooltip.Content>
<p>{tip}</p>
</Tooltip.Content>
</Tooltip.Root>

View File

@ -0,0 +1,13 @@
<script>
import { Avatar as AvatarPrimitive } from "bits-ui";
import { cn } from "$lib/utils.js";
let className = undefined;
export { className as class };
</script>
<AvatarPrimitive.Fallback
class={cn("flex h-full w-full items-center justify-center rounded-full bg-muted", className)}
{...$$restProps}
>
<slot />
</AvatarPrimitive.Fallback>

View File

@ -0,0 +1,15 @@
<script>
import { Avatar as AvatarPrimitive } from "bits-ui";
import { cn } from "$lib/utils.js";
let className = undefined;
export let src = undefined;
export let alt = undefined;
export { className as class };
</script>
<AvatarPrimitive.Image
{src}
{alt}
class={cn("aspect-square h-full w-full", className)}
{...$$restProps}
/>

View File

@ -0,0 +1,15 @@
<script>
import { Avatar as AvatarPrimitive } from "bits-ui";
import { cn } from "$lib/utils.js";
let className = undefined;
export let delayMs = undefined;
export { className as class };
</script>
<AvatarPrimitive.Root
{delayMs}
class={cn("relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full", className)}
{...$$restProps}
>
<slot />
</AvatarPrimitive.Root>

View File

@ -0,0 +1,12 @@
import Root from "./avatar.svelte";
import Image from "./avatar-image.svelte";
import Fallback from "./avatar-fallback.svelte";
export {
Root,
Image,
Fallback,
//
Root as Avatar,
Image as AvatarImage,
Fallback as AvatarFallback,
};

View File

@ -0,0 +1,17 @@
<script>
import { badgeVariants } from "./index.js";
import { cn } from "$lib/utils.js";
let className = undefined;
export let href = undefined;
export let variant = "default";
export { className as class };
</script>
<svelte:element
this={href ? "a" : "span"}
{href}
class={cn(badgeVariants({ variant, className }))}
{...$$restProps}
>
<slot />
</svelte:element>

View File

@ -0,0 +1,18 @@
import { tv } from "tailwind-variants";
export { default as Badge } from "./badge.svelte";
export const badgeVariants = tv({
base: "inline-flex select-none items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
variants: {
variant: {
default: "border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
secondary:
"border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
destructive:
"border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
outline: "text-foreground",
},
},
defaultVariants: {
variant: "default",
},
});

View File

@ -0,0 +1,21 @@
<script>
import { Button as ButtonPrimitive } from "bits-ui";
import { buttonVariants } from "./index.js";
import { cn } from "$lib/utils.js";
let className = undefined;
export let variant = "default";
export let size = "default";
export let builders = [];
export { className as class };
</script>
<ButtonPrimitive.Root
{builders}
class={cn(buttonVariants({ variant, size, className }))}
type="button"
{...$$restProps}
on:click
on:keydown
>
<slot />
</ButtonPrimitive.Root>

View File

@ -0,0 +1,32 @@
import { tv } from "tailwind-variants";
import Root from "./button.svelte";
const buttonVariants = tv({
base: "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
variants: {
variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/90",
destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
outline:
"border border-input bg-background hover:bg-accent hover:text-accent-foreground",
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: "h-10 px-4 py-2",
sm: "h-9 rounded-md px-3",
lg: "h-11 rounded-md px-8",
icon: "h-10 w-10",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
});
export {
Root,
//
Root as Button,
buttonVariants,
};

View File

@ -0,0 +1,9 @@
<script>
import { cn } from "$lib/utils.js";
let className = undefined;
export { className as class };
</script>
<div class={cn("p-6 pt-0", className)} {...$$restProps}>
<slot />
</div>

View File

@ -0,0 +1,9 @@
<script>
import { cn } from "$lib/utils.js";
let className = undefined;
export { className as class };
</script>
<p class={cn("text-sm text-muted-foreground", className)} {...$$restProps}>
<slot />
</p>

View File

@ -0,0 +1,9 @@
<script>
import { cn } from "$lib/utils.js";
let className = undefined;
export { className as class };
</script>
<div class={cn("flex items-center p-6 pt-0", className)} {...$$restProps}>
<slot />
</div>

View File

@ -0,0 +1,9 @@
<script>
import { cn } from "$lib/utils.js";
let className = undefined;
export { className as class };
</script>
<div class={cn("flex flex-col space-y-1.5 p-6", className)} {...$$restProps}>
<slot />
</div>

View File

@ -0,0 +1,14 @@
<script>
import { cn } from "$lib/utils.js";
let className = undefined;
export let tag = "h3";
export { className as class };
</script>
<svelte:element
this={tag}
class={cn("text-lg font-semibold leading-none tracking-tight", className)}
{...$$restProps}
>
<slot />
</svelte:element>

View File

@ -0,0 +1,12 @@
<script>
import { cn } from "$lib/utils.js";
let className = undefined;
export { className as class };
</script>
<div
class={cn("rounded-lg border bg-card text-card-foreground shadow-sm", className)}
{...$$restProps}
>
<slot />
</div>

View File

@ -0,0 +1,21 @@
import Root from "./card.svelte";
import Content from "./card-content.svelte";
import Description from "./card-description.svelte";
import Footer from "./card-footer.svelte";
import Header from "./card-header.svelte";
import Title from "./card-title.svelte";
export {
Root,
Content,
Description,
Footer,
Header,
Title,
//
Root as Card,
Content as CardContent,
Description as CardDescription,
Footer as CardFooter,
Header as CardHeader,
Title as CardTitle,
};

View File

@ -0,0 +1,33 @@
<script>
import { Dialog as DialogPrimitive } from "bits-ui";
import X from "lucide-svelte/icons/x";
import * as Dialog from "./index.js";
import { cn, flyAndScale } from "$lib/utils.js";
let className = undefined;
export let transition = flyAndScale;
export let transitionConfig = {
duration: 200,
};
export { className as class };
</script>
<Dialog.Portal>
<Dialog.Overlay />
<DialogPrimitive.Content
{transition}
{transitionConfig}
class={cn(
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg sm:rounded-lg md:w-full",
className
)}
{...$$restProps}
>
<slot />
<DialogPrimitive.Close
class="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground"
>
<X class="h-4 w-4" />
<span class="sr-only">Close</span>
</DialogPrimitive.Close>
</DialogPrimitive.Content>
</Dialog.Portal>

View File

@ -0,0 +1,13 @@
<script>
import { Dialog as DialogPrimitive } from "bits-ui";
import { cn } from "$lib/utils.js";
let className = undefined;
export { className as class };
</script>
<DialogPrimitive.Description
class={cn("text-sm text-muted-foreground", className)}
{...$$restProps}
>
<slot />
</DialogPrimitive.Description>

View File

@ -0,0 +1,12 @@
<script>
import { cn } from "$lib/utils.js";
let className = undefined;
export { className as class };
</script>
<div
class={cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", className)}
{...$$restProps}
>
<slot />
</div>

View File

@ -0,0 +1,9 @@
<script>
import { cn } from "$lib/utils.js";
let className = undefined;
export { className as class };
</script>
<div class={cn("flex flex-col space-y-1.5 text-center sm:text-left", className)} {...$$restProps}>
<slot />
</div>

View File

@ -0,0 +1,18 @@
<script>
import { Dialog as DialogPrimitive } from "bits-ui";
import { fade } from "svelte/transition";
import { cn } from "$lib/utils.js";
let className = undefined;
export let transition = fade;
export let transitionConfig = {
duration: 150,
};
export { className as class };
</script>
<DialogPrimitive.Overlay
{transition}
{transitionConfig}
class={cn("fixed inset-0 z-50 bg-background/80 backdrop-blur-sm", className)}
{...$$restProps}
/>

View File

@ -0,0 +1,7 @@
<script>
import { Dialog as DialogPrimitive } from "bits-ui";
</script>
<DialogPrimitive.Portal {...$$restProps}>
<slot />
</DialogPrimitive.Portal>

View File

@ -0,0 +1,13 @@
<script>
import { Dialog as DialogPrimitive } from "bits-ui";
import { cn } from "$lib/utils.js";
let className = undefined;
export { className as class };
</script>
<DialogPrimitive.Title
class={cn("text-lg font-semibold leading-none tracking-tight", className)}
{...$$restProps}
>
<slot />
</DialogPrimitive.Title>

View File

@ -0,0 +1,34 @@
import { Dialog as DialogPrimitive } from "bits-ui";
import Title from "./dialog-title.svelte";
import Portal from "./dialog-portal.svelte";
import Footer from "./dialog-footer.svelte";
import Header from "./dialog-header.svelte";
import Overlay from "./dialog-overlay.svelte";
import Content from "./dialog-content.svelte";
import Description from "./dialog-description.svelte";
const Root = DialogPrimitive.Root;
const Trigger = DialogPrimitive.Trigger;
const Close = DialogPrimitive.Close;
export {
Root,
Title,
Portal,
Footer,
Header,
Trigger,
Overlay,
Content,
Description,
Close,
//
Root as Dialog,
Title as DialogTitle,
Portal as DialogPortal,
Footer as DialogFooter,
Header as DialogHeader,
Trigger as DialogTrigger,
Overlay as DialogOverlay,
Content as DialogContent,
Description as DialogDescription,
Close as DialogClose,
};

View File

@ -0,0 +1,31 @@
<script>
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
import Check from "lucide-svelte/icons/check";
import { cn } from "$lib/utils.js";
let className = undefined;
export let checked = undefined;
export { className as class };
</script>
<DropdownMenuPrimitive.CheckboxItem
bind:checked
class={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none data-[disabled]:pointer-events-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[disabled]:opacity-50",
className
)}
{...$$restProps}
on:click
on:keydown
on:focusin
on:focusout
on:pointerdown
on:pointerleave
on:pointermove
>
<span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<DropdownMenuPrimitive.CheckboxIndicator>
<Check class="h-4 w-4" />
</DropdownMenuPrimitive.CheckboxIndicator>
</span>
<slot />
</DropdownMenuPrimitive.CheckboxItem>

View File

@ -0,0 +1,23 @@
<script>
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
import { cn, flyAndScale } from "$lib/utils.js";
let className = undefined;
export let sideOffset = 4;
export let transition = flyAndScale;
export let transitionConfig = undefined;
export { className as class };
</script>
<DropdownMenuPrimitive.Content
{transition}
{transitionConfig}
{sideOffset}
class={cn(
"z-50 min-w-[8rem] rounded-md border bg-popover p-1 text-popover-foreground shadow-md focus:outline-none",
className
)}
{...$$restProps}
on:keydown
>
<slot />
</DropdownMenuPrimitive.Content>

View File

@ -0,0 +1,25 @@
<script>
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
import { cn } from "$lib/utils.js";
let className = undefined;
export let inset = undefined;
export { className as class };
</script>
<DropdownMenuPrimitive.Item
class={cn(
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled]:pointer-events-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[disabled]:opacity-50",
inset && "pl-8",
className
)}
{...$$restProps}
on:click
on:keydown
on:focusin
on:focusout
on:pointerdown
on:pointerleave
on:pointermove
>
<slot />
</DropdownMenuPrimitive.Item>

View File

@ -0,0 +1,14 @@
<script>
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
import { cn } from "$lib/utils.js";
let className = undefined;
export let inset = undefined;
export { className as class };
</script>
<DropdownMenuPrimitive.Label
class={cn("px-2 py-1.5 text-sm font-semibold", inset && "pl-8", className)}
{...$$restProps}
>
<slot />
</DropdownMenuPrimitive.Label>

View File

@ -0,0 +1,8 @@
<script>
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
export let value = undefined;
</script>
<DropdownMenuPrimitive.RadioGroup {...$$restProps} bind:value>
<slot />
</DropdownMenuPrimitive.RadioGroup>

View File

@ -0,0 +1,31 @@
<script>
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
import Circle from "lucide-svelte/icons/circle";
import { cn } from "$lib/utils.js";
let className = undefined;
export let value;
export { className as class };
</script>
<DropdownMenuPrimitive.RadioItem
class={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none data-[disabled]:pointer-events-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[disabled]:opacity-50",
className
)}
{value}
{...$$restProps}
on:click
on:keydown
on:focusin
on:focusout
on:pointerdown
on:pointerleave
on:pointermove
>
<span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<DropdownMenuPrimitive.RadioIndicator>
<Circle class="h-2 w-2 fill-current" />
</DropdownMenuPrimitive.RadioIndicator>
</span>
<slot />
</DropdownMenuPrimitive.RadioItem>

View File

@ -0,0 +1,11 @@
<script>
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
import { cn } from "$lib/utils.js";
let className = undefined;
export { className as class };
</script>
<DropdownMenuPrimitive.Separator
class={cn("-mx-1 my-1 h-px bg-muted", className)}
{...$$restProps}
/>

View File

@ -0,0 +1,9 @@
<script>
import { cn } from "$lib/utils.js";
let className = undefined;
export { className as class };
</script>
<span class={cn("ml-auto text-xs tracking-widest opacity-60", className)} {...$$restProps}>
<slot />
</span>

View File

@ -0,0 +1,26 @@
<script>
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
import { cn, flyAndScale } from "$lib/utils.js";
let className = undefined;
export let transition = flyAndScale;
export let transitionConfig = {
x: -10,
y: 0,
};
export { className as class };
</script>
<DropdownMenuPrimitive.SubContent
{transition}
{transitionConfig}
class={cn(
"z-50 min-w-[8rem] rounded-md border bg-popover p-1 text-popover-foreground shadow-lg focus:outline-none",
className
)}
{...$$restProps}
on:keydown
on:focusout
on:pointermove
>
<slot />
</DropdownMenuPrimitive.SubContent>

View File

@ -0,0 +1,26 @@
<script>
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
import ChevronRight from "lucide-svelte/icons/chevron-right";
import { cn } from "$lib/utils.js";
let className = undefined;
export let inset = undefined;
export { className as class };
</script>
<DropdownMenuPrimitive.SubTrigger
class={cn(
"flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[highlighted]:bg-accent data-[state=open]:bg-accent data-[highlighted]:text-accent-foreground data-[state=open]:text-accent-foreground",
inset && "pl-8",
className
)}
{...$$restProps}
on:click
on:keydown
on:focusin
on:focusout
on:pointerleave
on:pointermove
>
<slot />
<ChevronRight class="ml-auto h-4 w-4" />
</DropdownMenuPrimitive.SubTrigger>

View File

@ -0,0 +1,46 @@
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
import Item from "./dropdown-menu-item.svelte";
import Label from "./dropdown-menu-label.svelte";
import Content from "./dropdown-menu-content.svelte";
import Shortcut from "./dropdown-menu-shortcut.svelte";
import RadioItem from "./dropdown-menu-radio-item.svelte";
import Separator from "./dropdown-menu-separator.svelte";
import RadioGroup from "./dropdown-menu-radio-group.svelte";
import SubContent from "./dropdown-menu-sub-content.svelte";
import SubTrigger from "./dropdown-menu-sub-trigger.svelte";
import CheckboxItem from "./dropdown-menu-checkbox-item.svelte";
const Sub = DropdownMenuPrimitive.Sub;
const Root = DropdownMenuPrimitive.Root;
const Trigger = DropdownMenuPrimitive.Trigger;
const Group = DropdownMenuPrimitive.Group;
export {
Sub,
Root,
Item,
Label,
Group,
Trigger,
Content,
Shortcut,
Separator,
RadioItem,
SubContent,
SubTrigger,
RadioGroup,
CheckboxItem,
//
Root as DropdownMenu,
Sub as DropdownMenuSub,
Item as DropdownMenuItem,
Label as DropdownMenuLabel,
Group as DropdownMenuGroup,
Content as DropdownMenuContent,
Trigger as DropdownMenuTrigger,
Shortcut as DropdownMenuShortcut,
RadioItem as DropdownMenuRadioItem,
Separator as DropdownMenuSeparator,
RadioGroup as DropdownMenuRadioGroup,
SubContent as DropdownMenuSubContent,
SubTrigger as DropdownMenuSubTrigger,
CheckboxItem as DropdownMenuCheckboxItem,
};

View File

@ -0,0 +1,7 @@
<script>
import * as Button from "$lib/components/ui/button/index.js";
</script>
<Button.Root type="submit" on:click on:keydown {...$$restProps}>
<slot />
</Button.Root>

View File

@ -0,0 +1,14 @@
<script>
import * as FormPrimitive from "formsnap";
import { cn } from "$lib/utils.js";
let className = undefined;
export { className as class };
</script>
<FormPrimitive.Description
class={cn("text-sm text-muted-foreground", className)}
{...$$restProps}
let:descriptionAttrs
>
<slot {descriptionAttrs} />
</FormPrimitive.Description>

View File

@ -0,0 +1,16 @@
<script lang="ts" context="module"></script>
<script lang="ts" generics="T extends Record<string, unknown>, U extends FormPathLeaves<T>">
import * as FormPrimitive from "formsnap";
import { cn } from "$lib/utils.js";
export let form;
export let name;
let className = undefined;
export { className as class };
</script>
<FormPrimitive.ElementField {form} {name} let:constraints let:errors let:tainted let:value>
<div class={cn("space-y-2", className)}>
<slot {constraints} {errors} {tainted} {value} />
</div>
</FormPrimitive.ElementField>

View File

@ -0,0 +1,21 @@
<script>
import * as FormPrimitive from "formsnap";
import { cn } from "$lib/utils.js";
let className = undefined;
export { className as class };
export let errorClasses = undefined;
</script>
<FormPrimitive.FieldErrors
class={cn("text-sm font-medium text-destructive", className)}
{...$$restProps}
let:errors
let:fieldErrorsAttrs
let:errorAttrs
>
<slot {errors} {fieldErrorsAttrs} {errorAttrs}>
{#each errors as error}
<div {...errorAttrs} class={cn(errorClasses)}>{error}</div>
{/each}
</slot>
</FormPrimitive.FieldErrors>

View File

@ -0,0 +1,16 @@
<script lang="ts" context="module"></script>
<script lang="ts" generics="T extends Record<string, unknown>, U extends FormPath<T>">
import * as FormPrimitive from "formsnap";
import { cn } from "$lib/utils.js";
export let form;
export let name;
let className = undefined;
export { className as class };
</script>
<FormPrimitive.Field {form} {name} let:constraints let:errors let:tainted let:value>
<div class={cn("space-y-2", className)}>
<slot {constraints} {errors} {tainted} {value} />
</div>
</FormPrimitive.Field>

View File

@ -0,0 +1,22 @@
<script lang="ts" context="module"></script>
<script lang="ts" generics="T extends Record<string, unknown>, U extends FormPath<T>">
import * as FormPrimitive from "formsnap";
import { cn } from "$lib/utils.js";
export let form;
export let name;
let className = undefined;
export { className as class };
</script>
<FormPrimitive.Fieldset
{form}
{name}
let:constraints
let:errors
let:tainted
let:value
class={cn("space-y-2", className)}
>
<slot {constraints} {errors} {tainted} {value} />
</FormPrimitive.Fieldset>

View File

@ -0,0 +1,12 @@
<script>
import { getFormControl } from "formsnap";
import { cn } from "$lib/utils.js";
import { Label } from "$lib/components/ui/label/index.js";
let className = undefined;
export { className as class };
const { labelAttrs } = getFormControl();
</script>
<Label {...$labelAttrs} class={cn("data-[fs-error]:text-destructive", className)} {...$$restProps}>
<slot {labelAttrs} />
</Label>

View File

@ -0,0 +1,14 @@
<script>
import * as FormPrimitive from "formsnap";
import { cn } from "$lib/utils.js";
let className = undefined;
export { className as class };
</script>
<FormPrimitive.Legend
{...$$restProps}
class={cn("text-sm font-medium leading-none data-[fs-error]:text-destructive", className)}
let:legendAttrs
>
<slot {legendAttrs} />
</FormPrimitive.Legend>

View File

@ -0,0 +1,31 @@
import * as FormPrimitive from "formsnap";
import Description from "./form-description.svelte";
import Label from "./form-label.svelte";
import FieldErrors from "./form-field-errors.svelte";
import Field from "./form-field.svelte";
import Fieldset from "./form-fieldset.svelte";
import Legend from "./form-legend.svelte";
import ElementField from "./form-element-field.svelte";
import Button from "./form-button.svelte";
const Control = FormPrimitive.Control;
export {
Field,
Control,
Label,
Button,
FieldErrors,
Description,
Fieldset,
Legend,
ElementField,
//
Field as FormField,
Control as FormControl,
Description as FormDescription,
Label as FormLabel,
FieldErrors as FormFieldErrors,
Fieldset as FormFieldset,
Legend as FormLegend,
ElementField as FormElementField,
Button as FormButton,
};

View File

@ -0,0 +1,6 @@
import Root from "./input.svelte";
export {
Root,
//
Root as Input,
};

View File

@ -0,0 +1,32 @@
<script>
import { cn } from "$lib/utils.js";
let className = undefined;
export let value = undefined;
export { className as class };
export let readonly = undefined;
</script>
<input
class={cn(
"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
className
)}
bind:value
{readonly}
on:blur
on:change
on:click
on:focus
on:focusin
on:focusout
on:keydown
on:keypress
on:keyup
on:mouseover
on:mouseenter
on:mouseleave
on:paste
on:input
on:wheel
{...$$restProps}
/>

View File

@ -0,0 +1,6 @@
import Root from "./label.svelte";
export {
Root,
//
Root as Label,
};

View File

@ -0,0 +1,17 @@
<script>
import { Label as LabelPrimitive } from "bits-ui";
import { cn } from "$lib/utils.js";
let className = undefined;
export { className as class };
</script>
<LabelPrimitive.Root
class={cn(
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
className
)}
{...$$restProps}
on:mousedown
>
<slot />
</LabelPrimitive.Root>

View File

@ -0,0 +1,31 @@
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,
};

View File

@ -0,0 +1,34 @@
<script>
import { Select as SelectPrimitive } from "bits-ui";
import { scale } from "svelte/transition";
import { cn, flyAndScale } from "$lib/utils.js";
export let sideOffset = 4;
export let inTransition = flyAndScale;
export let inTransitionConfig = undefined;
export let outTransition = scale;
export let outTransitionConfig = {
start: 0.95,
opacity: 0,
duration: 50,
};
let className = 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>

View File

@ -0,0 +1,36 @@
<script>
import Check from "lucide-svelte/icons/check";
import { Select as SelectPrimitive } from "bits-ui";
import { cn } from "$lib/utils.js";
let className = undefined;
export let value;
export let label = undefined;
export let 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-[disabled]:pointer-events-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground 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>
{label || value}
</slot>
</SelectPrimitive.Item>

View File

@ -0,0 +1,13 @@
<script>
import { Select as SelectPrimitive } from "bits-ui";
import { cn } from "$lib/utils.js";
let className = 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>

View File

@ -0,0 +1,8 @@
<script>
import { Select as SelectPrimitive } from "bits-ui";
import { cn } from "$lib/utils.js";
let className = undefined;
export { className as class };
</script>
<SelectPrimitive.Separator class={cn("-mx-1 my-1 h-px bg-muted", className)} {...$$restProps} />

View File

@ -0,0 +1,23 @@
<script>
import { Select as SelectPrimitive } from "bits-ui";
import ChevronDown from "lucide-svelte/icons/chevron-down";
import { cn } from "$lib/utils.js";
let className = 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-background 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 [&>span]:line-clamp-1",
className
)}
{...$$restProps}
let:builder
on:click
on:keydown
>
<slot {builder} />
<div>
<ChevronDown class="h-4 w-4 opacity-50" />
</div>
</SelectPrimitive.Trigger>

View File

@ -0,0 +1,99 @@
import { Dialog as SheetPrimitive } from "bits-ui";
import { tv } from "tailwind-variants";
import Portal from "./sheet-portal.svelte";
import Overlay from "./sheet-overlay.svelte";
import Content from "./sheet-content.svelte";
import Header from "./sheet-header.svelte";
import Footer from "./sheet-footer.svelte";
import Title from "./sheet-title.svelte";
import Description from "./sheet-description.svelte";
const Root = SheetPrimitive.Root;
const Close = SheetPrimitive.Close;
const Trigger = SheetPrimitive.Trigger;
export {
Root,
Close,
Trigger,
Portal,
Overlay,
Content,
Header,
Footer,
Title,
Description,
//
Root as Sheet,
Close as SheetClose,
Trigger as SheetTrigger,
Portal as SheetPortal,
Overlay as SheetOverlay,
Content as SheetContent,
Header as SheetHeader,
Footer as SheetFooter,
Title as SheetTitle,
Description as SheetDescription,
};
export const sheetVariants = tv({
base: "fixed z-50 gap-4 bg-background p-6 shadow-lg",
variants: {
side: {
top: "inset-x-0 top-0 border-b",
bottom: "inset-x-0 bottom-0 border-t",
left: "inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-sm",
right: "inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm",
},
},
defaultVariants: {
side: "right",
},
});
export const sheetTransitions = {
top: {
in: {
y: "-100%",
duration: 500,
opacity: 1,
},
out: {
y: "-100%",
duration: 300,
opacity: 1,
},
},
bottom: {
in: {
y: "100%",
duration: 500,
opacity: 1,
},
out: {
y: "100%",
duration: 300,
opacity: 1,
},
},
left: {
in: {
x: "-100%",
duration: 500,
opacity: 1,
},
out: {
x: "-100%",
duration: 300,
opacity: 1,
},
},
right: {
in: {
x: "100%",
duration: 500,
opacity: 1,
},
out: {
x: "100%",
duration: 300,
opacity: 1,
},
},
};

View File

@ -0,0 +1,34 @@
<script>
import { Dialog as SheetPrimitive } from "bits-ui";
import X from "lucide-svelte/icons/x";
import { fly } from "svelte/transition";
import { SheetOverlay, SheetPortal, sheetTransitions, sheetVariants } from "./index.js";
import { cn } from "$lib/utils.js";
let className = undefined;
export let side = "right";
export { className as class };
export let inTransition = fly;
export let inTransitionConfig = sheetTransitions[side ?? "right"].in;
export let outTransition = fly;
export let outTransitionConfig = sheetTransitions[side ?? "right"].out;
</script>
<SheetPortal>
<SheetOverlay />
<SheetPrimitive.Content
{inTransition}
{inTransitionConfig}
{outTransition}
{outTransitionConfig}
class={cn(sheetVariants({ side }), className)}
{...$$restProps}
>
<slot />
<SheetPrimitive.Close
class="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary"
>
<X class="h-4 w-4" />
<span class="sr-only">Close</span>
</SheetPrimitive.Close>
</SheetPrimitive.Content>
</SheetPortal>

View File

@ -0,0 +1,10 @@
<script>
import { Dialog as SheetPrimitive } from "bits-ui";
import { cn } from "$lib/utils.js";
let className = undefined;
export { className as class };
</script>
<SheetPrimitive.Description class={cn("text-sm text-muted-foreground", className)} {...$$restProps}>
<slot />
</SheetPrimitive.Description>

View File

@ -0,0 +1,12 @@
<script>
import { cn } from "$lib/utils.js";
let className = undefined;
export { className as class };
</script>
<div
class={cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", className)}
{...$$restProps}
>
<slot />
</div>

View File

@ -0,0 +1,9 @@
<script>
import { cn } from "$lib/utils.js";
let className = undefined;
export { className as class };
</script>
<div class={cn("flex flex-col space-y-2 text-center sm:text-left", className)} {...$$restProps}>
<slot />
</div>

View File

@ -0,0 +1,18 @@
<script>
import { Dialog as SheetPrimitive } from "bits-ui";
import { fade } from "svelte/transition";
import { cn } from "$lib/utils.js";
let className = undefined;
export let transition = fade;
export let transitionConfig = {
duration: 150,
};
export { className as class };
</script>
<SheetPrimitive.Overlay
{transition}
{transitionConfig}
class={cn("fixed inset-0 z-50 bg-background/80 backdrop-blur-sm ", className)}
{...$$restProps}
/>

View File

@ -0,0 +1,10 @@
<script>
import { Dialog as SheetPrimitive } from "bits-ui";
import { cn } from "$lib/utils.js";
let className = undefined;
export { className as class };
</script>
<SheetPrimitive.Portal class={cn(className)} {...$$restProps}>
<slot />
</SheetPrimitive.Portal>

View File

@ -0,0 +1,13 @@
<script>
import { Dialog as SheetPrimitive } from "bits-ui";
import { cn } from "$lib/utils.js";
let className = undefined;
export { className as class };
</script>
<SheetPrimitive.Title
class={cn("text-lg font-semibold text-foreground", className)}
{...$$restProps}
>
<slot />
</SheetPrimitive.Title>

View File

@ -0,0 +1,27 @@
import Root from "./table.svelte";
import Body from "./table-body.svelte";
import Caption from "./table-caption.svelte";
import Cell from "./table-cell.svelte";
import Footer from "./table-footer.svelte";
import Head from "./table-head.svelte";
import Header from "./table-header.svelte";
import Row from "./table-row.svelte";
export {
Root,
Body,
Caption,
Cell,
Footer,
Head,
Header,
Row,
//
Root as Table,
Body as TableBody,
Caption as TableCaption,
Cell as TableCell,
Footer as TableFooter,
Head as TableHead,
Header as TableHeader,
Row as TableRow,
};

View File

@ -0,0 +1,9 @@
<script>
import { cn } from "$lib/utils.js";
let className = undefined;
export { className as class };
</script>
<tbody class={cn("[&_tr:last-child]:border-0", className)} {...$$restProps}>
<slot />
</tbody>

View File

@ -0,0 +1,9 @@
<script>
import { cn } from "$lib/utils.js";
let className = undefined;
export { className as class };
</script>
<caption class={cn("mt-4 text-sm text-muted-foreground", className)} {...$$restProps}>
<slot />
</caption>

View File

@ -0,0 +1,14 @@
<script>
import { cn } from "$lib/utils.js";
let className = undefined;
export { className as class };
</script>
<td
class={cn("p-4 align-middle [&:has([role=checkbox])]:pr-0", className)}
{...$$restProps}
on:click
on:keydown
>
<slot />
</td>

View File

@ -0,0 +1,9 @@
<script>
import { cn } from "$lib/utils.js";
let className = undefined;
export { className as class };
</script>
<tfoot class={cn("bg-primary font-medium text-primary-foreground", className)} {...$$restProps}>
<slot />
</tfoot>

View File

@ -0,0 +1,15 @@
<script>
import { cn } from "$lib/utils.js";
let className = undefined;
export { className as class };
</script>
<th
class={cn(
"h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0",
className
)}
{...$$restProps}
>
<slot />
</th>

View File

@ -0,0 +1,10 @@
<script>
import { cn } from "$lib/utils.js";
let className = undefined;
export { className as class };
</script>
<!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
<thead class={cn("[&_tr]:border-b", className)} {...$$restProps} on:click on:keydown>
<slot />
</thead>

View File

@ -0,0 +1,17 @@
<script>
import { cn } from "$lib/utils.js";
let className = undefined;
export { className as class };
</script>
<tr
class={cn(
"border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted",
className
)}
{...$$restProps}
on:click
on:keydown
>
<slot />
</tr>

View File

@ -0,0 +1,11 @@
<script>
import { cn } from "$lib/utils.js";
let className = undefined;
export { className as class };
</script>
<div class="relative w-full overflow-auto">
<table class={cn("w-full caption-bottom text-sm", className)} {...$$restProps}>
<slot />
</table>
</div>

View File

@ -0,0 +1,13 @@
import { Tooltip as TooltipPrimitive } from "bits-ui";
import Content from "./tooltip-content.svelte";
const Root = TooltipPrimitive.Root;
const Trigger = TooltipPrimitive.Trigger;
export {
Root,
Trigger,
Content,
//
Root as Tooltip,
Content as TooltipContent,
Trigger as TooltipTrigger,
};

View File

@ -0,0 +1,25 @@
<script>
import { Tooltip as TooltipPrimitive } from "bits-ui";
import { cn, flyAndScale } from "$lib/utils.js";
let className = undefined;
export let sideOffset = 4;
export let transition = flyAndScale;
export let transitionConfig = {
y: 8,
duration: 150,
};
export { className as class };
</script>
<TooltipPrimitive.Content
{transition}
{transitionConfig}
{sideOffset}
class={cn(
"z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md",
className
)}
{...$$restProps}
>
<slot />
</TooltipPrimitive.Content>

48
src/lib/utils.js Normal file
View File

@ -0,0 +1,48 @@
import { clsx } from "clsx";
import { twMerge } from "tailwind-merge";
import { cubicOut } from "svelte/easing";
export function cn(...inputs) {
return twMerge(clsx(inputs));
}
export const flyAndScale = (
node,
params = { y: -8, x: 0, start: 0.95, duration: 150 }
) => {
const style = getComputedStyle(node);
const transform = style.transform === "none" ? "" : style.transform;
const scaleConversion = (valueA, scaleA, scaleB) => {
const [minA, maxA] = scaleA;
const [minB, maxB] = scaleB;
const percentage = (valueA - minA) / (maxA - minA);
const valueB = percentage * (maxB - minB) + minB;
return valueB;
};
const styleToString = (style) => {
return Object.keys(style).reduce((str, key) => {
if (style[key] === undefined) return str;
return str + `${key}:${style[key]};`;
}, "");
};
return {
duration: params.duration ?? 200,
delay: 0,
css: (t) => {
const y = scaleConversion(t, [0, 1], [params.y ?? 5, 0]);
const x = scaleConversion(t, [0, 1], [params.x ?? 0, 0]);
const scale = scaleConversion(t, [0, 1], [params.start ?? 0.95, 1]);
return styleToString({
transform: `${transform} translate3d(${x}px, ${y}px, 0) scale(${scale})`,
opacity: t
});
},
easing: cubicOut
};
};

View File

@ -1,10 +1,8 @@
import { config } from "$lib"
/** @type {import("./$types").PageServerLoad} */
/** @type {import("./$types").LayoutServerLoad} */
export const load = async ({fetch}) => {
const res = await fetch(config.api_url+"/me", {
credentials: 'include'
});
const res = await fetch(config.api_url+"/me")
const data = await res.json();

124
src/routes/+layout.svelte Normal file
View File

@ -0,0 +1,124 @@
<script>
import '../app.pcss';
import Menu from 'lucide-svelte/icons/menu';
import Package2 from 'lucide-svelte/icons/package-2';
import Search from 'lucide-svelte/icons/search';
import CircleUser from 'lucide-svelte/icons/circle-user';
import Sun from 'svelte-radix/Sun.svelte';
import Moon from 'svelte-radix/Moon.svelte';
import * as DropdownMenu from '$lib/components/ui/dropdown-menu/index.js';
import { Input } from '$lib/components/ui/input/index.js';
import * as Sheet from '$lib/components/ui/sheet/index.js';
import { Button } from '$lib/components/ui/button/index.js';
import { ModeWatcher, toggleMode } from 'mode-watcher';
/** @type {import('./$types').LayoutServerData} */
export let data;
</script>
<ModeWatcher />
<div class="flex min-h-screen w-full flex-col">
<header class="sticky top-0 flex h-16 items-center gap-4 border-b bg-background px-4 md:px-6">
<nav
class="hidden flex-col gap-6 text-lg font-medium md:flex md:flex-row md:items-center md:gap-5 md:text-sm lg:gap-6"
>
<a href="##" class="flex items-center gap-2 text-lg font-semibold md:text-base">
<Package2 class="h-6 w-6" />
<span class="sr-only">{data.me.user.name}</span>
</a>
<a href="/" class="text-foreground transition-colors hover:text-foreground"> Dashboard </a>
<a href="/articles" class="text-muted-foreground transition-colors hover:text-foreground">
Articles
</a>
<a href="##" class="text-muted-foreground transition-colors hover:text-foreground">
Emails
</a>
<a href="##" class="text-muted-foreground transition-colors hover:text-foreground">
Website
</a>
<a href="##" class="text-muted-foreground transition-colors hover:text-foreground">
Analytics
</a>
</nav>
<Sheet.Root>
<Sheet.Trigger asChild let:builder>
<Button variant="outline" size="icon" class="shrink-0 md:hidden" builders={[builder]}>
<Menu class="h-5 w-5" />
<span class="sr-only">Toggle navigation menu</span>
</Button>
</Sheet.Trigger>
<Sheet.Content side="left">
<nav class="grid gap-6 text-lg font-medium">
<a href="##" class="flex items-center gap-2 text-lg font-semibold">
<Package2 class="h-6 w-6" />
<span class="sr-only">{data.me.user.name}</span>
</a>
<a href="/" class="hover:text-foreground"> Dashboard </a>
<a href="/articles" class="text-muted-foreground hover:text-foreground"> Articles </a>
<a href="##" class="text-muted-foreground hover:text-foreground"> Products </a>
<a href="##" class="text-muted-foreground hover:text-foreground"> Customers </a>
<a href="##" class="text-muted-foreground hover:text-foreground"> Analytics </a>
<div class="mt-auto">
<Button on:click={toggleMode} variant="outline" size="icon">
<Sun
class="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0"
/>
<Moon
class="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100"
/>
<span class="sr-only">Toggle theme</span>
</Button>
</div>
</nav>
</Sheet.Content>
</Sheet.Root>
<div class="flex w-full items-center gap-4 md:ml-auto md:gap-2 lg:gap-4">
<div class="hidden md:block ml-auto">
<Button on:click={toggleMode} variant="outline" size="icon">
<Sun
class="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0"
/>
<Moon
class="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100"
/>
<span class="sr-only">Toggle theme</span>
</Button>
</div>
<form class="flex-1 sm:flex-initial">
<div class="relative">
<Search class="absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground" />
<Input
type="search"
placeholder="Search articles..."
class="pl-8 sm:w-[300px] md:w-[200px] lg:w-[300px]"
/>
</div>
</form>
<DropdownMenu.Root>
<DropdownMenu.Trigger asChild let:builder>
<Button builders={[builder]} variant="secondary" size="icon" class="rounded-full">
<CircleUser class="h-5 w-5" />
<span class="sr-only">Toggle user menu</span>
</Button>
</DropdownMenu.Trigger>
<DropdownMenu.Content align="end">
<DropdownMenu.Label>My Account</DropdownMenu.Label>
<DropdownMenu.Separator />
<DropdownMenu.Item>Settings</DropdownMenu.Item>
<DropdownMenu.Item>Support</DropdownMenu.Item>
<DropdownMenu.Separator />
<DropdownMenu.Item>Logout</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu.Root>
</div>
</header>
<main class="flex flex-1 flex-col gap-4 p-4 md:gap-8 md:p-8">
<slot />
</main>
</div>

View File

@ -1,10 +1,220 @@
<script>
/** @type {import('./$types').PageServerData} */
export let data;
<script lang="ts">
import Activity from 'lucide-svelte/icons/activity';
import ArrowUpRight from 'lucide-svelte/icons/arrow-up-right';
import CreditCard from 'lucide-svelte/icons/credit-card';
import DollarSign from 'lucide-svelte/icons/dollar-sign';
import Users from 'lucide-svelte/icons/users';
import * as Avatar from '$lib/components/ui/avatar/index.js';
import { Badge } from '$lib/components/ui/badge/index.js';
import { Button } from '$lib/components/ui/button/index.js';
import * as Card from '$lib/components/ui/card/index.js';
import * as Table from '$lib/components/ui/table/index.js';
</script>
<p>{JSON.stringify(data.me)}</p>
<div class="grid gap-4 md:grid-cols-2 md:gap-8 lg:grid-cols-4">
<Card.Root>
<Card.Header class="flex flex-row items-center justify-between space-y-0 pb-2">
<Card.Title class="text-sm font-medium">Total Articles</Card.Title>
<DollarSign class="h-4 w-4 text-muted-foreground" />
</Card.Header>
<Card.Content>
<div class="text-2xl font-bold">48</div>
<p class="text-xs text-muted-foreground">7 remaining this month</p>
</Card.Content>
</Card.Root>
<Card.Root>
<Card.Header class="flex flex-row items-center justify-between space-y-0 pb-2">
<Card.Title class="text-sm font-medium">Subscriptions</Card.Title>
<Users class="h-4 w-4 text-muted-foreground" />
</Card.Header>
<Card.Content>
<div class="text-2xl font-bold">+2350</div>
<p class="text-xs text-muted-foreground">+180.1% from last month</p>
</Card.Content>
</Card.Root>
<Card.Root>
<Card.Header class="flex flex-row items-center justify-between space-y-0 pb-2">
<Card.Title class="text-sm font-medium">Emails collected</Card.Title>
<CreditCard class="h-4 w-4 text-muted-foreground" />
</Card.Header>
<Card.Content>
<div class="text-2xl font-bold">+12,234</div>
<p class="text-xs text-muted-foreground">+19% from last month</p>
</Card.Content>
</Card.Root>
<Card.Root>
<Card.Header class="flex flex-row items-center justify-between space-y-0 pb-2">
<Card.Title class="text-sm font-medium">Reads</Card.Title>
<Activity class="h-4 w-4 text-muted-foreground" />
</Card.Header>
<Card.Content>
<div class="text-2xl font-bold">+573</div>
<p class="text-xs text-muted-foreground">+201 since last hour</p>
</Card.Content>
</Card.Root>
</div>
<div class="grid gap-4 md:gap-8 lg:grid-cols-2 xl:grid-cols-3">
<Card.Root class="xl:col-span-2">
<Card.Header class="flex flex-row items-center">
<div class="grid gap-2">
<Card.Title>Articles</Card.Title>
<Card.Description>Recent articles written from your youtube videos.</Card.Description>
</div>
<Button href="##" size="sm" class="ml-auto gap-1">
View All
<ArrowUpRight class="h-4 w-4" />
</Button>
</Card.Header>
<Card.Content>
<Table.Root>
<Table.Header>
<Table.Row>
<Table.Head>Customer</Table.Head>
<Table.Head class="xl:table.-column hidden">Type</Table.Head>
<Table.Head class="xl:table.-column hidden">Status</Table.Head>
<Table.Head class="xl:table.-column hidden">Date</Table.Head>
<Table.Head class="text-right">Amount</Table.Head>
</Table.Row>
</Table.Header>
<Table.Body>
<Table.Row>
<Table.Cell>
<div class="font-medium">Liam Johnson</div>
<div class="hidden text-sm text-muted-foreground md:inline">liam@example.com</div>
</Table.Cell>
<Table.Cell class="xl:table.-column hidden">Sale</Table.Cell>
<Table.Cell class="xl:table.-column hidden">
<Badge class="text-xs" variant="outline">Approved</Badge>
</Table.Cell>
<Table.Cell class="md:table.-cell xl:table.-column hidden lg:hidden">
2023-06-23
</Table.Cell>
<Table.Cell class="text-right">$250.00</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<div class="font-medium">Olivia Smith</div>
<div class="hidden text-sm text-muted-foreground md:inline">olivia@example.com</div>
</Table.Cell>
<Table.Cell class="xl:table.-column hidden">Refund</Table.Cell>
<Table.Cell class="xl:table.-column hidden">
<Badge class="text-xs" variant="outline">Declined</Badge>
</Table.Cell>
<Table.Cell class="md:table.-cell xl:table.-column hidden lg:hidden">
2023-06-24
</Table.Cell>
<Table.Cell class="text-right">$150.00</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<div class="font-medium">Noah Williams</div>
<div class="hidden text-sm text-muted-foreground md:inline">noah@example.com</div>
</Table.Cell>
<Table.Cell class="xl:table.-column hidden">Subscription</Table.Cell>
<Table.Cell class="xl:table.-column hidden">
<Badge class="text-xs" variant="outline">Approved</Badge>
</Table.Cell>
<Table.Cell class="md:table.-cell xl:table.-column hidden lg:hidden">
2023-06-25
</Table.Cell>
<Table.Cell class="text-right">$350.00</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<div class="font-medium">Emma Brown</div>
<div class="hidden text-sm text-muted-foreground md:inline">emma@example.com</div>
</Table.Cell>
<Table.Cell class="xl:table.-column hidden">Sale</Table.Cell>
<Table.Cell class="xl:table.-column hidden">
<Badge class="text-xs" variant="outline">Approved</Badge>
</Table.Cell>
<Table.Cell class="md:table.-cell xl:table.-column hidden lg:hidden">
2023-06-26
</Table.Cell>
<Table.Cell class="text-right">$450.00</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<div class="font-medium">Liam Johnson</div>
<div class="hidden text-sm text-muted-foreground md:inline">liam@example.com</div>
</Table.Cell>
<Table.Cell class="xl:table.-column hidden">Sale</Table.Cell>
<Table.Cell class="xl:table.-column hidden">
<Badge class="text-xs" variant="outline">Approved</Badge>
</Table.Cell>
<Table.Cell class="md:table.-cell xl:table.-column hidden lg:hidden">
2023-06-27
</Table.Cell>
<Table.Cell class="text-right">$550.00</Table.Cell>
</Table.Row>
</Table.Body>
</Table.Root>
</Card.Content>
</Card.Root>
<Card.Root>
<Card.Header>
<Card.Title>Recent Signups</Card.Title>
</Card.Header>
<Card.Content class="grid gap-8">
<div class="flex items-center gap-4">
<Avatar.Root class="hidden h-9 w-9 sm:flex">
<Avatar.Image src="/avatars/01.png" alt="Avatar" />
<Avatar.Fallback>OM</Avatar.Fallback>
</Avatar.Root>
<div class="grid gap-1">
<p class="text-sm font-medium leading-none">Olivia Martin</p>
<p class="text-sm text-muted-foreground">olivia.martin@email.com</p>
</div>
<div class="ml-auto font-medium">56 seconds ago</div>
</div>
<div class="flex items-center gap-4">
<Avatar.Root class="hidden h-9 w-9 sm:flex">
<Avatar.Image src="/avatars/02.png" alt="Avatar" />
<Avatar.Fallback>JL</Avatar.Fallback>
</Avatar.Root>
<div class="grid gap-1">
<p class="text-sm font-medium leading-none">Jackson Lee</p>
<p class="text-sm text-muted-foreground">jackson.lee@email.com</p>
</div>
<div class="ml-auto font-medium">2 minutes ago</div>
</div>
<div class="flex items-center gap-4">
<Avatar.Root class="hidden h-9 w-9 sm:flex">
<Avatar.Image src="/avatars/03.png" alt="Avatar" />
<Avatar.Fallback>IN</Avatar.Fallback>
</Avatar.Root>
<div class="grid gap-1">
<p class="text-sm font-medium leading-none">Isabella Nguyen</p>
<p class="text-sm text-muted-foreground">isabella.nguyen@email.com</p>
</div>
<div class="ml-auto font-medium">5 minutes ago</div>
</div>
<div class="flex items-center gap-4">
<Avatar.Root class="hidden h-9 w-9 sm:flex">
<Avatar.Image src="/avatars/04.png" alt="Avatar" />
<Avatar.Fallback>WK</Avatar.Fallback>
</Avatar.Root>
<div class="grid gap-1">
<p class="text-sm font-medium leading-none">William Kim</p>
<p class="text-sm text-muted-foreground">will@email.com</p>
</div>
<div class="ml-auto font-medium">9 minutes ago</div>
</div>
<div class="flex items-center gap-4">
<Avatar.Root class="hidden h-9 w-9 sm:flex">
<Avatar.Image src="/avatars/05.png" alt="Avatar" />
<Avatar.Fallback>SD</Avatar.Fallback>
</Avatar.Root>
<div class="grid gap-1">
<p class="text-sm font-medium leading-none">Sofia Davis</p>
<p class="text-sm text-muted-foreground">sofia.davis@email.com</p>
</div>
<div class="ml-auto font-medium">15 minutes ago</div>
</div>
</Card.Content>
</Card.Root>
</div>
<!-- <p>{JSON.stringify(data)}</p>
{#await me}
<p>loading...</p>

View File

@ -0,0 +1,18 @@
import { config } from "$lib"
import { superValidate } from "sveltekit-superforms";
import { formSchema } from "./schema";
import { zod } from "sveltekit-superforms/adapters";
/** @type {import("./$types").PageServerLoad} */
export const load = async ({fetch}) => {
const res = await fetch(config.api_url+"/blog?mine=true", {
credentials: 'include'
});
const data = await res.json();
console.log(data);
return {
articles: data,
form: await superValidate(zod(formSchema)),
}
}

View File

@ -0,0 +1,180 @@
<script>
import * as Table from '$lib/components/ui/table/index.js';
import { ExternalLink, Pen, Trash } from 'lucide-svelte';
import * as Dialog from '$lib/components/ui/dialog';
import * as Select from '$lib/components/ui/select';
import * as Form from '$lib/components/ui/form';
import { Button, buttonVariants } from '$lib/components/ui/button';
import { Input } from '$lib/components/ui/input';
import TooltipButton from '$lib/components/molecules/tooltipbutton.svelte';
import ProBadge from '$lib/components/molecules/probadge.svelte';
import { formSchema } from "./schema";
import { superForm } from 'sveltekit-superforms';
import { zodClient } from 'sveltekit-superforms/adapters';
/** @type {import("./$types").PageData} */
export let data;
const form = superForm(data.form, {
validators: zodClient(formSchema)
});
const { form: formData, enhance } = form;
const invoices = [
{
id: 'AG64NE',
title: 'Nullam ornare ornare orci a auctor.',
preview:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam ornare ornare orci a auctor. Mauris gravida luctus vulputate. Cras porttitor, mi ut pharetra blandit, dolor elit convallis velit, volutpat dictum quam enim ut purus.',
source: 'Youtube'
},
{
id: 'AG64NE',
title: 'Nullam ornare ornare orci a auctor.',
preview:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam ornare ornare orci a auctor. Mauris gravida luctus vulputate. Cras porttitor, mi ut pharetra blandit, dolor elit convallis velit, volutpat dictum quam enim ut purus.',
source: 'Youtube'
},
{
id: 'AG64NE',
title: 'Nullam ornare ornare orci a auctor.',
preview:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam ornare ornare orci a auctor. Mauris gravida luctus vulputate. Cras porttitor, mi ut pharetra blandit, dolor elit convallis velit, volutpat dictum quam enim ut purus.',
source: 'Youtube'
},
{
id: 'AG64NE',
title: 'Nullam ornare ornare orci a auctor.',
preview:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam ornare ornare orci a auctor. Mauris gravida luctus vulputate. Cras porttitor, mi ut pharetra blandit, dolor elit convallis velit, volutpat dictum quam enim ut purus.',
source: 'Youtube'
},
{
id: 'AG64NE',
title: 'Nullam ornare ornare orci a auctor.',
preview:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam ornare ornare orci a auctor. Mauris gravida luctus vulputate. Cras porttitor, mi ut pharetra blandit, dolor elit convallis velit, volutpat dictum quam enim ut purus.',
source: 'Youtube'
}
];
</script>
<div class="mx-auto max-w-[1500px]">
<Dialog.Root>
<Dialog.Trigger class={buttonVariants({ variant: 'default' })}>Create Article</Dialog.Trigger>
<Dialog.Content class="sm:max-w-[750px]">
<Dialog.Header>
<Dialog.Title>Create Article</Dialog.Title>
<Dialog.Description>
Configure your article and let our AI do the writing!
</Dialog.Description>
</Dialog.Header>
<form method="POST" use:enhance name="blog-converter">
<Form.Field {form} name="email">
<Form.Control let:attrs>
<div class="grid gap-4 py-4">
<div class="grid grid-cols-4 items-center gap-4">
<Form.Label for="youtube_url" class="text-right">Youtube video URL*</Form.Label>
<Input
id="youtube_url"
placeholder="www.youtube.com/watch?v=..."
class="col-span-3"
/>
</div>
<div class="grid grid-cols-4 items-center gap-4">
<Form.Label for="length" class="text-right">Article length</Form.Label>
<Select.Root portal={null} name="length">
<Select.Trigger class="w-[300px]">
<Select.Value />
</Select.Trigger>
<Select.Content>
<Select.Group>
<Select.Item value="short" label="Short (~700 words)"
>Short (~700 words)</Select.Item
>
<Select.Item value="medium" label="Medium (~1500 words)"
>Medium (~1500 words)<ProBadge /></Select.Item
>
<Select.Item value="long" label="Long (~2500 words)"
>Long (~2500 words)<ProBadge /></Select.Item
>
</Select.Group>
</Select.Content>
<Select.Input name="blogLength" />
</Select.Root>
</div>
<div class="grid grid-cols-4 items-center gap-4">
<Form.Label for="length" class="text-right">Format</Form.Label>
<Select.Root portal={null} name="length">
<Select.Trigger class="w-[200px]">
<Select.Value />
</Select.Trigger>
<Select.Content>
<Select.Group>
<Select.Item value="summary" label="Summary">Summary</Select.Item>
<Select.Item value="listicle" label="Listicle">Listicle</Select.Item>
<Select.Item value="product review" label="Product Review"
>Product Review</Select.Item
>
<Select.Item value="news report" label="News Report">News Report</Select.Item>
<Select.Item value="tutorial" label="Tutorial">Tutorial</Select.Item>
</Select.Group>
</Select.Content>
<Select.Input name="blogFormat" />
</Select.Root>
</div>
</div>
</Form.Control>
<Form.Description />
<Form.FieldErrors />
</Form.Field>
</form>
<Dialog.Footer>
<Button type="submit" form="blog-converter">Create</Button>
</Dialog.Footer>
</Dialog.Content>
</Dialog.Root>
<Table.Root>
<Table.Caption>A list of your recent articles.</Table.Caption>
<Table.Header>
<Table.Row>
<Table.Head class="w-[100px]">ID</Table.Head>
<Table.Head class="w-[300px]">Title</Table.Head>
<Table.Head class="w-[500px]">Preview</Table.Head>
<Table.Head>Source</Table.Head>
<Table.Head>Actions</Table.Head>
</Table.Row>
</Table.Header>
<Table.Body>
{#each invoices as invoice, i (i)}
<Table.Row>
<Table.Cell class="font-medium">{invoice.id}</Table.Cell>
<Table.Cell class="max-w-16 overflow-hidden overflow-ellipsis text-nowrap"
>{invoice.title}</Table.Cell
>
<Table.Cell class="max-w-10 overflow-hidden overflow-ellipsis text-nowrap"
>{invoice.preview}</Table.Cell
>
<Table.Cell>{invoice.source}</Table.Cell>
<Table.Cell>
<TooltipButton variant="outline" size="icon" tip="Preview">
<ExternalLink size="1rem" />
</TooltipButton>
<TooltipButton variant="outline" size="icon" tip="Edit">
<Pen size="1rem" />
</TooltipButton>
<TooltipButton variant="outline" size="icon" tip="Delete">
<Trash size="1rem" />
</TooltipButton>
</Table.Cell>
</Table.Row>
{/each}
</Table.Body>
</Table.Root>
</div>
<!-- <p>{JSON.stringify(data)}</p> -->

View File

@ -0,0 +1,9 @@
import { z } from "zod";
export const formSchema = z.object({
youtube_url: z.string().url(),
length: z.enum(["short", "medium", "long"]).optional(),
format: z.enum(["summary", "listicle", "product review", "news report", "tutorial"]).optional(),
});
/** @typedef {typeof formSchema} FormSchema */

View File

@ -0,0 +1,6 @@
<script>
import { config } from "$lib";
</script>
<a href="{config.api_url}/auth/google">Log in here you fat bastard</a>

BIN
static/avatars/01.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

BIN
static/avatars/02.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

BIN
static/avatars/03.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

BIN
static/avatars/04.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

BIN
static/avatars/05.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

View File

@ -1,3 +1,4 @@
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
import adapter from '@sveltejs/adapter-auto';
/** @type {import('@sveltejs/kit').Config} */
@ -7,7 +8,9 @@ const config = {
// If your environment is not supported, or you settled on a specific environment, switch out the adapter.
// See https://kit.svelte.dev/docs/adapters for more information about adapters.
adapter: adapter()
}
},
preprocess: [vitePreprocess({})]
};
export default config;

64
tailwind.config.js Normal file
View File

@ -0,0 +1,64 @@
import { fontFamily } from "tailwindcss/defaultTheme";
/** @type {import('tailwindcss').Config} */
const config = {
darkMode: ["class"],
content: ["./src/**/*.{html,js,svelte,ts}"],
safelist: ["dark"],
theme: {
container: {
center: true,
padding: "2rem",
screens: {
"2xl": "1400px"
}
},
extend: {
colors: {
border: "hsl(var(--border) / <alpha-value>)",
input: "hsl(var(--input) / <alpha-value>)",
ring: "hsl(var(--ring) / <alpha-value>)",
background: "hsl(var(--background) / <alpha-value>)",
foreground: "hsl(var(--foreground) / <alpha-value>)",
primary: {
DEFAULT: "hsl(var(--primary) / <alpha-value>)",
foreground: "hsl(var(--primary-foreground) / <alpha-value>)"
},
secondary: {
DEFAULT: "hsl(var(--secondary) / <alpha-value>)",
foreground: "hsl(var(--secondary-foreground) / <alpha-value>)"
},
destructive: {
DEFAULT: "hsl(var(--destructive) / <alpha-value>)",
foreground: "hsl(var(--destructive-foreground) / <alpha-value>)"
},
muted: {
DEFAULT: "hsl(var(--muted) / <alpha-value>)",
foreground: "hsl(var(--muted-foreground) / <alpha-value>)"
},
accent: {
DEFAULT: "hsl(var(--accent) / <alpha-value>)",
foreground: "hsl(var(--accent-foreground) / <alpha-value>)"
},
popover: {
DEFAULT: "hsl(var(--popover) / <alpha-value>)",
foreground: "hsl(var(--popover-foreground) / <alpha-value>)"
},
card: {
DEFAULT: "hsl(var(--card) / <alpha-value>)",
foreground: "hsl(var(--card-foreground) / <alpha-value>)"
}
},
borderRadius: {
lg: "var(--radius)",
md: "calc(var(--radius) - 2px)",
sm: "calc(var(--radius) - 4px)"
},
fontFamily: {
sans: [...fontFamily.sans]
}
}
},
};
export default config;