diff --git a/apps/recorder/.devcontainer/Dockerfile b/.devcontainer.bk/Dockerfile
similarity index 100%
rename from apps/recorder/.devcontainer/Dockerfile
rename to .devcontainer.bk/Dockerfile
diff --git a/apps/recorder/.devcontainer/devcontainer.json b/.devcontainer.bk/devcontainer.json
similarity index 100%
rename from apps/recorder/.devcontainer/devcontainer.json
rename to .devcontainer.bk/devcontainer.json
diff --git a/apps/recorder/.devcontainer/docker-compose.yml b/.devcontainer.bk/docker-compose.yml
similarity index 100%
rename from apps/recorder/.devcontainer/docker-compose.yml
rename to .devcontainer.bk/docker-compose.yml
diff --git a/apps/app/.env.development b/apps/app/.env.development
deleted file mode 100644
index d0abda2..0000000
--- a/apps/app/.env.development
+++ /dev/null
@@ -1,29 +0,0 @@
-# Server
-AUTH_TYPE="basic" #
-
-BASIC_USER="konobangu"
-BASIC_PASSWORD="konobangu"
-
-OIDC_PROVIDER_ENDPOINT="https://some-oidc-auth.com/oidc/.well-known/openid-configuration"
-OIDC_CLIENT_ID=""
-OIDC_CLIENT_SECRET=""
-OIDC_ISSUER="https://some-oidc-auth.com/oidc"
-OIDC_AUDIENCE="https://konobangu.com/api"
-OIDC_ICON_URL=""
-OIDC_EXTRA_SCOPE_REGEX=""
-OIDC_EXTRA_CLAIM_KEY=""
-OIDC_EXTRA_CLAIM_VALUE=""
-
-DATABASE_URL="postgres://konobangu:konobangu@127.0.0.1:5432/konobangu"
-BETTERSTACK_API_KEY=""
-BETTERSTACK_URL=""
-FLAGS_SECRET=""
-ARCJET_KEY=""
-SVIX_TOKEN=""
-LIVEBLOCKS_SECRET=""
-
-# Client
-NEXT_PUBLIC_APP_URL="http://localhost:5000"
-NEXT_PUBLIC_WEB_URL="http://localhost:5001"
-NEXT_PUBLIC_DOCS_URL="http://localhost:5004"
-NEXT_PUBLIC_VERCEL_PROJECT_PRODUCTION_URL="https://konobangu.com"
\ No newline at end of file
diff --git a/apps/app/.env.example b/apps/app/.env.example
deleted file mode 100644
index 059e679..0000000
--- a/apps/app/.env.example
+++ /dev/null
@@ -1,29 +0,0 @@
-# AUTH
-AUTH_TYPE="basic"
-
-NEXT_PUBLIC_OIDC_PROVIDER_ENDPOINT="https://some-oidc-auth.com/oidc/.well-known/openid-configuration"
-NEXT_PUBLIC_OIDC_CLIENT_ID=""
-NEXT_PUBLIC_OIDC_CLIENT_SECRET=""
-NEXT_PUBLIC_OIDC_ICON_URL=""
-OIDC_ISSUER="https://some-oidc-auth.com/oidc"
-OIDC_AUDIENCE="https://konobangu.com/api"
-OIDC_EXTRA_SCOPES="" # 如 "read:konobangu,write:konobangu"
-OIDC_EXTRA_CLAIM_KEY=""
-OIDC_EXTRA_CLAIM_VALUE=""
-
-# DATABASE
-DATABASE_URL="postgres://konobangu:konobangu@127.0.0.1:5432/konobangu"
-
-# SERVER MISC
-BETTERSTACK_API_KEY=""
-BETTERSTACK_URL=""
-FLAGS_SECRET=""
-ARCJET_KEY=""
-SVIX_TOKEN=""
-LIVEBLOCKS_SECRET=""
-
-# WEBUI
-NEXT_PUBLIC_APP_URL="http://localhost:5000"
-NEXT_PUBLIC_WEB_URL="http://localhost:5001"
-NEXT_PUBLIC_DOCS_URL="http://localhost:5004"
-NEXT_PUBLIC_VERCEL_PROJECT_PRODUCTION_URL="https://konobangu.com"
\ No newline at end of file
diff --git a/apps/app/.gitignore b/apps/app/.gitignore
deleted file mode 100644
index 9045095..0000000
--- a/apps/app/.gitignore
+++ /dev/null
@@ -1,45 +0,0 @@
-# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
-
-# dependencies
-/node_modules
-/.pnp
-.pnp.js
-
-# testing
-/coverage
-
-# next.js
-/.next/
-/out/
-
-# production
-/build
-
-# misc
-.DS_Store
-*.pem
-
-# debug
-npm-debug.log*
-yarn-debug.log*
-yarn-error.log*
-.pnpm-debug.log*
-
-# local env files
-.env*.local
-
-# vercel
-.vercel
-
-# typescript
-*.tsbuildinfo
-next-env.d.ts
-
-# prisma
-.env
-
-# react.email
-.react-email
-
-# Sentry
-.sentryclirc
diff --git a/apps/app/__tests__/sign-in.test.tsx b/apps/app/__tests__/sign-in.test.tsx
deleted file mode 100644
index 5bd4c28..0000000
--- a/apps/app/__tests__/sign-in.test.tsx
+++ /dev/null
@@ -1,13 +0,0 @@
-import { render, screen } from '@testing-library/react';
-import { expect, test } from 'vitest';
-import Page from '../app/(unauthenticated)/sign-in/[[...sign-in]]/page';
-
-test('Sign In Page', () => {
- render( );
- expect(
- screen.getByRole('heading', {
- level: 1,
- name: 'Welcome back',
- })
- ).toBeDefined();
-});
diff --git a/apps/app/__tests__/sign-up.test.tsx b/apps/app/__tests__/sign-up.test.tsx
deleted file mode 100644
index 9a4dcbe..0000000
--- a/apps/app/__tests__/sign-up.test.tsx
+++ /dev/null
@@ -1,13 +0,0 @@
-import { render, screen } from '@testing-library/react';
-import { expect, test } from 'vitest';
-import Page from '../app/(unauthenticated)/sign-up/[[...sign-up]]/page';
-
-test('Sign Up Page', () => {
- render( );
- expect(
- screen.getByRole('heading', {
- level: 1,
- name: 'Create an account',
- })
- ).toBeDefined();
-});
diff --git a/apps/app/app/(authenticated)/components/avatar-stack.tsx b/apps/app/app/(authenticated)/components/avatar-stack.tsx
deleted file mode 100644
index 1e926d5..0000000
--- a/apps/app/app/(authenticated)/components/avatar-stack.tsx
+++ /dev/null
@@ -1,59 +0,0 @@
-'use client';
-
-import { useOthers, useSelf } from '@konobangu/collaboration/hooks';
-import {
- Avatar,
- AvatarFallback,
- AvatarImage,
-} from '@konobangu/design-system/components/ui/avatar';
-import {
- Tooltip,
- TooltipContent,
- TooltipTrigger,
-} from '@konobangu/design-system/components/ui/tooltip';
-import { tailwind } from '@konobangu/tailwind-config';
-
-type PresenceAvatarProps = {
- info?: Liveblocks['UserMeta']['info'];
-};
-
-const PresenceAvatar = ({ info }: PresenceAvatarProps) => (
-
-
-
-
-
- {info?.name?.slice(0, 2)}
-
-
-
-
- {info?.name ?? 'Unknown'}
-
-
-);
-
-export const AvatarStack = () => {
- const others = useOthers();
- const self = useSelf();
- const hasMoreUsers = others.length > 3;
-
- return (
-
- {others.slice(0, 3).map(({ connectionId, info }) => (
-
- ))}
-
- {hasMoreUsers && (
-
- )}
-
- {self &&
}
-
- );
-};
diff --git a/apps/app/app/(authenticated)/components/collaboration-provider.tsx b/apps/app/app/(authenticated)/components/collaboration-provider.tsx
deleted file mode 100644
index 94bb373..0000000
--- a/apps/app/app/(authenticated)/components/collaboration-provider.tsx
+++ /dev/null
@@ -1,48 +0,0 @@
-'use client';
-
-import { getUsers } from '@/app/actions/users/get';
-import { searchUsers } from '@/app/actions/users/search';
-import { Room } from '@konobangu/collaboration/room';
-import type { ReactNode } from 'react';
-
-export const CollaborationProvider = ({
- orgId,
- children,
-}: {
- orgId: string;
- children: ReactNode;
-}) => {
- const resolveUsers = async ({ userIds }: { userIds: string[] }) => {
- const response = await getUsers(userIds);
-
- if ('error' in response) {
- throw new Error('Problem resolving users');
- }
-
- return response.data;
- };
-
- const resolveMentionSuggestions = async ({ text }: { text: string }) => {
- const response = await searchUsers(text);
-
- if ('error' in response) {
- throw new Error('Problem resolving mention suggestions');
- }
-
- return response.data;
- };
-
- return (
- Loading...
- }
- resolveUsers={resolveUsers}
- resolveMentionSuggestions={resolveMentionSuggestions}
- >
- {children}
-
- );
-};
diff --git a/apps/app/app/(authenticated)/components/cursors.tsx b/apps/app/app/(authenticated)/components/cursors.tsx
deleted file mode 100644
index d630598..0000000
--- a/apps/app/app/(authenticated)/components/cursors.tsx
+++ /dev/null
@@ -1,106 +0,0 @@
-'use client';
-
-import { useMyPresence, useOthers } from '@konobangu/collaboration/hooks';
-import { useEffect } from 'react';
-
-const Cursor = ({
- name,
- color,
- x,
- y,
-}: {
- name: string | undefined;
- color: string;
- x: number;
- y: number;
-}) => (
-
-
- Cursor
-
-
-
- {name}
-
-
-);
-
-export const Cursors = () => {
- /**
- * useMyPresence returns the presence of the current user and a function to update it.
- * updateMyPresence is different than the setState function returned by the useState hook from React.
- * You don't need to pass the full presence object to update it.
- * See https://liveblocks.io/docs/api-reference/liveblocks-react#useMyPresence for more information
- */
- const [_cursor, updateMyPresence] = useMyPresence();
-
- /**
- * Return all the other users in the room and their presence (a cursor position in this case)
- */
- const others = useOthers();
-
- useEffect(() => {
- const onPointerMove = (event: PointerEvent) => {
- // Update the user cursor position on every pointer move
- updateMyPresence({
- cursor: {
- x: Math.round(event.clientX),
- y: Math.round(event.clientY),
- },
- });
- };
-
- const onPointerLeave = () => {
- // When the pointer goes out, set cursor to null
- updateMyPresence({
- cursor: null,
- });
- };
-
- document.body.addEventListener('pointermove', onPointerMove);
- document.body.addEventListener('pointerleave', onPointerLeave);
-
- return () => {
- document.body.removeEventListener('pointermove', onPointerMove);
- document.body.removeEventListener('pointerleave', onPointerLeave);
- };
- }, [updateMyPresence]);
-
- return others.map(({ connectionId, presence, info }) => {
- if (!presence.cursor) {
- return null;
- }
-
- return (
-
- );
- });
-};
diff --git a/apps/app/app/(authenticated)/components/header.tsx b/apps/app/app/(authenticated)/components/header.tsx
deleted file mode 100644
index 138eb88..0000000
--- a/apps/app/app/(authenticated)/components/header.tsx
+++ /dev/null
@@ -1,43 +0,0 @@
-import {
- Breadcrumb,
- BreadcrumbItem,
- BreadcrumbLink,
- BreadcrumbList,
- BreadcrumbPage,
- BreadcrumbSeparator,
-} from '@konobangu/design-system/components/ui/breadcrumb';
-import { Separator } from '@konobangu/design-system/components/ui/separator';
-import { SidebarTrigger } from '@konobangu/design-system/components/ui/sidebar';
-import { Fragment, type ReactNode } from 'react';
-
-type HeaderProps = {
- pages: string[];
- page: string;
- children?: ReactNode;
-};
-
-export const Header = ({ pages, page, children }: HeaderProps) => (
-
-);
diff --git a/apps/app/app/(authenticated)/components/posthog-identifier.tsx b/apps/app/app/(authenticated)/components/posthog-identifier.tsx
deleted file mode 100644
index bda1472..0000000
--- a/apps/app/app/(authenticated)/components/posthog-identifier.tsx
+++ /dev/null
@@ -1,44 +0,0 @@
-'use client';
-
-import { analytics } from '@konobangu/analytics/client';
-import { useSession } from '@konobangu/auth/client';
-import { usePathname, useSearchParams } from 'next/navigation';
-import { useEffect, useRef } from 'react';
-
-export const PostHogIdentifier = () => {
- const session = useSession();
- const user = session?.data?.user;
- const identified = useRef(false);
- const pathname = usePathname();
- const searchParams = useSearchParams();
-
- useEffect(() => {
- // Track pageviews
- if (pathname && analytics) {
- let url = window.origin + pathname;
- if (searchParams.toString()) {
- url = `${url}?${searchParams.toString()}`;
- }
- analytics.capture('$pageview', {
- $current_url: url,
- });
- }
- }, [pathname, searchParams]);
-
- useEffect(() => {
- if (!user || identified.current) {
- return;
- }
-
- analytics.identify(user.id, {
- email: user.email,
- name: user.name,
- createdAt: user.createdAt,
- avatar: user.image,
- });
-
- identified.current = true;
- }, [user]);
-
- return null;
-};
diff --git a/apps/app/app/(authenticated)/components/sidebar.tsx b/apps/app/app/(authenticated)/components/sidebar.tsx
deleted file mode 100644
index 5a5c103..0000000
--- a/apps/app/app/(authenticated)/components/sidebar.tsx
+++ /dev/null
@@ -1,342 +0,0 @@
-'use client';
-
-// import { OrganizationSwitcher, UserButton } from '@konobangu/auth/client';
-import { ModeToggle } from '@konobangu/design-system/components/mode-toggle';
-import {
- Collapsible,
- CollapsibleContent,
- CollapsibleTrigger,
-} from '@konobangu/design-system/components/ui/collapsible';
-import {
- DropdownMenu,
- DropdownMenuContent,
- DropdownMenuItem,
- DropdownMenuSeparator,
- DropdownMenuTrigger,
-} from '@konobangu/design-system/components/ui/dropdown-menu';
-import {
- Sidebar,
- SidebarContent,
- SidebarFooter,
- SidebarGroup,
- SidebarGroupContent,
- SidebarGroupLabel,
- SidebarHeader,
- SidebarInset,
- SidebarMenu,
- SidebarMenuAction,
- SidebarMenuButton,
- SidebarMenuItem,
- SidebarMenuSub,
- SidebarMenuSubButton,
- SidebarMenuSubItem,
- useSidebar,
-} from '@konobangu/design-system/components/ui/sidebar';
-import { cn } from '@konobangu/design-system/lib/utils';
-import {
- AnchorIcon,
- BookOpenIcon,
- BotIcon,
- ChevronRightIcon,
- FolderIcon,
- FrameIcon,
- LifeBuoyIcon,
- MapIcon,
- MoreHorizontalIcon,
- PieChartIcon,
- SendIcon,
- Settings2Icon,
- ShareIcon,
- SquareTerminalIcon,
- Trash2Icon,
-} from 'lucide-react';
-import type { ReactNode } from 'react';
-
-type GlobalSidebarProperties = {
- readonly children: ReactNode;
-};
-
-const data = {
- user: {
- name: 'shadcn',
- email: 'm@example.com',
- avatar: '/avatars/shadcn.jpg',
- },
- navMain: [
- {
- title: 'Playground',
- url: '#',
- icon: SquareTerminalIcon,
- isActive: true,
- items: [
- {
- title: 'History',
- url: '#',
- },
- {
- title: 'Starred',
- url: '#',
- },
- {
- title: 'Settings',
- url: '#',
- },
- ],
- },
- {
- title: 'Models',
- url: '#',
- icon: BotIcon,
- items: [
- {
- title: 'Genesis',
- url: '#',
- },
- {
- title: 'Explorer',
- url: '#',
- },
- {
- title: 'Quantum',
- url: '#',
- },
- ],
- },
- {
- title: 'Documentation',
- url: '#',
- icon: BookOpenIcon,
- items: [
- {
- title: 'Introduction',
- url: '#',
- },
- {
- title: 'Get Started',
- url: '#',
- },
- {
- title: 'Tutorials',
- url: '#',
- },
- {
- title: 'Changelog',
- url: '#',
- },
- ],
- },
- {
- title: 'Settings',
- url: '#',
- icon: Settings2Icon,
- items: [
- {
- title: 'General',
- url: '#',
- },
- {
- title: 'Team',
- url: '#',
- },
- {
- title: 'Billing',
- url: '#',
- },
- {
- title: 'Limits',
- url: '#',
- },
- ],
- },
- ],
- navSecondary: [
- {
- title: 'Webhooks',
- url: '/webhooks',
- icon: AnchorIcon,
- },
- {
- title: 'Support',
- url: '#',
- icon: LifeBuoyIcon,
- },
- {
- title: 'Feedback',
- url: '#',
- icon: SendIcon,
- },
- ],
- projects: [
- {
- name: 'Design Engineering',
- url: '#',
- icon: FrameIcon,
- },
- {
- name: 'Sales & Marketing',
- url: '#',
- icon: PieChartIcon,
- },
- {
- name: 'Travel',
- url: '#',
- icon: MapIcon,
- },
- ],
-};
-
-export const GlobalSidebar = ({ children }: GlobalSidebarProperties) => {
- const sidebar = useSidebar();
-
- return (
- <>
-
-
-
-
- div]:w-full',
- sidebar.open ? '' : '-mx-1'
- )}
- >
- {/* */}
-
-
-
-
-
-
- Platform
-
- {data.navMain.map((item) => (
-
-
-
-
-
- {item.title}
-
-
- {item.items?.length ? (
- <>
-
-
-
- Toggle
-
-
-
-
- {item.items?.map((subItem) => (
-
-
-
- {subItem.title}
-
-
-
- ))}
-
-
- >
- ) : null}
-
-
- ))}
-
-
-
- Projects
-
- {data.projects.map((item) => (
-
-
-
-
- {item.name}
-
-
-
-
-
-
- More
-
-
-
-
-
- View Project
-
-
-
- Share Project
-
-
-
-
- Delete Project
-
-
-
-
- ))}
-
-
-
- More
-
-
-
-
-
-
-
- {data.navSecondary.map((item) => (
-
-
-
-
- {item.title}
-
-
-
- ))}
-
-
-
-
-
-
-
- {/* */}
-
-
-
-
-
- {children}
- >
- );
-};
diff --git a/apps/app/app/(authenticated)/layout.tsx b/apps/app/app/(authenticated)/layout.tsx
deleted file mode 100644
index ccf64bd..0000000
--- a/apps/app/app/(authenticated)/layout.tsx
+++ /dev/null
@@ -1,42 +0,0 @@
-import { getSessionFromHeaders } from '@konobangu/auth/server';
-import { SidebarProvider } from '@konobangu/design-system/components/ui/sidebar';
-import { env } from '@konobangu/env';
-import { showBetaFeature } from '@konobangu/feature-flags';
-import { secure } from '@konobangu/security';
-import { redirect } from 'next/navigation';
-import type { ReactNode } from 'react';
-import { PostHogIdentifier } from './components/posthog-identifier';
-import { GlobalSidebar } from './components/sidebar';
-
-type AppLayoutProperties = {
- readonly children: ReactNode;
-};
-
-const AppLayout = async ({ children }: AppLayoutProperties) => {
- if (env.ARCJET_KEY) {
- await secure(['CATEGORY:PREVIEW']);
- }
-
- const { user } = await getSessionFromHeaders();
-
- if (!user) {
- return redirect('/sign-in'); // from next/navigation
- }
- const betaFeature = await showBetaFeature();
-
- return (
-
-
- {betaFeature && (
-
- Beta feature now available
-
- )}
- {children}
-
-
-
- );
-};
-
-export default AppLayout;
diff --git a/apps/app/app/(authenticated)/page.tsx b/apps/app/app/(authenticated)/page.tsx
deleted file mode 100644
index ddaafd1..0000000
--- a/apps/app/app/(authenticated)/page.tsx
+++ /dev/null
@@ -1,57 +0,0 @@
-import { getSessionFromHeaders } from '@konobangu/auth/server';
-import { database } from '@konobangu/database';
-import { env } from '@konobangu/env';
-import type { Metadata } from 'next';
-import dynamic from 'next/dynamic';
-import { notFound } from 'next/navigation';
-import { AvatarStack } from './components/avatar-stack';
-import { Cursors } from './components/cursors';
-import { Header } from './components/header';
-
-const title = 'Acme Inc';
-const description = 'My application.';
-
-const CollaborationProvider = dynamic(() =>
- import('./components/collaboration-provider').then(
- (mod) => mod.CollaborationProvider
- )
-);
-
-export const metadata: Metadata = {
- title,
- description,
-};
-
-const App = async () => {
- const pages = await database.selectFrom('page').selectAll().execute();
- const { orgId } = await getSessionFromHeaders();
-
- if (!orgId) {
- notFound();
- }
-
- return (
- <>
-
- {env.LIVEBLOCKS_SECRET && (
-
-
-
-
- )}
-
-
-
- {pages.map((page) => (
-
- {page.name}
-
- ))}
-
-
-
- >
- );
-};
-
-export default App;
diff --git a/apps/app/app/(authenticated)/webhooks/page.tsx b/apps/app/app/(authenticated)/webhooks/page.tsx
deleted file mode 100644
index b0bae17..0000000
--- a/apps/app/app/(authenticated)/webhooks/page.tsx
+++ /dev/null
@@ -1,29 +0,0 @@
-import { webhooks } from '@konobangu/webhooks';
-import { notFound } from 'next/navigation';
-
-export const metadata = {
- title: 'Webhooks',
- description: 'Send webhooks to your users.',
-};
-
-const WebhooksPage = async () => {
- const response = await webhooks.getAppPortal();
-
- if (!response?.url) {
- notFound();
- }
-
- return (
-
-
-
- );
-};
-
-export default WebhooksPage;
diff --git a/apps/app/app/(unauthenticated)/layout.tsx b/apps/app/app/(unauthenticated)/layout.tsx
deleted file mode 100644
index 7f3dcd0..0000000
--- a/apps/app/app/(unauthenticated)/layout.tsx
+++ /dev/null
@@ -1,58 +0,0 @@
-import { ModeToggle } from '@konobangu/design-system/components/mode-toggle';
-import { env } from '@konobangu/env';
-import { CommandIcon } from 'lucide-react';
-import Link from 'next/link';
-import type { ReactNode } from 'react';
-
-type AuthLayoutProps = {
- readonly children: ReactNode;
-};
-
-const AuthLayout = ({ children }: AuthLayoutProps) => (
-
-
-
-
-
- Acme Inc
-
-
-
-
-
-
-
- “This library has saved me countless hours of work and helped
- me deliver stunning designs to my clients faster than ever
- before.”
-
-
-
-
-
-
-
- {children}
-
- By clicking continue, you agree to our{' '}
-
- Terms of Service
- {' '}
- and{' '}
-
- Privacy Policy
-
- .
-
-
-
-
-);
-
-export default AuthLayout;
diff --git a/apps/app/app/(unauthenticated)/sign-in/[[...sign-in]]/page.tsx b/apps/app/app/(unauthenticated)/sign-in/[[...sign-in]]/page.tsx
deleted file mode 100644
index b3f658a..0000000
--- a/apps/app/app/(unauthenticated)/sign-in/[[...sign-in]]/page.tsx
+++ /dev/null
@@ -1,23 +0,0 @@
-import { createMetadata } from '@konobangu/seo/metadata';
-import type { Metadata } from 'next';
-import dynamic from 'next/dynamic';
-
-const title = 'Welcome back';
-const description = 'Enter your details to sign in.';
-const SignIn = dynamic(() =>
- import('@konobangu/auth/components/sign-in').then((mod) => mod.SignIn)
-);
-
-export const metadata: Metadata = createMetadata({ title, description });
-
-const SignInPage = () => (
- <>
-
-
{title}
-
{description}
-
-
- >
-);
-
-export default SignInPage;
diff --git a/apps/app/app/(unauthenticated)/sign-up/[[...sign-up]]/page.tsx b/apps/app/app/(unauthenticated)/sign-up/[[...sign-up]]/page.tsx
deleted file mode 100644
index 280e4f3..0000000
--- a/apps/app/app/(unauthenticated)/sign-up/[[...sign-up]]/page.tsx
+++ /dev/null
@@ -1,23 +0,0 @@
-import { createMetadata } from '@konobangu/seo/metadata';
-import type { Metadata } from 'next';
-import dynamic from 'next/dynamic';
-
-const title = 'Create an account';
-const description = 'Enter your details to get started.';
-const SignUp = dynamic(() =>
- import('@konobangu/auth/components/sign-up').then((mod) => mod.SignUp)
-);
-
-export const metadata: Metadata = createMetadata({ title, description });
-
-const SignUpPage = () => (
- <>
-
-
{title}
-
{description}
-
-
- >
-);
-
-export default SignUpPage;
diff --git a/apps/app/app/.well-known/vercel/flags/route.ts b/apps/app/app/.well-known/vercel/flags/route.ts
deleted file mode 100644
index 955eaed..0000000
--- a/apps/app/app/.well-known/vercel/flags/route.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-import { getFlags } from '@konobangu/feature-flags/access';
-
-export const GET = getFlags;
diff --git a/apps/app/app/actions/users/get.ts b/apps/app/app/actions/users/get.ts
deleted file mode 100644
index ab94463..0000000
--- a/apps/app/app/actions/users/get.ts
+++ /dev/null
@@ -1,63 +0,0 @@
-'use server';
-
-import {
- getFullOrganizationFromSession,
- getSessionFromHeaders,
-} from '@konobangu/auth/server';
-import { tailwind } from '@konobangu/tailwind-config';
-
-const colors = [
- tailwind.theme.colors.red[500],
- tailwind.theme.colors.orange[500],
- tailwind.theme.colors.amber[500],
- tailwind.theme.colors.yellow[500],
- tailwind.theme.colors.lime[500],
- tailwind.theme.colors.green[500],
- tailwind.theme.colors.emerald[500],
- tailwind.theme.colors.teal[500],
- tailwind.theme.colors.cyan[500],
- tailwind.theme.colors.sky[500],
- tailwind.theme.colors.blue[500],
- tailwind.theme.colors.indigo[500],
- tailwind.theme.colors.violet[500],
- tailwind.theme.colors.purple[500],
- tailwind.theme.colors.fuchsia[500],
- tailwind.theme.colors.pink[500],
- tailwind.theme.colors.rose[500],
-];
-
-export const getUsers = async (
- userIds: string[]
-): Promise<
- | {
- data: Liveblocks['UserMeta']['info'][];
- }
- | {
- error: unknown;
- }
-> => {
- try {
- const session = await getSessionFromHeaders();
- const { orgId } = session;
-
- if (!orgId) {
- throw new Error('Not logged in');
- }
-
- const { fullOrganization } = await getFullOrganizationFromSession(session);
-
- const members = fullOrganization?.members || [];
-
- const data: Liveblocks['UserMeta']['info'][] = members
- .filter((user) => user?.userId && userIds.includes(user?.userId))
- .map((user) => ({
- name: user.user.name ?? user.user.email ?? 'Unknown user',
- picture: user.user.image,
- color: colors[Math.floor(Math.random() * colors.length)],
- }));
-
- return { data };
- } catch (error) {
- return { error };
- }
-};
diff --git a/apps/app/app/actions/users/search.ts b/apps/app/app/actions/users/search.ts
deleted file mode 100644
index f142dcd..0000000
--- a/apps/app/app/actions/users/search.ts
+++ /dev/null
@@ -1,50 +0,0 @@
-'use server';
-
-import {
- getFullOrganizationFromSession,
- getSessionFromHeaders,
-} from '@konobangu/auth/server';
-import Fuse from 'fuse.js';
-
-export const searchUsers = async (
- query: string
-): Promise<
- | {
- data: string[];
- }
- | {
- error: unknown;
- }
-> => {
- try {
- const session = await getSessionFromHeaders();
- const { orgId } = session;
-
- if (!orgId) {
- throw new Error('Not logged in');
- }
-
- const { fullOrganization } = await getFullOrganizationFromSession(session);
-
- const members = fullOrganization?.members || [];
-
- const users = members.map((user) => ({
- id: user.id,
- name: user.user.name ?? user.user.email ?? 'Unknown user',
- imageUrl: user.user.image,
- }));
-
- const fuse = new Fuse(users, {
- keys: ['name'],
- minMatchCharLength: 1,
- threshold: 0.3,
- });
-
- const results = fuse.search(query);
- const data = results.map((result) => result.item.id);
-
- return { data };
- } catch (error) {
- return { error };
- }
-};
diff --git a/apps/app/app/api/collaboration/auth/route.ts b/apps/app/app/api/collaboration/auth/route.ts
deleted file mode 100644
index 6032b28..0000000
--- a/apps/app/app/api/collaboration/auth/route.ts
+++ /dev/null
@@ -1,42 +0,0 @@
-import { getSessionFromHeaders } from '@konobangu/auth/server';
-import { authenticate } from '@konobangu/collaboration/auth';
-import { tailwind } from '@konobangu/tailwind-config';
-
-const COLORS = [
- tailwind.theme.colors.red[500],
- tailwind.theme.colors.orange[500],
- tailwind.theme.colors.amber[500],
- tailwind.theme.colors.yellow[500],
- tailwind.theme.colors.lime[500],
- tailwind.theme.colors.green[500],
- tailwind.theme.colors.emerald[500],
- tailwind.theme.colors.teal[500],
- tailwind.theme.colors.cyan[500],
- tailwind.theme.colors.sky[500],
- tailwind.theme.colors.blue[500],
- tailwind.theme.colors.indigo[500],
- tailwind.theme.colors.violet[500],
- tailwind.theme.colors.purple[500],
- tailwind.theme.colors.fuchsia[500],
- tailwind.theme.colors.pink[500],
- tailwind.theme.colors.rose[500],
-];
-
-export const POST = async () => {
- const session = await getSessionFromHeaders();
- const { orgId, user } = session;
-
- if (!user || !orgId) {
- return new Response('Unauthorized', { status: 401 });
- }
-
- return authenticate({
- userId: user.id,
- orgId,
- userInfo: {
- name: user.name ?? user.email ?? undefined,
- avatar: user.image ?? undefined,
- color: COLORS[Math.floor(Math.random() * COLORS.length)],
- },
- });
-};
diff --git a/apps/app/app/apple-icon.png b/apps/app/app/apple-icon.png
deleted file mode 100644
index d185929..0000000
Binary files a/apps/app/app/apple-icon.png and /dev/null differ
diff --git a/apps/app/app/cron/keep-alive/route.ts b/apps/app/app/cron/keep-alive/route.ts
deleted file mode 100644
index b34a27c..0000000
--- a/apps/app/app/cron/keep-alive/route.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import { database } from '@konobangu/database';
-
-export const POST = async () => {
- const newPage = await database
- .insertInto('page')
- .values([
- {
- name: 'cron-temp',
- },
- ])
- .returning('id')
- .executeTakeFirstOrThrow();
-
- await database.deleteFrom('page').where('id', '=', newPage.id);
-
- return new Response('OK', { status: 200 });
-};
diff --git a/apps/app/app/global-error.tsx b/apps/app/app/global-error.tsx
deleted file mode 100644
index 6e0511e..0000000
--- a/apps/app/app/global-error.tsx
+++ /dev/null
@@ -1,29 +0,0 @@
-'use client';
-
-import { Button } from '@konobangu/design-system/components/ui/button';
-import { fonts } from '@konobangu/design-system/lib/fonts';
-import { captureException } from '@sentry/nextjs';
-import type NextError from 'next/error';
-import { useEffect } from 'react';
-
-type GlobalErrorProperties = {
- readonly error: NextError & { digest?: string };
- readonly reset: () => void;
-};
-
-const GlobalError = ({ error, reset }: GlobalErrorProperties) => {
- useEffect(() => {
- captureException(error);
- }, [error]);
-
- return (
-
-
- Oops, something went wrong
- reset()}>Try again
-
-
- );
-};
-
-export default GlobalError;
diff --git a/apps/app/app/health/route.ts b/apps/app/app/health/route.ts
deleted file mode 100644
index 4ad1dd2..0000000
--- a/apps/app/app/health/route.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-export const runtime = 'edge';
-
-export const GET = (): Response => new Response('OK', { status: 200 });
diff --git a/apps/app/app/icon.png b/apps/app/app/icon.png
deleted file mode 100644
index d798285..0000000
Binary files a/apps/app/app/icon.png and /dev/null differ
diff --git a/apps/app/app/layout.tsx b/apps/app/app/layout.tsx
deleted file mode 100644
index 2c7acbb..0000000
--- a/apps/app/app/layout.tsx
+++ /dev/null
@@ -1,18 +0,0 @@
-import '@konobangu/design-system/styles/globals.css';
-import { DesignSystemProvider } from '@konobangu/design-system';
-import { fonts } from '@konobangu/design-system/lib/fonts';
-import type { ReactNode } from 'react';
-
-type RootLayoutProperties = {
- readonly children: ReactNode;
-};
-
-const RootLayout = ({ children }: RootLayoutProperties) => (
-
-
- {children}
-
-
-);
-
-export default RootLayout;
diff --git a/apps/app/app/opengraph-image.png b/apps/app/app/opengraph-image.png
deleted file mode 100644
index c79169f..0000000
Binary files a/apps/app/app/opengraph-image.png and /dev/null differ
diff --git a/apps/app/instrumentation.ts b/apps/app/instrumentation.ts
deleted file mode 100644
index 99d8478..0000000
--- a/apps/app/instrumentation.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-import { initializeSentry } from '@konobangu/next-config/instrumentation';
-
-export const register = initializeSentry();
diff --git a/apps/app/liveblocks.config.ts b/apps/app/liveblocks.config.ts
deleted file mode 100644
index 032b91d..0000000
--- a/apps/app/liveblocks.config.ts
+++ /dev/null
@@ -1 +0,0 @@
-export * from '@konobangu/collaboration/config';
diff --git a/apps/app/middleware.ts b/apps/app/middleware.ts
deleted file mode 100644
index 9f463a1..0000000
--- a/apps/app/middleware.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-import { authMiddleware } from '@konobangu/auth/middleware';
-import {
- noseconeConfig,
- noseconeMiddleware,
-} from '@konobangu/security/middleware';
-import type { NextRequest } from 'next/server';
-
-const securityHeaders = noseconeMiddleware(noseconeConfig);
-
-export async function middleware(_request: NextRequest) {
- const response = await securityHeaders();
- return authMiddleware(response as any);
-}
-
-export const config = {
- matcher: [
- // Skip Next.js internals and all static files, unless found in search params
- '/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)',
- // Always run for API routes
- '/(api|trpc)(.*)',
- ],
-};
diff --git a/apps/app/next.config.ts b/apps/app/next.config.ts
deleted file mode 100644
index 5ca2ed6..0000000
--- a/apps/app/next.config.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { env } from '@konobangu/env';
-import { config, withAnalyzer, withSentry } from '@konobangu/next-config';
-import type { NextConfig } from 'next';
-
-let nextConfig: NextConfig = { ...config };
-
-if (env.VERCEL) {
- nextConfig = withSentry(nextConfig);
-}
-
-if (env.ANALYZE === 'true') {
- nextConfig = withAnalyzer(nextConfig);
-}
-
-export default nextConfig;
diff --git a/apps/app/package.json b/apps/app/package.json
deleted file mode 100644
index 6975b63..0000000
--- a/apps/app/package.json
+++ /dev/null
@@ -1,51 +0,0 @@
-{
- "name": "app",
- "private": true,
- "scripts": {
- "dev": "next dev -p 5000 --turbopack",
- "build": "next build",
- "start": "next start",
- "analyze": "ANALYZE=true pnpm build",
- "test": "vitest run",
- "clean": "git clean -xdf .cache .turbo dist node_modules",
- "typecheck": "tsc --noEmit --emitDeclarationOnly false"
- },
- "dependencies": {
- "@konobangu/analytics": "workspace:*",
- "@konobangu/auth": "workspace:*",
- "@konobangu/collaboration": "workspace:*",
- "@konobangu/database": "workspace:*",
- "@konobangu/design-system": "workspace:*",
- "@konobangu/env": "workspace:*",
- "@konobangu/feature-flags": "workspace:*",
- "@konobangu/migrate": "workspace:*",
- "@konobangu/next-config": "workspace:*",
- "@konobangu/security": "workspace:*",
- "@konobangu/seo": "workspace:*",
- "@konobangu/tailwind-config": "workspace:*",
- "@konobangu/webhooks": "workspace:*",
- "@prisma/client": "6.0.1",
- "@sentry/nextjs": "^8.48.0",
- "fuse.js": "^7.0.0",
- "import-in-the-middle": "^1.12.0",
- "lucide-react": "^0.468.0",
- "next": "^15.1.4",
- "next-themes": "^0.4.4",
- "react": "^19.0.0",
- "react-dom": "^19.0.0",
- "require-in-the-middle": "^7.4.0"
- },
- "devDependencies": {
- "@konobangu/testing": "workspace:*",
- "@konobangu/typescript-config": "workspace:*",
- "@testing-library/dom": "^10.4.0",
- "@testing-library/react": "^16.1.0",
- "@types/node": "22.10.1",
- "@types/react": "19.0.1",
- "@types/react-dom": "19.0.2",
- "jsdom": "^25.0.1",
- "tailwindcss": "^3.4.17",
- "typescript": "^5.7.3",
- "vitest": "^2.1.8"
- }
-}
diff --git a/apps/app/postcss.config.mjs b/apps/app/postcss.config.mjs
deleted file mode 100644
index d799a05..0000000
--- a/apps/app/postcss.config.mjs
+++ /dev/null
@@ -1 +0,0 @@
-export { default } from '@konobangu/design-system/postcss.config.mjs';
diff --git a/apps/app/sentry.client.config.ts b/apps/app/sentry.client.config.ts
deleted file mode 100644
index 9f17f95..0000000
--- a/apps/app/sentry.client.config.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * This file configures the initialization of Sentry on the client.
- * The config you add here will be used whenever a users loads a page in their browser.
- * https://docs.sentry.io/platforms/javascript/guides/nextjs/
- */
-
-import { init, replayIntegration } from '@sentry/nextjs';
-
-init({
- dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
-
- // Adjust this value in production, or use tracesSampler for greater control
- tracesSampleRate: 1,
-
- // Setting this option to true will print useful information to the console while you're setting up Sentry.
- debug: false,
-
- replaysOnErrorSampleRate: 1,
-
- /*
- * This sets the sample rate to be 10%. You may want this to be 100% while
- * in development and sample at a lower rate in production
- */
- replaysSessionSampleRate: 0.1,
-
- // You can remove this option if you're not planning to use the Sentry Session Replay feature:
- integrations: [
- replayIntegration({
- // Additional Replay configuration goes in here, for example:
- maskAllText: true,
- blockAllMedia: true,
- }),
- ],
-});
diff --git a/apps/app/tailwind.config.ts b/apps/app/tailwind.config.ts
deleted file mode 100644
index 9f53cee..0000000
--- a/apps/app/tailwind.config.ts
+++ /dev/null
@@ -1 +0,0 @@
-export { config as default } from '@konobangu/tailwind-config/config';
diff --git a/apps/app/tsconfig.json b/apps/app/tsconfig.json
deleted file mode 100644
index 2f5c616..0000000
--- a/apps/app/tsconfig.json
+++ /dev/null
@@ -1,17 +0,0 @@
-{
- "extends": "@konobangu/typescript-config/nextjs.json",
- "compilerOptions": {
- "baseUrl": ".",
- "paths": {
- "@/*": ["./*"],
- "@konobangu/*": ["../../packages/*"]
- }
- },
- "include": [
- "next-env.d.ts",
- "next.config.ts",
- "**/*.ts",
- "**/*.tsx",
- ".next/types/**/*.ts"
- ]
-}
diff --git a/apps/app/vercel.json b/apps/app/vercel.json
deleted file mode 100644
index 0632aa0..0000000
--- a/apps/app/vercel.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "crons": [
- {
- "path": "/cron/keep-alive",
- "schedule": "0 1 * * *"
- }
- ]
-}
diff --git a/apps/app/vitest.config.ts b/apps/app/vitest.config.ts
deleted file mode 100644
index 1db3319..0000000
--- a/apps/app/vitest.config.ts
+++ /dev/null
@@ -1 +0,0 @@
-export { default } from '@konobangu/testing';
diff --git a/apps/docs/package.json b/apps/docs/package.json
index 557f8da..97756a1 100644
--- a/apps/docs/package.json
+++ b/apps/docs/package.json
@@ -4,8 +4,5 @@
"scripts": {
"dev": "npx --yes mintlify dev --port 5004",
"lint": "npx --yes mintlify broken-links"
- },
- "devDependencies": {
- "typescript": "^5.7.3"
}
}
diff --git a/apps/email-playground/package.json b/apps/email-playground/package.json
index a0af978..aab812f 100644
--- a/apps/email-playground/package.json
+++ b/apps/email-playground/package.json
@@ -10,15 +10,11 @@
"typecheck": "tsc --noEmit --emitDeclarationOnly false"
},
"dependencies": {
- "@konobangu/email": "workspace:*",
"@react-email/components": "0.0.31",
"react": "^19.0.0",
"react-email": "3.0.4"
},
"devDependencies": {
- "@konobangu/typescript-config": "workspace:*",
- "@types/node": "22.10.1",
- "@types/react": "19.0.1",
- "typescript": "^5.7.3"
+ "@types/react": "19.0.1"
}
}
diff --git a/apps/email-playground/tsconfig.json b/apps/email-playground/tsconfig.json
index 9d2f043..e664740 100644
--- a/apps/email-playground/tsconfig.json
+++ b/apps/email-playground/tsconfig.json
@@ -1,5 +1,9 @@
{
- "extends": "@konobangu/typescript-config/nextjs.json",
+ "extends": "../../tsconfig.base.json",
+ "compilerOptions": {
+ "composite": true,
+ "jsx": "react-jsx"
+ },
"include": ["**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]
}
diff --git a/apps/recorder/Cargo.toml b/apps/recorder/Cargo.toml
index 3580d7b..9c454a1 100644
--- a/apps/recorder/Cargo.toml
+++ b/apps/recorder/Cargo.toml
@@ -36,7 +36,6 @@ sea-orm = { version = "1.1", features = [
"debug-print",
] }
figment = { version = "0.10", features = ["toml", "json", "env", "yaml"] }
-
axum = "0.8"
uuid = { version = "1.6.0", features = ["v4"] }
tracing-subscriber = { version = "0.3", features = ["env-filter", "json"] }
diff --git a/apps/recorder/package.json b/apps/recorder/package.json
index f1d1f80..16efa0c 100644
--- a/apps/recorder/package.json
+++ b/apps/recorder/package.json
@@ -8,30 +8,17 @@
"preview": "rsbuild preview"
},
"dependencies": {
- "@abraham/reflection": "^0.12.0",
"@graphiql/react": "^0.28.2",
"@graphiql/toolkit": "^0.11.1",
- "@konobangu/design-system": "workspace:*",
- "@konobangu/tailwind-config": "workspace:*",
- "@outposts/injection-js": "^2.5.1",
- "@tanstack/react-router": "^1.95.6",
- "@tanstack/router-devtools": "^1.95.6",
"graphiql": "^3.8.3",
- "graphql-ws": "^5.16.2",
+ "graphql-ws": "^6.0.4",
"observable-hooks": "^4.2.4",
- "oidc-client-rx": "0.1.0-alpha.6",
"react": "^19.0.0",
- "react-dom": "^19.0.0",
- "rxjs": "^7.8.1"
+ "react-dom": "^19.0.0"
},
"devDependencies": {
- "@konobangu/typescript-config": "workspace:*",
- "@rsbuild/core": "1.1.3",
"@rsbuild/plugin-react": "^1.1.1",
- "@tanstack/router-plugin": "^1.95.6",
"@types/react": "^19.0.7",
- "@types/react-dom": "^19.0.3",
- "tailwindcss": "^3.4.17",
- "typescript": "^5.7.3"
+ "@types/react-dom": "^19.0.3"
}
}
diff --git a/apps/recorder/postcss.config.mjs b/apps/recorder/postcss.config.mjs
new file mode 100644
index 0000000..8dc11a1
--- /dev/null
+++ b/apps/recorder/postcss.config.mjs
@@ -0,0 +1,5 @@
+export default {
+ plugins: {
+ '@tailwindcss/postcss': {},
+ },
+};
diff --git a/apps/recorder/recorder.config.toml b/apps/recorder/recorder.config.toml
new file mode 100644
index 0000000..3ba2360
--- /dev/null
+++ b/apps/recorder/recorder.config.toml
@@ -0,0 +1,103 @@
+# Application logging configuration
+[logger]
+# Enable or disable logging.
+enable = true
+# Enable pretty backtrace (sets RUST_BACKTRACE=1)
+pretty_backtrace = true
+# Log level, options: trace, debug, info, warn or error.
+level = "debug"
+# Define the logging format. options: compact, pretty or Json
+format = "compact"
+# By default the logger has filtering only logs that came from your code or logs that came from `loco` framework. to see all third party libraries
+# Uncomment the line below to override to see all third party libraries you can enable this config and override the logger filters.
+# override_filter: trace
+
+# Web server configuration
+[server]
+# Port on which the server will listen. the server binding is 0.0.0.0:{PORT}
+port = 5001
+binding = "0.0.0.0"
+# The UI hostname or IP address that mailers will point to.
+host = '{{ get_env(name="HOST", default="localhost") }}'
+# Out of the box middleware configuration. to disable middleware you can changed the `enable` field to `false` of comment the middleware block
+
+# Enable Etag cache header middleware
+[server.middlewares.etag]
+enable = true
+
+# Generating a unique request ID and enhancing logging with additional information such as the start and completion of request processing, latency, status code, and other request details.
+[server.middleware.request_id]
+enable = true
+
+[server.middleware.logger]
+enable = true
+
+# when your code is panicked, the request still returns 500 status code.
+[server.middleware.catch_panic]
+enable = true
+
+# Timeout for incoming requests middleware. requests that take more time from the configuration will cute and 408 status code will returned.
+[server.middleware.timeout_request]
+enable = false
+# Duration time in milliseconds.
+timeout = 5000
+
+# Set the value of the [`Access-Control-Allow-Origin`][mdn] header
+# allow_origins:
+# - https://loco.rs
+# Set the value of the [`Access-Control-Allow-Headers`][mdn] header
+# allow_headers:
+# - Content-Type
+# Set the value of the [`Access-Control-Allow-Methods`][mdn] header
+# allow_methods:
+# - POST
+# Set the value of the [`Access-Control-Max-Age`][mdn] header in seconds
+# max_age: 3600
+[server.middleware.cors]
+enable = true
+
+# Database Configuration
+[database]
+# Database connection URI
+uri = '{{ get_env(name="DATABASE_URL", default="postgres://konobangu:konobangu@localhost:5432/konobangu") }}'
+# When enabled, the sql query will be logged.
+enable_logging = true
+# Set the timeout duration when acquiring a connection.
+connect_timeout = 500
+# Set the idle duration before closing a connection.
+idle_timeout = 500
+# Minimum number of connections for a pool.
+min_connections = 1
+# Maximum number of connections for a pool.
+max_connections = 10
+# Run migration up when application loaded
+auto_migrate = true
+
+[storage]
+data_dir = '{{ get_env(name="STORAGE_DATA_DIR", default="./data") }}'
+
+[mikan]
+base_url = "https://mikanani.me/"
+
+[mikan.http_client]
+exponential_backoff_max_retries = 3
+leaky_bucket_max_tokens = 2
+leaky_bucket_initial_tokens = 1
+leaky_bucket_refill_tokens = 1
+leaky_bucket_refill_interval = 500
+
+[auth]
+auth_type = '{{ get_env(name="AUTH_TYPE", default = "basic") }}'
+basic_user = '{{ get_env(name="BASIC_USER", default = "konobangu") }}'
+basic_password = '{{ get_env(name="BASIC_PASSWORD", default = "konobangu") }}'
+oidc_issuer = '{{ get_env(name="OIDC_ISSUER", default = "") }}'
+oidc_audience = '{{ get_env(name="OIDC_AUDIENCE", default = "") }}'
+oidc_client_id = '{{ get_env(name="OIDC_CLIENT_ID", default = "") }}'
+oidc_client_secret = '{{ get_env(name="OIDC_CLIENT_SECRET", default = "") }}'
+oidc_extra_scopes = '{{ get_env(name="OIDC_EXTRA_SCOPES", default = "") }}'
+oidc_extra_claim_key = '{{ get_env(name="OIDC_EXTRA_CLAIM_KEY", default = "") }}'
+oidc_extra_claim_value = '{{ get_env(name="OIDC_EXTRA_CLAIM_VALUE", default = "") }}'
+
+[graphql]
+# depth_limit = inf
+# complexity_limit = inf
diff --git a/apps/recorder/recorder.config.yaml b/apps/recorder/recorder.config.yaml
deleted file mode 100644
index fe063df..0000000
--- a/apps/recorder/recorder.config.yaml
+++ /dev/null
@@ -1,101 +0,0 @@
-# Loco configuration file documentation
-
-# Application logging configuration
-logger:
- # Enable or disable logging.
- enable: true
- # Enable pretty backtrace (sets RUST_BACKTRACE=1)
- pretty_backtrace: true
- # Log level, options: trace, debug, info, warn or error.
- level: debug
- # Define the logging format. options: compact, pretty or Json
- format: compact
- # By default the logger has filtering only logs that came from your code or logs that came from `loco` framework. to see all third party libraries
- # Uncomment the line below to override to see all third party libraries you can enable this config and override the logger filters.
- # override_filter: trace
-
-# Web server configuration
-server:
- # Port on which the server will listen. the server binding is 0.0.0.0:{PORT}
- port: 5001
- binding: "0.0.0.0"
- # The UI hostname or IP address that mailers will point to.
- host: '{{ get_env(name="HOST", default="localhost") }}'
- # Out of the box middleware configuration. to disable middleware you can changed the `enable` field to `false` of comment the middleware block
- middlewares:
- # Enable Etag cache header middleware
- etag:
- enable: true
- # Generating a unique request ID and enhancing logging with additional information such as the start and completion of request processing, latency, status code, and other request details.
- logger:
- # Enable/Disable the middleware.
- enable: true
- # when your code is panicked, the request still returns 500 status code.
- catch_panic:
- # Enable/Disable the middleware.
- enable: true
- # Timeout for incoming requests middleware. requests that take more time from the configuration will cute and 408 status code will returned.
- timeout_request:
- # Enable/Disable the middleware.
- enable: false
- # Duration time in milliseconds.
- timeout: 5000
-
- cors:
- enable: true
- # Set the value of the [`Access-Control-Allow-Origin`][mdn] header
- # allow_origins:
- # - https://loco.rs
- # Set the value of the [`Access-Control-Allow-Headers`][mdn] header
- # allow_headers:
- # - Content-Type
- # Set the value of the [`Access-Control-Allow-Methods`][mdn] header
- # allow_methods:
- # - POST
- # Set the value of the [`Access-Control-Max-Age`][mdn] header in seconds
- # max_age: 3600
-
-# Database Configuration
-database:
- # Database connection URI
- uri: '{{ get_env(name="DATABASE_URL", default="postgres://konobangu:konobangu@localhost:5432/konobangu") }}'
- # When enabled, the sql query will be logged.
- enable_logging: true
- # Set the timeout duration when acquiring a connection.
- connect_timeout: 500
- # Set the idle duration before closing a connection.
- idle_timeout: 500
- # Minimum number of connections for a pool.
- min_connections: 1
- # Maximum number of connections for a pool.
- max_connections: 1
- # Run migration up when application loaded
- auto_migrate: true
-
-storage:
- data_dir: '{{ get_env(name="STORAGE_DATA_DIR", default="./data") }}'
-
-mikan:
- base_url: "https://mikanani.me/"
- http_client:
- exponential_backoff_max_retries: 3
- leaky_bucket_max_tokens: 2
- leaky_bucket_initial_tokens: 0
- leaky_bucket_refill_tokens: 1
- leaky_bucket_refill_interval: 500
-
-auth:
- auth_type: '{{ get_env(name="AUTH_TYPE", default = "basic") }}'
- basic_user: '{{ get_env(name="BASIC_USER", default = "konobangu") }}'
- basic_password: '{{ get_env(name="BASIC_PASSWORD", default = "konobangu") }}'
- oidc_issuer: '{{ get_env(name="OIDC_ISSUER", default = "") }}'
- oidc_audience: '{{ get_env(name="OIDC_AUDIENCE", default = "") }}'
- oidc_client_id: '{{ get_env(name="OIDC_CLIENT_ID", default = "") }}'
- oidc_client_secret: '{{ get_env(name="OIDC_CLIENT_SECRET", default = "") }}'
- oidc_extra_scopes: '{{ get_env(name="OIDC_EXTRA_SCOPES", default = "") }}'
- oidc_extra_claim_key: '{{ get_env(name="OIDC_EXTRA_CLAIM_KEY", default = "") }}'
- oidc_extra_claim_value: '{{ get_env(name="OIDC_EXTRA_CLAIM_VALUE", default = "") }}'
-
-graphql:
- depth_limit: null
- complexity_limit: null
diff --git a/apps/recorder/src/graphql/config.rs b/apps/recorder/src/graphql/config.rs
index 5f90aad..9c92304 100644
--- a/apps/recorder/src/graphql/config.rs
+++ b/apps/recorder/src/graphql/config.rs
@@ -1,7 +1,75 @@
-use serde::{Deserialize, Serialize};
+use core::f64;
-#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
-pub struct GraphQLConfig {
- pub depth_limit: Option,
- pub complexity_limit: Option,
+use serde::{
+ Deserialize, Deserializer, Serialize, Serializer,
+ de::{self, Unexpected},
+};
+
+#[derive(Debug, Clone, Copy)]
+pub struct OnlyInfOrNaN(f64);
+
+impl OnlyInfOrNaN {
+ pub fn inf() -> Self {
+ OnlyInfOrNaN(f64::INFINITY)
+ }
+
+ pub fn nan() -> Self {
+ OnlyInfOrNaN(f64::NAN)
+ }
+}
+
+impl From for Option {
+ fn from(_: OnlyInfOrNaN) -> Self {
+ None
+ }
+}
+
+impl Serialize for OnlyInfOrNaN {
+ fn serialize(&self, serializer: S) -> Result
+ where
+ S: Serializer,
+ {
+ serializer.serialize_f64(self.0)
+ }
+}
+
+impl<'de> Deserialize<'de> for OnlyInfOrNaN {
+ fn deserialize(deserializer: D) -> Result
+ where
+ D: Deserializer<'de>,
+ {
+ let value = f64::deserialize(deserializer)?;
+ if value.is_nan() {
+ Ok(Self::nan())
+ } else if value.is_infinite() {
+ Ok(Self::inf())
+ } else {
+ Err(de::Error::invalid_value(
+ Unexpected::Float(value),
+ &"a NaN or a Inf",
+ ))
+ }
+ }
+}
+
+#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
+#[serde(untagged)]
+pub enum GraphQLLimitNum {
+ Num(usize),
+ Adhoc(OnlyInfOrNaN),
+}
+
+impl From for Option {
+ fn from(value: GraphQLLimitNum) -> Self {
+ match value {
+ GraphQLLimitNum::Adhoc(v) => v.into(),
+ GraphQLLimitNum::Num(v) => Some(v),
+ }
+ }
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct GraphQLConfig {
+ pub depth_limit: Option,
+ pub complexity_limit: Option,
}
diff --git a/apps/recorder/src/graphql/service.rs b/apps/recorder/src/graphql/service.rs
index 2151bcc..f658366 100644
--- a/apps/recorder/src/graphql/service.rs
+++ b/apps/recorder/src/graphql/service.rs
@@ -14,7 +14,11 @@ impl GraphQLService {
config: GraphQLConfig,
db: DatabaseConnection,
) -> RResult {
- let schema = schema_root::schema(db, config.depth_limit, config.complexity_limit)?;
+ let schema = schema_root::schema(
+ db,
+ config.depth_limit.and_then(|l| l.into()),
+ config.complexity_limit.and_then(|l| l.into()),
+ )?;
Ok(Self { schema })
}
}
diff --git a/apps/recorder/src/lib.rs b/apps/recorder/src/lib.rs
index 6b572f7..171a9f8 100644
--- a/apps/recorder/src/lib.rs
+++ b/apps/recorder/src/lib.rs
@@ -24,5 +24,5 @@ pub mod sync;
pub mod tasks;
#[cfg(test)]
pub mod test_utils;
-pub mod views;
+pub mod utils;
pub mod web;
diff --git a/apps/recorder/src/main.css b/apps/recorder/src/main.css
index bd6213e..f1d8c73 100644
--- a/apps/recorder/src/main.css
+++ b/apps/recorder/src/main.css
@@ -1,3 +1 @@
-@tailwind base;
-@tailwind components;
-@tailwind utilities;
\ No newline at end of file
+@import "tailwindcss";
diff --git a/apps/recorder/src/utils/mod.rs b/apps/recorder/src/utils/mod.rs
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/apps/recorder/src/utils/mod.rs
@@ -0,0 +1 @@
+
diff --git a/apps/recorder/src/views/mod.rs b/apps/recorder/src/views/mod.rs
deleted file mode 100644
index 2806b76..0000000
--- a/apps/recorder/src/views/mod.rs
+++ /dev/null
@@ -1 +0,0 @@
-pub mod subscribers;
diff --git a/apps/recorder/src/views/subscribers.rs b/apps/recorder/src/views/subscribers.rs
deleted file mode 100644
index f108748..0000000
--- a/apps/recorder/src/views/subscribers.rs
+++ /dev/null
@@ -1,13 +0,0 @@
-use serde::{Deserialize, Serialize};
-
-use crate::models::subscribers;
-
-#[derive(Debug, Deserialize, Serialize)]
-pub struct CurrentResponse {}
-
-impl CurrentResponse {
- #[must_use]
- pub fn new(_user: &subscribers::Model) -> Self {
- Self {}
- }
-}
diff --git a/apps/recorder/src/web/controller/graphql/index.tsx b/apps/recorder/src/web/controller/graphql/index.tsx
index 2a8f542..9e0ed9c 100644
--- a/apps/recorder/src/web/controller/graphql/index.tsx
+++ b/apps/recorder/src/web/controller/graphql/index.tsx
@@ -2,10 +2,10 @@ import { type Fetcher, createGraphiQLFetcher } from '@graphiql/toolkit';
import { createFileRoute } from '@tanstack/react-router';
import GraphiQL from 'graphiql';
import { useMemo } from 'react';
-import { beforeLoadGuard } from '../../../auth/guard';
-import 'graphiql/graphiql.css';
import { firstValueFrom } from 'rxjs';
+import { beforeLoadGuard } from '../../../auth/guard';
import { useAuth } from '../../../auth/hooks';
+import 'graphiql/graphiql.css';
export const Route = createFileRoute('/graphql/')({
component: RouteComponent,
@@ -32,5 +32,5 @@ function RouteComponent() {
[oidcSecurityService]
);
- return ;
+ return ;
}
diff --git a/apps/recorder/tailwind.config.ts b/apps/recorder/tailwind.config.ts
deleted file mode 100644
index 9f53cee..0000000
--- a/apps/recorder/tailwind.config.ts
+++ /dev/null
@@ -1 +0,0 @@
-export { config as default } from '@konobangu/tailwind-config/config';
diff --git a/apps/recorder/tsconfig.json b/apps/recorder/tsconfig.json
index 447ffe7..d254fee 100644
--- a/apps/recorder/tsconfig.json
+++ b/apps/recorder/tsconfig.json
@@ -1,12 +1,11 @@
{
- "extends": "@konobangu/typescript-config/base.json",
+ "extends": "../../tsconfig.base.json",
"compilerOptions": {
- "lib": ["DOM", "ES2024", "DOM.AsyncIterable", "DOM.Iterable"],
+ "rootDir": ".",
+ "composite": true,
"jsx": "react-jsx",
- "noEmit": true,
"module": "ESNext",
"moduleResolution": "Bundler",
- "allowImportingTsExtensions": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"strict": true,
diff --git a/apps/storybook/.gitignore b/apps/storybook/.gitignore
deleted file mode 100644
index d85f7c1..0000000
--- a/apps/storybook/.gitignore
+++ /dev/null
@@ -1,45 +0,0 @@
-# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
-
-# dependencies
-/node_modules
-/.pnp
-.pnp.*
-.yarn/*
-!.yarn/patches
-!.yarn/plugins
-!.yarn/releases
-!.yarn/versions
-
-# testing
-/coverage
-
-# next.js
-/.next/
-/out/
-
-# production
-/build
-
-# misc
-.DS_Store
-*.pem
-
-# debug
-npm-debug.log*
-yarn-debug.log*
-yarn-error.log*
-
-# env files (can opt-in for commiting if needed)
-.env*
-
-# vercel
-.vercel
-
-# typescript
-*.tsbuildinfo
-next-env.d.ts
-
-*storybook.log
-
-# storybook
-storybook-static/
\ No newline at end of file
diff --git a/apps/storybook/.storybook/main.ts b/apps/storybook/.storybook/main.ts
deleted file mode 100644
index 0592005..0000000
--- a/apps/storybook/.storybook/main.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-import { dirname, join } from 'node:path';
-import type { StorybookConfig } from '@storybook/nextjs';
-
-/**
- * This function is used to resolve the absolute path of a package.
- * It is needed in projects that use Yarn PnP or are set up within a monorepo.
- */
-const getAbsolutePath = (value: string) =>
- dirname(require.resolve(join(value, 'package.json')));
-
-const config: StorybookConfig = {
- stories: [
- '../stories/**/*.mdx',
- '../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)',
- ],
- addons: [
- getAbsolutePath('@storybook/addon-onboarding'),
- getAbsolutePath('@storybook/addon-essentials'),
- getAbsolutePath('@chromatic-com/storybook'),
- getAbsolutePath('@storybook/addon-interactions'),
- getAbsolutePath('@storybook/addon-themes'),
- ],
- framework: {
- name: getAbsolutePath('@storybook/nextjs'),
- options: {},
- },
- staticDirs: ['../public'],
-};
-
-export default config;
diff --git a/apps/storybook/.storybook/preview-head.html b/apps/storybook/.storybook/preview-head.html
deleted file mode 100644
index 5873c0b..0000000
--- a/apps/storybook/.storybook/preview-head.html
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
-
-
-
-
diff --git a/apps/storybook/.storybook/preview.tsx b/apps/storybook/.storybook/preview.tsx
deleted file mode 100644
index 7c2c1fd..0000000
--- a/apps/storybook/.storybook/preview.tsx
+++ /dev/null
@@ -1,53 +0,0 @@
-import { Toaster } from '@konobangu/design-system/components/ui/sonner';
-import { TooltipProvider } from '@konobangu/design-system/components/ui/tooltip';
-import { ThemeProvider } from '@konobangu/design-system/providers/theme';
-import { withThemeByClassName } from '@storybook/addon-themes';
-import type { Preview } from '@storybook/react';
-
-import '@konobangu/design-system/styles/globals.css';
-
-const preview: Preview = {
- parameters: {
- controls: {
- matchers: {
- color: /(background|color)$/i,
- date: /Date$/i,
- },
- },
- chromatic: {
- modes: {
- light: {
- theme: 'light',
- className: 'light',
- },
- dark: {
- theme: 'dark',
- className: 'dark',
- },
- },
- },
- },
- decorators: [
- withThemeByClassName({
- themes: {
- light: 'light',
- dark: 'dark',
- },
- defaultTheme: 'light',
- }),
- (Story) => {
- return (
-
-
-
-
-
-
-
-
- );
- },
- ],
-};
-
-export default preview;
diff --git a/apps/storybook/README.md b/apps/storybook/README.md
deleted file mode 100644
index a5cbf96..0000000
--- a/apps/storybook/README.md
+++ /dev/null
@@ -1,40 +0,0 @@
-This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/pages/api-reference/create-next-app).
-
-## Getting Started
-
-First, run the development server:
-
-```bash
-npm run dev
-# or
-yarn dev
-# or
-pnpm dev
-# or
-bun dev
-```
-
-Open [http://localhost:5000](http://localhost:5000) with your browser to see the result.
-
-You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file.
-
-[API routes](https://nextjs.org/docs/pages/building-your-application/routing/api-routes) can be accessed on [http://localhost:5000/api/hello](http://localhost:5000/api/hello). This endpoint can be edited in `pages/api/hello.ts`.
-
-The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/pages/building-your-application/routing/api-routes) instead of React pages.
-
-This project uses [`next/font`](https://nextjs.org/docs/pages/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
-
-## Learn More
-
-To learn more about Next.js, take a look at the following resources:
-
-- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
-- [Learn Next.js](https://nextjs.org/learn-pages-router) - an interactive Next.js tutorial.
-
-You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
-
-## Deploy on Vercel
-
-The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
-
-Check out our [Next.js deployment documentation](https://nextjs.org/docs/pages/building-your-application/deploying) for more details.
diff --git a/apps/storybook/next.config.ts b/apps/storybook/next.config.ts
deleted file mode 100644
index b08f02b..0000000
--- a/apps/storybook/next.config.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-import type { NextConfig } from 'next';
-
-const nextConfig: NextConfig = {
- reactStrictMode: true,
-};
-
-export default nextConfig;
diff --git a/apps/storybook/package.json b/apps/storybook/package.json
deleted file mode 100644
index 648f019..0000000
--- a/apps/storybook/package.json
+++ /dev/null
@@ -1,39 +0,0 @@
-{
- "name": "storybook",
- "version": "0.1.0",
- "private": true,
- "scripts": {
- "dev": "storybook dev -p 6006",
- "build-storybook": "storybook build",
- "chromatic": "chromatic --exit-zero-on-changes",
- "clean": "git clean -xdf .cache .turbo dist node_modules",
- "typecheck": "tsc --noEmit --emitDeclarationOnly false"
- },
- "dependencies": {
- "@konobangu/design-system": "workspace:*",
- "lucide-react": "^0.468.0",
- "next": "^15.1.4",
- "react": "^19.0.0",
- "react-dom": "^19.0.0"
- },
- "devDependencies": {
- "@chromatic-com/storybook": "^3.2.3",
- "@konobangu/typescript-config": "workspace:*",
- "@storybook/addon-essentials": "^8.4.7",
- "@storybook/addon-interactions": "^8.4.7",
- "@storybook/addon-onboarding": "^8.4.7",
- "@storybook/addon-themes": "^8.4.7",
- "@storybook/blocks": "^8.4.7",
- "@storybook/nextjs": "^8.4.7",
- "@storybook/react": "^8.4.7",
- "@storybook/test": "^8.4.7",
- "@types/node": "^22.10.6",
- "@types/react": "^19.0.7",
- "@types/react-dom": "^19.0.3",
- "chromatic": "^11.23.0",
- "postcss": "^8.5.1",
- "storybook": "^8.4.7",
- "tailwindcss": "^3.4.17",
- "typescript": "^5.7.3"
- }
-}
diff --git a/apps/storybook/postcss.config.mjs b/apps/storybook/postcss.config.mjs
deleted file mode 100644
index 1a69fd2..0000000
--- a/apps/storybook/postcss.config.mjs
+++ /dev/null
@@ -1,8 +0,0 @@
-/** @type {import('postcss-load-config').Config} */
-const config = {
- plugins: {
- tailwindcss: {},
- },
-};
-
-export default config;
diff --git a/apps/storybook/public/favicon.ico b/apps/storybook/public/favicon.ico
deleted file mode 100644
index 718d6fe..0000000
Binary files a/apps/storybook/public/favicon.ico and /dev/null differ
diff --git a/apps/storybook/stories/accordion.stories.tsx b/apps/storybook/stories/accordion.stories.tsx
deleted file mode 100644
index c08c137..0000000
--- a/apps/storybook/stories/accordion.stories.tsx
+++ /dev/null
@@ -1,60 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import {
- Accordion,
- AccordionContent,
- AccordionItem,
- AccordionTrigger,
-} from '@konobangu/design-system/components/ui/accordion';
-
-/**
- * A vertically stacked set of interactive headings that each reveal a section
- * of content.
- */
-const meta = {
- title: 'ui/Accordion',
- component: Accordion,
- tags: ['autodocs'],
- argTypes: {
- type: {
- options: ['single', 'multiple'],
- control: { type: 'radio' },
- },
- },
- args: {
- type: 'single',
- collapsible: true,
- },
- render: (args) => (
-
-
- Is it accessible?
-
- Yes. It adheres to the WAI-ARIA design pattern.
-
-
-
- Is it styled?
-
- Yes. It comes with default styles that matches the other components'
- aesthetic.
-
-
-
- Is it animated?
-
- Yes. It's animated by default, but you can disable it if you prefer.
-
-
-
- ),
-} satisfies Meta;
-
-export default meta;
-
-type Story = StoryObj;
-
-/**
- * The default behavior of the accordion allows only one item to be open.
- */
-export const Default: Story = {};
diff --git a/apps/storybook/stories/alert-dialog.stories.tsx b/apps/storybook/stories/alert-dialog.stories.tsx
deleted file mode 100644
index 1df1f2d..0000000
--- a/apps/storybook/stories/alert-dialog.stories.tsx
+++ /dev/null
@@ -1,54 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import {
- AlertDialog,
- AlertDialogAction,
- AlertDialogCancel,
- AlertDialogContent,
- AlertDialogDescription,
- AlertDialogFooter,
- AlertDialogHeader,
- AlertDialogTitle,
- AlertDialogTrigger,
-} from '@konobangu/design-system/components/ui/alert-dialog';
-
-/**
- * A modal dialog that interrupts the user with important content and expects
- * a response.
- */
-const meta = {
- title: 'ui/AlertDialog',
- component: AlertDialog,
- tags: ['autodocs'],
- argTypes: {},
- render: (args) => (
-
- Open
-
-
- Are you sure absolutely sure?
-
- This action cannot be undone. This will permanently delete your
- account and remove your data from our servers.
-
-
-
- Cancel
- Continue
-
-
-
- ),
- parameters: {
- layout: 'centered',
- },
-} satisfies Meta;
-
-export default meta;
-
-type Story = StoryObj;
-
-/**
- * The default form of the alert dialog.
- */
-export const Default: Story = {};
diff --git a/apps/storybook/stories/alert.stories.tsx b/apps/storybook/stories/alert.stories.tsx
deleted file mode 100644
index 6cede53..0000000
--- a/apps/storybook/stories/alert.stories.tsx
+++ /dev/null
@@ -1,60 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-import { AlertCircle } from 'lucide-react';
-
-import {
- Alert,
- AlertDescription,
- AlertTitle,
-} from '@konobangu/design-system/components/ui/alert';
-
-/**
- * Displays a callout for user attention.
- */
-const meta = {
- title: 'ui/Alert',
- component: Alert,
- tags: ['autodocs'],
- argTypes: {
- variant: {
- options: ['default', 'destructive'],
- control: { type: 'radio' },
- },
- },
- args: {
- variant: 'default',
- },
- render: (args) => (
-
- Heads up!
-
- You can add components to your app using the cli.
-
-
- ),
-} satisfies Meta;
-
-export default meta;
-
-type Story = StoryObj;
-/**
- * The default form of the alert.
- */
-export const Default: Story = {};
-
-/**
- * Use the `destructive` alert to indicate a destructive action.
- */
-export const Destructive: Story = {
- render: (args) => (
-
-
- Error
-
- Your session has expired. Please log in again.
-
-
- ),
- args: {
- variant: 'destructive',
- },
-};
diff --git a/apps/storybook/stories/aspect-ratio.stories.tsx b/apps/storybook/stories/aspect-ratio.stories.tsx
deleted file mode 100644
index b99d442..0000000
--- a/apps/storybook/stories/aspect-ratio.stories.tsx
+++ /dev/null
@@ -1,71 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-import Image from 'next/image';
-
-import { AspectRatio } from '@konobangu/design-system/components/ui/aspect-ratio';
-
-/**
- * Displays content within a desired ratio.
- */
-const meta: Meta = {
- title: 'ui/AspectRatio',
- component: AspectRatio,
- tags: ['autodocs'],
- argTypes: {},
- render: (args) => (
-
-
-
- ),
- decorators: [
- (Story) => (
-
-
-
- ),
- ],
-} satisfies Meta;
-
-export default meta;
-
-type Story = StoryObj;
-
-/**
- * The default form of the aspect ratio.
- */
-export const Default: Story = {
- args: {
- ratio: 16 / 9,
- },
-};
-
-/**
- * Use the `1:1` aspect ratio to display a square image.
- */
-export const Square: Story = {
- args: {
- ratio: 1,
- },
-};
-
-/**
- * Use the `4:3` aspect ratio to display a landscape image.
- */
-export const Landscape: Story = {
- args: {
- ratio: 4 / 3,
- },
-};
-
-/**
- * Use the `2.35:1` aspect ratio to display a cinemascope image.
- */
-export const Cinemascope: Story = {
- args: {
- ratio: 2.35 / 1,
- },
-};
diff --git a/apps/storybook/stories/avatar.stories.tsx b/apps/storybook/stories/avatar.stories.tsx
deleted file mode 100644
index 45100c5..0000000
--- a/apps/storybook/stories/avatar.stories.tsx
+++ /dev/null
@@ -1,35 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import {
- Avatar,
- AvatarFallback,
- AvatarImage,
-} from '@konobangu/design-system/components/ui/avatar';
-
-/**
- * An image element with a fallback for representing the user.
- */
-const meta = {
- title: 'ui/Avatar',
- component: Avatar,
- tags: ['autodocs'],
- argTypes: {},
- render: (args) => (
-
-
- CN
-
- ),
- parameters: {
- layout: 'centered',
- },
-} satisfies Meta;
-
-export default meta;
-
-type Story = StoryObj;
-
-/**
- * The default form of the avatar.
- */
-export const Default: Story = {};
diff --git a/apps/storybook/stories/badge.stories.tsx b/apps/storybook/stories/badge.stories.tsx
deleted file mode 100644
index 4a72991..0000000
--- a/apps/storybook/stories/badge.stories.tsx
+++ /dev/null
@@ -1,62 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import { Badge } from '@konobangu/design-system/components/ui/badge';
-
-/**
- * Displays a badge or a component that looks like a badge.
- */
-const meta = {
- title: 'ui/Badge',
- component: Badge,
- tags: ['autodocs'],
- argTypes: {
- children: {
- control: 'text',
- },
- },
- args: {
- children: 'Badge',
- },
- parameters: {
- layout: 'centered',
- },
-} satisfies Meta;
-
-export default meta;
-
-type Story = StoryObj;
-
-/**
- * The default form of the badge.
- */
-export const Default: Story = {};
-
-/**
- * Use the `secondary` badge to call for less urgent information, blending
- * into the interface while still signaling minor updates or statuses.
- */
-export const Secondary: Story = {
- args: {
- variant: 'secondary',
- },
-};
-
-/**
- * Use the `destructive` badge to indicate errors, alerts, or the need for
- * immediate attention.
- */
-export const Destructive: Story = {
- args: {
- variant: 'destructive',
- },
-};
-
-/**
- * Use the `outline` badge for overlaying without obscuring interface details,
- * emphasizing clarity and subtlety..
- */
-export const Outline: Story = {
- args: {
- variant: 'outline',
- },
-};
diff --git a/apps/storybook/stories/breadcrumb.stories.tsx b/apps/storybook/stories/breadcrumb.stories.tsx
deleted file mode 100644
index bb4ca82..0000000
--- a/apps/storybook/stories/breadcrumb.stories.tsx
+++ /dev/null
@@ -1,78 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-import { ArrowRightSquare } from 'lucide-react';
-
-import {
- Breadcrumb,
- BreadcrumbItem,
- BreadcrumbLink,
- BreadcrumbList,
- BreadcrumbPage,
- BreadcrumbSeparator,
-} from '@konobangu/design-system/components/ui/breadcrumb';
-
-/**
- * Displays the path to the current resource using a hierarchy of links.
- */
-const meta = {
- title: 'ui/Breadcrumb',
- component: Breadcrumb,
- tags: ['autodocs'],
- argTypes: {},
- args: {},
- render: (args) => (
-
-
-
- Home
-
-
-
- Components
-
-
-
- Breadcrumb
-
-
-
- ),
- parameters: {
- layout: 'centered',
- },
-} satisfies Meta;
-
-export default meta;
-
-type Story = StoryObj;
-
-/**
- * Displays the path of links to the current resource.
- */
-export const Default: Story = {};
-
-/**
- * Displays the path with a custom icon for the separator.
- */
-export const WithCustomSeparator: Story = {
- render: (args) => (
-
-
-
- Home
-
-
-
-
-
- Components
-
-
-
-
-
- Breadcrumb
-
-
-
- ),
-};
diff --git a/apps/storybook/stories/button.stories.tsx b/apps/storybook/stories/button.stories.tsx
deleted file mode 100644
index 5cb5839..0000000
--- a/apps/storybook/stories/button.stories.tsx
+++ /dev/null
@@ -1,157 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-import { Loader2, Mail } from 'lucide-react';
-
-import { Button } from '@konobangu/design-system/components/ui/button';
-
-/**
- * Displays a button or a component that looks like a button.
- */
-const meta = {
- title: 'ui/Button',
- component: Button,
- tags: ['autodocs'],
- argTypes: {
- children: {
- control: 'text',
- },
- },
- parameters: {
- layout: 'centered',
- },
- args: {
- variant: 'default',
- size: 'default',
- children: 'Button',
- },
-} satisfies Meta;
-
-export default meta;
-
-type Story = StoryObj;
-
-/**
- * The default form of the button, used for primary actions and commands.
- */
-export const Default: Story = {};
-
-/**
- * Use the `outline` button to reduce emphasis on secondary actions, such as
- * canceling or dismissing a dialog.
- */
-export const Outline: Story = {
- args: {
- variant: 'outline',
- },
-};
-
-/**
- * Use the `ghost` button is minimalistic and subtle, for less intrusive
- * actions.
- */
-export const Ghost: Story = {
- args: {
- variant: 'ghost',
- },
-};
-
-/**
- * Use the `secondary` button to call for less emphasized actions, styled to
- * complement the primary button while being less conspicuous.
- */
-export const Secondary: Story = {
- args: {
- variant: 'secondary',
- },
-};
-
-/**
- * Use the `destructive` button to indicate errors, alerts, or the need for
- * immediate attention.
- */
-export const Destructive: Story = {
- args: {
- variant: 'destructive',
- },
-};
-
-/**
- * Use the `link` button to reduce emphasis on tertiary actions, such as
- * hyperlink or navigation, providing a text-only interactive element.
- */
-export const Link: Story = {
- args: {
- variant: 'link',
- },
-};
-
-/**
- * Add the `disabled` prop to a button to prevent interactions and add a
- * loading indicator, such as a spinner, to signify an in-progress action.
- */
-export const Loading: Story = {
- render: (args) => (
-
-
- Button
-
- ),
- args: {
- ...Outline.args,
- disabled: true,
- },
-};
-
-/**
- * Add an icon element to a button to enhance visual communication and
- * providing additional context for the action.
- */
-export const WithIcon: Story = {
- render: (args) => (
-
- Login with Email Button
-
- ),
- args: {
- ...Secondary.args,
- },
-};
-
-/**
- * Use the `sm` size for a smaller button, suitable for interfaces needing
- * compact elements without sacrificing usability.
- */
-export const Small: Story = {
- args: {
- size: 'sm',
- },
-};
-
-/**
- * Use the `lg` size for a larger button, offering better visibility and
- * easier interaction for users.
- */
-export const Large: Story = {
- args: {
- size: 'lg',
- },
-};
-
-/**
- * Use the "icon" size for a button with only an icon.
- */
-export const Icon: Story = {
- args: {
- ...Secondary.args,
- size: 'icon',
- children: ,
- },
-};
-
-/**
- * Add the `disabled` prop to prevent interactions with the button.
- */
-export const Disabled: Story = {
- args: {
- disabled: true,
- },
-};
diff --git a/apps/storybook/stories/calendar.stories.tsx b/apps/storybook/stories/calendar.stories.tsx
deleted file mode 100644
index ddd5b3f..0000000
--- a/apps/storybook/stories/calendar.stories.tsx
+++ /dev/null
@@ -1,81 +0,0 @@
-import { action } from '@storybook/addon-actions';
-import type { Meta, StoryObj } from '@storybook/react';
-import { addDays } from 'date-fns';
-
-import { Calendar } from '@konobangu/design-system/components/ui/calendar';
-
-/**
- * A date field component that allows users to enter and edit date.
- */
-const meta = {
- title: 'ui/Calendar',
- component: Calendar,
- tags: ['autodocs'],
- argTypes: {},
- args: {
- mode: 'single',
- selected: new Date(),
- onSelect: action('onDayClick'),
- className: 'rounded-md border w-fit',
- },
- parameters: {
- layout: 'centered',
- },
-} satisfies Meta;
-
-export default meta;
-
-type Story = StoryObj;
-
-/**
- * The default form of the calendar.
- */
-export const Default: Story = {};
-
-/**
- * Use the `multiple` mode to select multiple dates.
- */
-export const Multiple: Story = {
- args: {
- min: 1,
- selected: [new Date(), addDays(new Date(), 2), addDays(new Date(), 8)],
- mode: 'multiple',
- },
-};
-
-/**
- * Use the `range` mode to select a range of dates.
- */
-export const Range: Story = {
- args: {
- selected: {
- from: new Date(),
- to: addDays(new Date(), 7),
- },
- mode: 'range',
- },
-};
-
-/**
- * Use the `disabled` prop to disable specific dates.
- */
-export const Disabled: Story = {
- args: {
- disabled: [
- addDays(new Date(), 1),
- addDays(new Date(), 2),
- addDays(new Date(), 3),
- addDays(new Date(), 5),
- ],
- },
-};
-
-/**
- * Use the `numberOfMonths` prop to display multiple months.
- */
-export const MultipleMonths: Story = {
- args: {
- numberOfMonths: 2,
- showOutsideDays: false,
- },
-};
diff --git a/apps/storybook/stories/card.stories.tsx b/apps/storybook/stories/card.stories.tsx
deleted file mode 100644
index 7e0c04c..0000000
--- a/apps/storybook/stories/card.stories.tsx
+++ /dev/null
@@ -1,75 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-import { BellRing } from 'lucide-react';
-
-import {
- Card,
- CardContent,
- CardDescription,
- CardFooter,
- CardHeader,
- CardTitle,
-} from '@konobangu/design-system/components/ui/card';
-
-const notifications = [
- {
- title: 'Your call has been confirmed.',
- description: '1 hour ago',
- },
- {
- title: 'You have a new message!',
- description: '1 hour ago',
- },
- {
- title: 'Your subscription is expiring soon!',
- description: '2 hours ago',
- },
-];
-
-/**
- * Displays a card with header, content, and footer.
- */
-const meta = {
- title: 'ui/Card',
- component: Card,
- tags: ['autodocs'],
- argTypes: {},
- args: {
- className: 'w-96',
- },
- render: (args) => (
-
-
- Notifications
- You have 3 unread messages.
-
-
- {notifications.map((notification, index) => (
-
-
-
-
{notification.title}
-
{notification.description}
-
-
- ))}
-
-
-
- Close
-
-
-
- ),
- parameters: {
- layout: 'centered',
- },
-} satisfies Meta;
-
-export default meta;
-
-type Story = StoryObj;
-
-/**
- * The default form of the card.
- */
-export const Default: Story = {};
diff --git a/apps/storybook/stories/carousel.stories.tsx b/apps/storybook/stories/carousel.stories.tsx
deleted file mode 100644
index 0e0ec68..0000000
--- a/apps/storybook/stories/carousel.stories.tsx
+++ /dev/null
@@ -1,73 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import {
- Carousel,
- CarouselContent,
- CarouselItem,
- CarouselNext,
- CarouselPrevious,
-} from '@konobangu/design-system/components/ui/carousel';
-
-/**
- * A carousel with motion and swipe built using Embla.
- */
-const meta: Meta = {
- title: 'ui/Carousel',
- component: Carousel,
- tags: ['autodocs'],
- argTypes: {},
- args: {
- className: 'w-full max-w-xs',
- },
- render: (args) => (
-
-
- {Array.from({ length: 5 }).map((_, index) => (
-
-
- {index + 1}
-
-
- ))}
-
-
-
-
- ),
- parameters: {
- layout: 'centered',
- },
-} satisfies Meta;
-
-export default meta;
-
-type Story = StoryObj;
-
-/**
- * The default form of the carousel.
- */
-export const Default: Story = {};
-
-/**
- * Use the `basis` utility class to change the size of the carousel.
- */
-export const Size: Story = {
- render: (args) => (
-
-
- {Array.from({ length: 5 }).map((_, index) => (
-
-
- {index + 1}
-
-
- ))}
-
-
-
-
- ),
- args: {
- className: 'mx-12 w-full max-w-xs',
- },
-};
diff --git a/apps/storybook/stories/chart.stories.tsx b/apps/storybook/stories/chart.stories.tsx
deleted file mode 100644
index d90937f..0000000
--- a/apps/storybook/stories/chart.stories.tsx
+++ /dev/null
@@ -1,271 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-import { useMemo } from 'react';
-import {
- Area,
- AreaChart,
- Bar,
- BarChart,
- CartesianGrid,
- Label,
- Line,
- LineChart,
- Pie,
- PieChart,
- XAxis,
-} from 'recharts';
-
-import {
- type ChartConfig,
- ChartContainer,
- ChartTooltip,
- ChartTooltipContent,
-} from '@konobangu/design-system/components/ui/chart';
-
-const multiSeriesData = [
- { month: 'January', desktop: 186, mobile: 80 },
- { month: 'February', desktop: 305, mobile: 200 },
- { month: 'March', desktop: 237, mobile: 120 },
- { month: 'April', desktop: 73, mobile: 190 },
- { month: 'May', desktop: 209, mobile: 130 },
- { month: 'June', desktop: 214, mobile: 140 },
-];
-
-const multiSeriesConfig = {
- desktop: {
- label: 'Desktop',
- color: 'hsl(var(--chart-1))',
- },
- mobile: {
- label: 'Mobile',
- color: 'hsl(var(--chart-2))',
- },
-} satisfies ChartConfig;
-
-const singleSeriesData = [
- { browser: 'chrome', visitors: 275, fill: 'var(--color-chrome)' },
- { browser: 'safari', visitors: 200, fill: 'var(--color-safari)' },
- { browser: 'other', visitors: 190, fill: 'var(--color-other)' },
-];
-
-const singleSeriesConfig = {
- visitors: {
- label: 'Visitors',
- },
- chrome: {
- label: 'Chrome',
- color: 'hsl(var(--chart-1))',
- },
- safari: {
- label: 'Safari',
- color: 'hsl(var(--chart-2))',
- },
- other: {
- label: 'Other',
- color: 'hsl(var(--chart-5))',
- },
-} satisfies ChartConfig;
-
-/**
- * Beautiful charts. Built using Recharts. Copy and paste into your apps.
- */
-const meta = {
- title: 'ui/Chart',
- component: ChartContainer,
- tags: ['autodocs'],
- argTypes: {},
- args: {
- children:
,
- },
-} satisfies Meta;
-
-export default meta;
-
-type Story = StoryObj;
-
-/**
- * Combine multiple Area components to create a stacked area chart.
- */
-export const StackedAreaChart: Story = {
- args: {
- config: multiSeriesConfig,
- },
- render: (args) => (
-
-
-
- value.slice(0, 3)}
- />
- }
- />
-
-
-
-
- ),
-};
-
-/**
- * Combine multiple Bar components to create a stacked bar chart.
- */
-export const StackedBarChart: Story = {
- args: {
- config: multiSeriesConfig,
- },
- render: (args) => (
-
-
-
- value.slice(0, 3)}
- />
- }
- />
-
-
-
-
- ),
-};
-
-/**
- * Combine multiple Line components to create a single line chart.
- */
-export const MultiLineChart: Story = {
- args: {
- config: multiSeriesConfig,
- },
- render: (args) => (
-
-
-
- value.slice(0, 3)}
- />
- }
- />
-
-
-
-
- ),
-};
-
-/**
- * Combine Pie and Label components to create a doughnut chart.
- */
-export const DoughnutChart: Story = {
- args: {
- config: singleSeriesConfig,
- },
- render: (args) => {
- const totalVisitors = useMemo(() => {
- return singleSeriesData.reduce((acc, curr) => acc + curr.visitors, 0);
- }, []);
- return (
-
-
- }
- />
-
- {
- if (viewBox && 'cx' in viewBox && 'cy' in viewBox) {
- return (
-
-
- {totalVisitors.toLocaleString()}
-
-
- Visitors
-
-
- );
- }
- }}
- />
-
-
-
- );
- },
-};
diff --git a/apps/storybook/stories/checkbox.stories.tsx b/apps/storybook/stories/checkbox.stories.tsx
deleted file mode 100644
index 0415d10..0000000
--- a/apps/storybook/stories/checkbox.stories.tsx
+++ /dev/null
@@ -1,50 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import { Checkbox } from '@konobangu/design-system/components/ui/checkbox';
-
-/**
- * A control that allows the user to toggle between checked and not checked.
- */
-const meta: Meta = {
- title: 'ui/Checkbox',
- component: Checkbox,
- tags: ['autodocs'],
- argTypes: {},
- args: {
- id: 'terms',
- disabled: false,
- },
- render: (args) => (
-
-
-
- Accept terms and conditions
-
-
- ),
- parameters: {
- layout: 'centered',
- },
-} satisfies Meta;
-
-export default meta;
-
-type Story = StoryObj;
-
-/**
- * The default form of the checkbox.
- */
-export const Default: Story = {};
-
-/**
- * Use the `disabled` prop to disable the checkbox.
- */
-export const Disabled: Story = {
- args: {
- id: 'disabled-terms',
- disabled: true,
- },
-};
diff --git a/apps/storybook/stories/collapsible.stories.tsx b/apps/storybook/stories/collapsible.stories.tsx
deleted file mode 100644
index e4d6bd9..0000000
--- a/apps/storybook/stories/collapsible.stories.tsx
+++ /dev/null
@@ -1,55 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-import { Info } from 'lucide-react';
-
-import {
- Collapsible,
- CollapsibleContent,
- CollapsibleTrigger,
-} from '@konobangu/design-system/components/ui/collapsible';
-
-/**
- * An interactive component which expands/collapses a panel.
- */
-const meta = {
- title: 'ui/Collapsible',
- component: Collapsible,
- tags: ['autodocs'],
- argTypes: {},
- args: {
- className: 'w-96',
- disabled: false,
- },
- render: (args) => (
-
-
- Can I use this in my project?
-
-
-
- Yes. Free to use for personal and commercial projects. No attribution
- required.
-
-
- ),
- parameters: {
- layout: 'centered',
- },
-} satisfies Meta;
-
-export default meta;
-
-type Story = StoryObj;
-
-/**
- * The default form of the collapsible.
- */
-export const Default: Story = {};
-
-/**
- * Use the `disabled` prop to disable the interaction.
- */
-export const Disabled: Story = {
- args: {
- disabled: true,
- },
-};
diff --git a/apps/storybook/stories/command.stories.tsx b/apps/storybook/stories/command.stories.tsx
deleted file mode 100644
index ca2a6ad..0000000
--- a/apps/storybook/stories/command.stories.tsx
+++ /dev/null
@@ -1,55 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-import { CommandSeparator } from 'cmdk';
-
-import {
- Command,
- CommandEmpty,
- CommandGroup,
- CommandInput,
- CommandItem,
- CommandList,
-} from '@konobangu/design-system/components/ui/command';
-
-/**
- * Fast, composable, unstyled command menu for React.
- */
-const meta = {
- title: 'ui/Command',
- component: Command,
- tags: ['autodocs'],
- argTypes: {},
- args: {
- className: 'rounded-lg w-96 border shadow-md',
- },
- render: (args) => (
-
-
-
- No results found.
-
- Calendar
- Search Emoji
- Calculator
-
-
-
- Profile
- Billing
- Settings
-
-
-
- ),
- parameters: {
- layout: 'centered',
- },
-} satisfies Meta;
-
-export default meta;
-
-type Story = StoryObj;
-
-/**
- * The default form of the command.
- */
-export const Default: Story = {};
diff --git a/apps/storybook/stories/context-menu.stories.tsx b/apps/storybook/stories/context-menu.stories.tsx
deleted file mode 100644
index 0133300..0000000
--- a/apps/storybook/stories/context-menu.stories.tsx
+++ /dev/null
@@ -1,153 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import {
- ContextMenu,
- ContextMenuCheckboxItem,
- ContextMenuContent,
- ContextMenuItem,
- ContextMenuLabel,
- ContextMenuRadioGroup,
- ContextMenuRadioItem,
- ContextMenuSeparator,
- ContextMenuShortcut,
- ContextMenuSub,
- ContextMenuSubContent,
- ContextMenuSubTrigger,
- ContextMenuTrigger,
-} from '@konobangu/design-system/components/ui/context-menu';
-
-/**
- * Displays a menu to the user — such as a set of actions or functions —
- * triggered by a button.
- */
-const meta = {
- title: 'ui/ContextMenu',
- component: ContextMenu,
- tags: ['autodocs'],
- argTypes: {},
- args: {},
- render: (args) => (
-
-
- Right click here
-
-
- Profile
- Billing
- Team
- Subscription
-
-
- ),
- parameters: {
- layout: 'centered',
- },
-} satisfies Meta;
-
-export default meta;
-
-type Story = StoryObj;
-
-/**
- * The default form of the context menu.
- */
-export const Default: Story = {};
-
-/**
- * A context menu with shortcuts.
- */
-export const WithShortcuts: Story = {
- render: (args) => (
-
-
- Right click here
-
-
-
- Back
- ⌘[
-
-
- Forward
- ⌘]
-
-
- Reload
- ⌘R
-
-
-
- ),
-};
-
-/**
- * A context menu with a submenu.
- */
-export const WithSubmenu: Story = {
- render: (args) => (
-
-
- Right click here
-
-
-
- New Tab
- ⌘N
-
-
- More Tools
-
-
- Save Page As...
- ⇧⌘S
-
- Create Shortcut...
- Name Window...
-
- Developer Tools
-
-
-
-
- ),
-};
-
-/**
- * A context menu with checkboxes.
- */
-export const WithCheckboxes: Story = {
- render: (args) => (
-
-
- Right click here
-
-
-
- Show Comments
- ⌘⇧C
-
- Show Preview
-
-
- ),
-};
-
-/**
- * A context menu with a radio group.
- */
-export const WithRadioGroup: Story = {
- render: (args) => (
-
-
- Right click here
-
-
-
- Theme
- Light
- Dark
-
-
-
- ),
-};
diff --git a/apps/storybook/stories/dialog.stories.tsx b/apps/storybook/stories/dialog.stories.tsx
deleted file mode 100644
index 9ffd6f1..0000000
--- a/apps/storybook/stories/dialog.stories.tsx
+++ /dev/null
@@ -1,62 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import {
- Dialog,
- DialogClose,
- DialogContent,
- DialogDescription,
- DialogFooter,
- DialogHeader,
- DialogTitle,
- DialogTrigger,
-} from '@konobangu/design-system/components/ui/dialog';
-
-/**
- * A window overlaid on either the primary window or another dialog window,
- * rendering the content underneath inert.
- */
-const meta = {
- title: 'ui/Dialog',
- component: Dialog,
- tags: ['autodocs'],
- argTypes: {},
- render: (args) => (
-
- Open
-
-
- Are you absolutely sure?
-
- This action cannot be undone. This will permanently delete your
- account and remove your data from our servers.
-
-
-
-
- Cancel
-
-
-
- Continue
-
-
-
-
-
- ),
- parameters: {
- layout: 'centered',
- },
-} satisfies Meta;
-
-export default meta;
-
-type Story = StoryObj;
-
-/**
- * The default form of the dialog.
- */
-export const Default: Story = {};
diff --git a/apps/storybook/stories/drawer.stories.tsx b/apps/storybook/stories/drawer.stories.tsx
deleted file mode 100644
index 45c0d00..0000000
--- a/apps/storybook/stories/drawer.stories.tsx
+++ /dev/null
@@ -1,58 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import {
- Drawer,
- DrawerClose,
- DrawerContent,
- DrawerDescription,
- DrawerFooter,
- DrawerHeader,
- DrawerTitle,
- DrawerTrigger,
-} from '@konobangu/design-system/components/ui/drawer';
-
-/**
- * A drawer component for React.
- */
-const meta: Meta = {
- title: 'ui/Drawer',
- component: Drawer,
- tags: ['autodocs'],
- argTypes: {},
- render: (args) => (
-
- Open
-
-
- Are you sure absolutely sure?
- This action cannot be undone.
-
-
-
- Submit
-
-
-
- Cancel
-
-
-
-
-
- ),
- parameters: {
- layout: 'centered',
- },
-};
-
-export default meta;
-
-type Story = StoryObj;
-
-/**
- * The default form of the drawer.
- */
-export const Default: Story = {};
diff --git a/apps/storybook/stories/dropdown-menu.stories.tsx b/apps/storybook/stories/dropdown-menu.stories.tsx
deleted file mode 100644
index 701ee9d..0000000
--- a/apps/storybook/stories/dropdown-menu.stories.tsx
+++ /dev/null
@@ -1,159 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-import { Mail, Plus, PlusCircle, Search, UserPlus } from 'lucide-react';
-
-import {
- DropdownMenu,
- DropdownMenuCheckboxItem,
- DropdownMenuContent,
- DropdownMenuGroup,
- DropdownMenuItem,
- DropdownMenuLabel,
- DropdownMenuPortal,
- DropdownMenuRadioGroup,
- DropdownMenuRadioItem,
- DropdownMenuSeparator,
- DropdownMenuShortcut,
- DropdownMenuSub,
- DropdownMenuSubContent,
- DropdownMenuSubTrigger,
- DropdownMenuTrigger,
-} from '@konobangu/design-system/components/ui/dropdown-menu';
-
-/**
- * Displays a menu to the user — such as a set of actions or functions —
- * triggered by a button.
- */
-const meta = {
- title: 'ui/DropdownMenu',
- component: DropdownMenu,
- tags: ['autodocs'],
- argTypes: {},
- render: (args) => (
-
- Open
-
- My Account
-
- Profile
- Billing
- Team
- Subscription
-
-
- ),
- parameters: {
- layout: 'centered',
- },
-} satisfies Meta;
-
-export default meta;
-
-type Story = StoryObj;
-
-/**
- * The default form of the dropdown menu.
- */
-export const Default: Story = {};
-
-/**
- * A dropdown menu with shortcuts.
- */
-export const WithShortcuts: Story = {
- render: (args) => (
-
- Open
-
- Controls
-
- Back
- ⌘[
-
-
- Forward
- ⌘]
-
-
-
- ),
-};
-
-/**
- * A dropdown menu with submenus.
- */
-export const WithSubmenus: Story = {
- render: (args) => (
-
- Open
-
-
-
- Search
-
-
-
-
-
- New Team
- ⌘+T
-
-
-
-
- Invite users
-
-
-
-
-
- Email
-
-
-
-
- More...
-
-
-
-
-
-
-
- ),
-};
-
-/**
- * A dropdown menu with radio items.
- */
-export const WithRadioItems: Story = {
- render: (args) => (
-
- Open
-
- Status
-
- Info
- Warning
- Error
-
-
-
- ),
-};
-
-/**
- * A dropdown menu with checkboxes.
- */
-export const WithCheckboxes: Story = {
- render: (args) => (
-
- Open
-
-
- Autosave
- ⌘S
-
- Show Comments
-
-
- ),
-};
diff --git a/apps/storybook/stories/form.stories.tsx b/apps/storybook/stories/form.stories.tsx
deleted file mode 100644
index d7bc5e8..0000000
--- a/apps/storybook/stories/form.stories.tsx
+++ /dev/null
@@ -1,85 +0,0 @@
-import { zodResolver } from '@hookform/resolvers/zod';
-import { action } from '@storybook/addon-actions';
-import type { Meta, StoryObj } from '@storybook/react';
-import { useForm } from 'react-hook-form';
-import * as z from 'zod';
-
-import {
- Form,
- FormControl,
- FormDescription,
- FormField,
- FormItem,
- FormLabel,
- FormMessage,
-} from '@konobangu/design-system/components/ui/form';
-
-/**
- * Building forms with React Hook Form and Zod.
- */
-const meta: Meta = {
- title: 'ui/Form',
- component: Form,
- tags: ['autodocs'],
- argTypes: {},
- render: (args) => ,
-} satisfies Meta;
-
-export default meta;
-
-type Story = StoryObj;
-
-const formSchema = z.object({
- username: z.string().min(2, {
- message: 'Username must be at least 2 characters.',
- }),
-});
-
-const ProfileForm = (args: Story['args']) => {
- const form = useForm>({
- resolver: zodResolver(formSchema),
- defaultValues: {
- username: '',
- },
- });
- function onSubmit(values: z.infer) {
- action('onSubmit')(values);
- }
- return (
-
-
- );
-};
-
-/**
- * The default form of the form.
- */
-export const Default: Story = {};
diff --git a/apps/storybook/stories/hover-card.stories.tsx b/apps/storybook/stories/hover-card.stories.tsx
deleted file mode 100644
index 4b8b1d0..0000000
--- a/apps/storybook/stories/hover-card.stories.tsx
+++ /dev/null
@@ -1,49 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import {
- HoverCard,
- HoverCardContent,
- HoverCardTrigger,
-} from '@konobangu/design-system/components/ui/hover-card';
-
-/**
- * For sighted users to preview content available behind a link.
- */
-const meta = {
- title: 'ui/HoverCard',
- component: HoverCard,
- tags: ['autodocs'],
- argTypes: {},
- args: {},
- render: (args) => (
-
- Hover
-
- The React Framework - created and maintained by @vercel.
-
-
- ),
- parameters: {
- layout: 'centered',
- },
-} satisfies Meta;
-
-export default meta;
-
-type Story = StoryObj;
-
-/**
- * The default form of the hover card.
- */
-export const Default: Story = {};
-
-/**
- * Use the `openDelay` and `closeDelay` props to control the delay before the
- * hover card opens and closes.
- */
-export const Instant: Story = {
- args: {
- openDelay: 0,
- closeDelay: 0,
- },
-};
diff --git a/apps/storybook/stories/input-otp.stories.tsx b/apps/storybook/stories/input-otp.stories.tsx
deleted file mode 100644
index c2102ab..0000000
--- a/apps/storybook/stories/input-otp.stories.tsx
+++ /dev/null
@@ -1,70 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-import { REGEXP_ONLY_DIGITS_AND_CHARS } from 'input-otp';
-
-import {
- InputOTP,
- InputOTPGroup,
- InputOTPSeparator,
- InputOTPSlot,
-} from '@konobangu/design-system/components/ui/input-otp';
-
-/**
- * Accessible one-time password component with copy paste functionality.
- */
-const meta = {
- title: 'ui/InputOTP',
- component: InputOTP,
- tags: ['autodocs'],
- argTypes: {},
- args: {
- maxLength: 6,
- pattern: REGEXP_ONLY_DIGITS_AND_CHARS,
- children: null,
- },
-
- render: (args) => (
-
-
-
-
-
-
-
-
-
-
- ),
- parameters: {
- layout: 'centered',
- },
-} satisfies Meta;
-
-export default meta;
-
-type Story = StoryObj;
-
-/**
- * The default form of the InputOTP field.
- */
-export const Default: Story = {};
-
-/**
- * Use multiple groups to separate the input slots.
- */
-export const SeparatedGroup: Story = {
- render: (args) => (
-
-
-
-
-
-
-
-
-
-
-
-
-
- ),
-};
diff --git a/apps/storybook/stories/input.stories.tsx b/apps/storybook/stories/input.stories.tsx
deleted file mode 100644
index 42abeb8..0000000
--- a/apps/storybook/stories/input.stories.tsx
+++ /dev/null
@@ -1,84 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import { Input } from '@konobangu/design-system/components/ui/input';
-
-/**
- * Displays a form input field or a component that looks like an input field.
- */
-const meta = {
- title: 'ui/Input',
- component: Input,
- tags: ['autodocs'],
- argTypes: {},
- args: {
- className: 'w-96',
- type: 'email',
- placeholder: 'Email',
- disabled: false,
- },
- parameters: {
- layout: 'centered',
- },
-} satisfies Meta;
-
-export default meta;
-
-type Story = StoryObj;
-
-/**
- * The default form of the input field.
- */
-export const Default: Story = {};
-
-/**
- * Use the `disabled` prop to make the input non-interactive and appears faded,
- * indicating that input is not currently accepted.
- */
-export const Disabled: Story = {
- args: { disabled: true },
-};
-
-/**
- * Use the `Label` component to includes a clear, descriptive label above or
- * alongside the input area to guide users.
- */
-export const WithLabel: Story = {
- render: (args) => (
-
- {args.placeholder}
-
-
- ),
-};
-
-/**
- * Use a text element below the input field to provide additional instructions
- * or information to users.
- */
-export const WithHelperText: Story = {
- render: (args) => (
-
-
{args.placeholder}
-
-
Enter your email address.
-
- ),
-};
-
-/**
- * Use the `Button` component to indicate that the input field can be submitted
- * or used to trigger an action.
- */
-export const WithButton: Story = {
- render: (args) => (
-
-
-
- Subscribe
-
-
- ),
-};
diff --git a/apps/storybook/stories/label.stories.tsx b/apps/storybook/stories/label.stories.tsx
deleted file mode 100644
index 8e63fea..0000000
--- a/apps/storybook/stories/label.stories.tsx
+++ /dev/null
@@ -1,30 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import { Label } from '@konobangu/design-system/components/ui/label';
-
-/**
- * Renders an accessible label associated with controls.
- */
-const meta = {
- title: 'ui/Label',
- component: Label,
- tags: ['autodocs'],
- argTypes: {
- children: {
- control: { type: 'text' },
- },
- },
- args: {
- children: 'Your email address',
- htmlFor: 'email',
- },
-} satisfies Meta;
-
-export default meta;
-
-type Story = StoryObj;
-
-/**
- * The default form of the label.
- */
-export const Default: Story = {};
diff --git a/apps/storybook/stories/menubar.stories.tsx b/apps/storybook/stories/menubar.stories.tsx
deleted file mode 100644
index 59d6103..0000000
--- a/apps/storybook/stories/menubar.stories.tsx
+++ /dev/null
@@ -1,126 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import {
- Menubar,
- MenubarCheckboxItem,
- MenubarContent,
- MenubarGroup,
- MenubarItem,
- MenubarLabel,
- MenubarMenu,
- MenubarRadioGroup,
- MenubarRadioItem,
- MenubarSeparator,
- MenubarShortcut,
- MenubarSub,
- MenubarSubContent,
- MenubarSubTrigger,
- MenubarTrigger,
-} from '@konobangu/design-system/components/ui/menubar';
-
-/**
- * A visually persistent menu common in desktop applications that provides
- * quick access to a consistent set of commands.
- */
-const meta = {
- title: 'ui/Menubar',
- component: Menubar,
- tags: ['autodocs'],
- argTypes: {},
-
- render: (args) => (
-
-
- File
-
-
- New Tab ⌘T
-
- New Window
-
- Share
-
- Print
-
-
-
- ),
- parameters: {
- layout: 'centered',
- },
-} satisfies Meta;
-
-export default meta;
-
-type Story = StoryObj;
-
-/**
- * The default form of the menubar.
- */
-export const Default: Story = {};
-
-/**
- * A menubar with a submenu.
- */
-export const WithSubmenu: Story = {
- render: (args) => (
-
-
- Actions
-
- Download
-
- Share
-
- Email link
- Messages
- Notes
-
-
-
-
-
- ),
-};
-
-/**
- * A menubar with radio items.
- */
-export const WithRadioItems: Story = {
- render: (args) => (
-
-
- View
-
- Device Size
-
- Small
- Medium
- Large
-
-
-
-
- ),
-};
-
-/**
- * A menubar with checkbox items.
- */
-export const WithCheckboxItems: Story = {
- render: (args) => (
-
-
- Filters
-
- Show All
-
- Unread
- Important
- Flagged
-
-
-
-
- ),
-};
diff --git a/apps/storybook/stories/navigation-menu.stories.tsx b/apps/storybook/stories/navigation-menu.stories.tsx
deleted file mode 100644
index e2f158c..0000000
--- a/apps/storybook/stories/navigation-menu.stories.tsx
+++ /dev/null
@@ -1,79 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import {
- NavigationMenu,
- NavigationMenuContent,
- NavigationMenuItem,
- NavigationMenuLink,
- NavigationMenuList,
- NavigationMenuTrigger,
- navigationMenuTriggerStyle,
-} from '@konobangu/design-system/components/ui/navigation-menu';
-
-/**
- * A collection of links for navigating websites.
- */
-const meta = {
- title: 'ui/NavigationMenu',
- component: NavigationMenu,
- tags: ['autodocs'],
- argTypes: {},
- render: (args) => (
-
-
-
-
- Overview
-
-
-
-
-
- Documentation
-
-
-
-
-
- API Reference
-
-
-
-
- Getting Started
-
-
-
-
- Guides
-
-
-
-
-
-
-
-
- External
-
-
-
-
- ),
- parameters: {
- layout: 'centered',
- },
-} satisfies Meta;
-
-export default meta;
-
-type Story = StoryObj;
-
-/**
- * The default form of the navigation menu.
- */
-export const Default: Story = {};
diff --git a/apps/storybook/stories/pagination.stories.tsx b/apps/storybook/stories/pagination.stories.tsx
deleted file mode 100644
index 4ecb778..0000000
--- a/apps/storybook/stories/pagination.stories.tsx
+++ /dev/null
@@ -1,57 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import {
- Pagination,
- PaginationContent,
- PaginationEllipsis,
- PaginationItem,
- PaginationLink,
- PaginationNext,
- PaginationPrevious,
-} from '@konobangu/design-system/components/ui/pagination';
-
-/**
- * Pagination with page navigation, next and previous links.
- */
-const meta = {
- title: 'ui/Pagination',
- component: Pagination,
- tags: ['autodocs'],
- argTypes: {},
- render: (args) => (
-
-
-
-
-
-
- 1
-
-
- 2
-
-
- 3
-
-
-
-
-
-
-
-
-
- ),
- parameters: {
- layout: 'centered',
- },
-} satisfies Meta;
-
-export default meta;
-
-type Story = StoryObj;
-
-/**
- * The default form of the pagination.
- */
-export const Default: Story = {};
diff --git a/apps/storybook/stories/popover.stories.tsx b/apps/storybook/stories/popover.stories.tsx
deleted file mode 100644
index 16212ec..0000000
--- a/apps/storybook/stories/popover.stories.tsx
+++ /dev/null
@@ -1,36 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import {
- Popover,
- PopoverContent,
- PopoverTrigger,
-} from '@konobangu/design-system/components/ui/popover';
-
-/**
- * Displays rich content in a portal, triggered by a button.
- */
-const meta = {
- title: 'ui/Popover',
- component: Popover,
- tags: ['autodocs'],
- argTypes: {},
-
- render: (args) => (
-
- Open
- Place content for the popover here.
-
- ),
- parameters: {
- layout: 'centered',
- },
-} satisfies Meta;
-
-export default meta;
-
-type Story = StoryObj;
-
-/**
- * The default form of the popover.
- */
-export const Default: Story = {};
diff --git a/apps/storybook/stories/progress.stories.tsx b/apps/storybook/stories/progress.stories.tsx
deleted file mode 100644
index 6969c73..0000000
--- a/apps/storybook/stories/progress.stories.tsx
+++ /dev/null
@@ -1,45 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import { Progress } from '@konobangu/design-system/components/ui/progress';
-
-/**
- * Displays an indicator showing the completion progress of a task, typically
- * displayed as a progress bar.
- */
-const meta = {
- title: 'ui/Progress',
- component: Progress,
- tags: ['autodocs'],
- argTypes: {},
- args: {
- value: 30,
- max: 100,
- },
-} satisfies Meta;
-
-export default meta;
-
-type Story = StoryObj;
-
-/**
- * The default form of the progress.
- */
-export const Default: Story = {};
-
-/**
- * When the progress is indeterminate.
- */
-export const Indeterminate: Story = {
- args: {
- value: undefined,
- },
-};
-
-/**
- * When the progress is completed.
- */
-export const Completed: Story = {
- args: {
- value: 100,
- },
-};
diff --git a/apps/storybook/stories/radio-group.stories.tsx b/apps/storybook/stories/radio-group.stories.tsx
deleted file mode 100644
index 66df39f..0000000
--- a/apps/storybook/stories/radio-group.stories.tsx
+++ /dev/null
@@ -1,40 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import {
- RadioGroup,
- RadioGroupItem,
-} from '@konobangu/design-system/components/ui/radio-group';
-
-/**
- * A set of checkable buttons—known as radio buttons—where no more than one of
- * the buttons can be checked at a time.
- */
-const meta = {
- title: 'ui/RadioGroup',
- component: RadioGroup,
- tags: ['autodocs'],
- argTypes: {},
- args: {
- defaultValue: 'comfortable',
- className: 'grid gap-2 grid-cols-[1rem_1fr] items-center',
- },
- render: (args) => (
-
-
- Default
-
- Comfortable
-
- Compact
-
- ),
-} satisfies Meta;
-
-export default meta;
-
-type Story = StoryObj;
-
-/**
- * The default form of the radio group.
- */
-export const Default: Story = {};
diff --git a/apps/storybook/stories/resizable.stories.tsx b/apps/storybook/stories/resizable.stories.tsx
deleted file mode 100644
index b60bcad..0000000
--- a/apps/storybook/stories/resizable.stories.tsx
+++ /dev/null
@@ -1,59 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import {
- ResizableHandle,
- ResizablePanel,
- ResizablePanelGroup,
-} from '@konobangu/design-system/components/ui/resizable';
-
-/**
- * Accessible resizable panel groups and layouts with keyboard support.
- */
-const meta: Meta = {
- title: 'ui/ResizablePanelGroup',
- component: ResizablePanelGroup,
- tags: ['autodocs'],
- argTypes: {
- onLayout: {
- control: false,
- },
- },
- args: {
- className: 'max-w-96 rounded-lg border',
- direction: 'horizontal',
- },
- render: (args) => (
-
-
-
- One
-
-
-
-
-
-
-
- Two
-
-
-
-
-
- Three
-
-
-
-
-
- ),
-} satisfies Meta;
-
-export default meta;
-
-type Story = StoryObj;
-
-/**
- * The default form of the resizable panel group.
- */
-export const Default: Story = {};
diff --git a/apps/storybook/stories/scroll-area.stories.tsx b/apps/storybook/stories/scroll-area.stories.tsx
deleted file mode 100644
index d9d8a9e..0000000
--- a/apps/storybook/stories/scroll-area.stories.tsx
+++ /dev/null
@@ -1,62 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import { ScrollArea } from '@konobangu/design-system/components/ui/scroll-area';
-
-/**
- * Augments native scroll functionality for custom, cross-browser styling.
- */
-const meta = {
- title: 'ui/ScrollArea',
- component: ScrollArea,
- tags: ['autodocs'],
- argTypes: {
- children: {
- control: 'text',
- },
- },
- args: {
- className: 'h-32 w-80 rounded-md border p-4',
- type: 'auto',
- children:
- "Jokester began sneaking into the castle in the middle of the night and leaving jokes all over the place: under the king's pillow, in his soup, even in the royal toilet. The king was furious, but he couldn't seem to stop Jokester. And then, one day, the people of the kingdom discovered that the jokes left by Jokester were so funny that they couldn't help but laugh. And once they started laughing, they couldn't stop. The king was so angry that he banished Jokester from the kingdom, but the people still laughed, and they laughed, and they laughed. And they all lived happily ever after.",
- },
- parameters: {
- layout: 'centered',
- },
-} satisfies Meta;
-
-export default meta;
-
-type Story = StoryObj;
-
-/**
- * The default form of the scroll area.
- */
-export const Default: Story = {};
-
-/**
- * Use the `type` prop with `always` to always show the scroll area.
- */
-export const Always: Story = {
- args: {
- type: 'always',
- },
-};
-
-/**
- * Use the `type` prop with `hover` to show the scroll area on hover.
- */
-export const Hover: Story = {
- args: {
- type: 'hover',
- },
-};
-
-/**
- * Use the `type` prop with `scroll` to show the scroll area when scrolling.
- */
-export const Scroll: Story = {
- args: {
- type: 'scroll',
- },
-};
diff --git a/apps/storybook/stories/select.stories.tsx b/apps/storybook/stories/select.stories.tsx
deleted file mode 100644
index e92b779..0000000
--- a/apps/storybook/stories/select.stories.tsx
+++ /dev/null
@@ -1,70 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import {
- Select,
- SelectContent,
- SelectGroup,
- SelectItem,
- SelectLabel,
- SelectSeparator,
- SelectTrigger,
- SelectValue,
-} from '@konobangu/design-system/components/ui/select';
-
-/**
- * Displays a list of options for the user to pick from—triggered by a button.
- */
-const meta: Meta = {
- title: 'ui/Select',
- component: Select,
- tags: ['autodocs'],
- argTypes: {},
- render: (args) => (
-
-
-
-
-
-
- Fruits
- Apple
- Banana
- Blueberry
- Grapes
- Pineapple
-
-
-
- Vegetables
- Aubergine
- Broccoli
-
- Carrot
-
- Courgette
- Leek
-
-
-
- Meat
- Beef
- Chicken
- Lamb
- Pork
-
-
-
- ),
- parameters: {
- layout: 'centered',
- },
-} satisfies Meta;
-
-export default meta;
-
-type Story = StoryObj;
-
-/**
- * The default form of the select.
- */
-export const Default: Story = {};
diff --git a/apps/storybook/stories/separator.stories.tsx b/apps/storybook/stories/separator.stories.tsx
deleted file mode 100644
index f088a94..0000000
--- a/apps/storybook/stories/separator.stories.tsx
+++ /dev/null
@@ -1,43 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import { Separator } from '@konobangu/design-system/components/ui/separator';
-
-/**
- * Visually or semantically separates content.
- */
-const meta = {
- title: 'ui/Separator',
- component: Separator,
- tags: ['autodocs'],
- argTypes: {},
-} satisfies Meta;
-
-export default meta;
-
-type Story = StoryObj;
-
-/**
- * The default form of the separator.
- */
-export const Horizontal: Story = {
- render: () => (
-
- ),
-};
-
-/**
- * A vertical separator.
- */
-export const Vertical: Story = {
- render: () => (
-
- ),
-};
diff --git a/apps/storybook/stories/sheet.stories.tsx b/apps/storybook/stories/sheet.stories.tsx
deleted file mode 100644
index 934d99f..0000000
--- a/apps/storybook/stories/sheet.stories.tsx
+++ /dev/null
@@ -1,72 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import {
- Sheet,
- SheetClose,
- SheetContent,
- SheetDescription,
- SheetFooter,
- SheetHeader,
- SheetTitle,
- SheetTrigger,
-} from '@konobangu/design-system/components/ui/sheet';
-
-/**
- * Extends the Dialog component to display content that complements the main
- * content of the screen.
- */
-const meta: Meta = {
- title: 'ui/Sheet',
- component: Sheet,
- tags: ['autodocs'],
- argTypes: {
- side: {
- options: ['top', 'bottom', 'left', 'right'],
- control: {
- type: 'radio',
- },
- },
- },
- args: {
- side: 'right',
- },
- render: (args) => (
-
- Open
-
-
- Are you absolutely sure?
-
- This action cannot be undone. This will permanently delete your
- account and remove your data from our servers.
-
-
-
-
-
- Cancel
-
-
-
- Submit
-
-
-
-
- ),
- parameters: {
- layout: 'centered',
- },
-} satisfies Meta;
-
-export default meta;
-
-type Story = StoryObj;
-
-/**
- * The default form of the sheet.
- */
-export const Default: Story = {};
diff --git a/apps/storybook/stories/sidebar.stories.tsx b/apps/storybook/stories/sidebar.stories.tsx
deleted file mode 100644
index debdc54..0000000
--- a/apps/storybook/stories/sidebar.stories.tsx
+++ /dev/null
@@ -1,494 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-import {
- AudioWaveform,
- BadgeCheck,
- Bell,
- BookOpen,
- Bot,
- ChevronRight,
- ChevronsUpDown,
- Command,
- CreditCard,
- Folder,
- Forward,
- Frame,
- GalleryVerticalEnd,
- LogOut,
- // biome-ignore lint/suspicious/noShadowRestrictedNames: "icon name"
- Map,
- MoreHorizontal,
- PieChart,
- Plus,
- Settings2,
- Sparkles,
- SquareTerminal,
- Trash2,
-} from 'lucide-react';
-
-import {
- Avatar,
- AvatarFallback,
- AvatarImage,
-} from '@konobangu/design-system/components/ui/avatar';
-import {
- Breadcrumb,
- BreadcrumbItem,
- BreadcrumbLink,
- BreadcrumbList,
- BreadcrumbPage,
- BreadcrumbSeparator,
-} from '@konobangu/design-system/components/ui/breadcrumb';
-import {
- Collapsible,
- CollapsibleContent,
- CollapsibleTrigger,
-} from '@konobangu/design-system/components/ui/collapsible';
-import {
- DropdownMenu,
- DropdownMenuContent,
- DropdownMenuGroup,
- DropdownMenuItem,
- DropdownMenuLabel,
- DropdownMenuSeparator,
- DropdownMenuShortcut,
- DropdownMenuTrigger,
-} from '@konobangu/design-system/components/ui/dropdown-menu';
-import { Separator } from '@konobangu/design-system/components/ui/separator';
-import {
- Sidebar,
- SidebarContent,
- SidebarFooter,
- SidebarGroup,
- SidebarGroupLabel,
- SidebarHeader,
- SidebarInset,
- SidebarMenu,
- SidebarMenuAction,
- SidebarMenuButton,
- SidebarMenuItem,
- SidebarMenuSub,
- SidebarMenuSubButton,
- SidebarMenuSubItem,
- SidebarProvider,
- SidebarRail,
- SidebarTrigger,
-} from '@konobangu/design-system/components/ui/sidebar';
-import { useState } from 'react';
-
-const meta: Meta = {
- title: 'ui/Sidebar',
- component: Sidebar,
- tags: ['autodocs'],
- argTypes: {},
-};
-export default meta;
-
-type Story = StoryObj;
-
-const data = {
- user: {
- name: 'shadcn',
- email: 'm@example.com',
- avatar: '/avatars/shadcn.jpg',
- },
- teams: [
- {
- name: 'Acme Inc',
- logo: GalleryVerticalEnd,
- plan: 'Enterprise',
- },
- {
- name: 'Acme Corp.',
- logo: AudioWaveform,
- plan: 'Startup',
- },
- {
- name: 'Evil Corp.',
- logo: Command,
- plan: 'Free',
- },
- ],
- navMain: [
- {
- title: 'Playground',
- url: '#',
- icon: SquareTerminal,
- isActive: true,
- items: [
- {
- title: 'History',
- url: '#',
- },
- {
- title: 'Starred',
- url: '#',
- },
- {
- title: 'Settings',
- url: '#',
- },
- ],
- },
- {
- title: 'Models',
- url: '#',
- icon: Bot,
- items: [
- {
- title: 'Genesis',
- url: '#',
- },
- {
- title: 'Explorer',
- url: '#',
- },
- {
- title: 'Quantum',
- url: '#',
- },
- ],
- },
- {
- title: 'Documentation',
- url: '#',
- icon: BookOpen,
- items: [
- {
- title: 'Introduction',
- url: '#',
- },
- {
- title: 'Get Started',
- url: '#',
- },
- {
- title: 'Tutorials',
- url: '#',
- },
- {
- title: 'Changelog',
- url: '#',
- },
- ],
- },
- {
- title: 'Settings',
- url: '#',
- icon: Settings2,
- items: [
- {
- title: 'General',
- url: '#',
- },
- {
- title: 'Team',
- url: '#',
- },
- {
- title: 'Billing',
- url: '#',
- },
- {
- title: 'Limits',
- url: '#',
- },
- ],
- },
- ],
- projects: [
- {
- name: 'Design Engineering',
- url: '#',
- icon: Frame,
- },
- {
- name: 'Sales & Marketing',
- url: '#',
- icon: PieChart,
- },
- {
- name: 'Travel',
- url: '#',
- icon: Map,
- },
- ],
-};
-
-export const Base: Story = {
- render: () => {
- const [activeTeam, setActiveTeam] = useState(data.teams[0]);
-
- return (
-
-
-
-
-
-
-
-
-
-
-
- {activeTeam.name}
-
-
- {activeTeam.plan}
-
-
-
-
-
-
-
- Teams
-
- {data.teams.map((team, index) => (
- setActiveTeam(team)}
- className="gap-2 p-2"
- >
-
-
-
- {team.name}
-
- ⌘{index + 1}
-
-
- ))}
-
-
-
-
- Add team
-
-
-
-
-
-
-
-
-
- Platform
-
- {data.navMain.map((item) => (
-
-
-
-
- {item.icon && }
- {item.title}
-
-
-
-
-
- {item.items?.map((subItem) => (
-
-
-
- {subItem.title}
-
-
-
- ))}
-
-
-
-
- ))}
-
-
-
- Projects
-
- {data.projects.map((item) => (
-
-
-
-
- {item.name}
-
-
-
-
-
-
- More
-
-
-
-
-
- View Project
-
-
-
- Share Project
-
-
-
-
- Delete Project
-
-
-
-
- ))}
-
-
-
- More
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- CN
-
-
-
-
- {data.user.name}
-
-
- {data.user.email}
-
-
-
-
-
-
-
-
-
-
-
- CN
-
-
-
-
- {data.user.name}
-
-
- {data.user.email}
-
-
-
-
-
-
-
-
- Upgrade to Pro
-
-
-
-
-
-
- Account
-
-
-
- Billing
-
-
-
- Notifications
-
-
-
-
-
- Log out
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
- },
- args: {},
-};
diff --git a/apps/storybook/stories/skeleton.stories.tsx b/apps/storybook/stories/skeleton.stories.tsx
deleted file mode 100644
index a3cbd6d..0000000
--- a/apps/storybook/stories/skeleton.stories.tsx
+++ /dev/null
@@ -1,35 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import { Skeleton } from '@konobangu/design-system/components/ui/skeleton';
-
-/**
- * Use to show a placeholder while content is loading.
- */
-const meta = {
- title: 'ui/Skeleton',
- component: Skeleton,
- tags: ['autodocs'],
- argTypes: {},
- parameters: {
- layout: 'centered',
- },
-} satisfies Meta;
-
-export default meta;
-
-type Story = StoryObj;
-
-/**
- * The default form of the skeleton.
- */
-export const Default: Story = {
- render: (args) => (
-
- ),
-};
diff --git a/apps/storybook/stories/slider.stories.tsx b/apps/storybook/stories/slider.stories.tsx
deleted file mode 100644
index e6aac4d..0000000
--- a/apps/storybook/stories/slider.stories.tsx
+++ /dev/null
@@ -1,45 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import { Slider } from '@konobangu/design-system/components/ui/slider';
-
-/**
- * An input where the user selects a value from within a given range.
- */
-const meta = {
- title: 'ui/Slider',
- component: Slider,
- tags: ['autodocs'],
- argTypes: {},
- args: {
- defaultValue: [33],
- max: 100,
- step: 1,
- },
-} satisfies Meta;
-
-export default meta;
-
-type Story = StoryObj;
-
-/**
- * The default form of the slider.
- */
-export const Default: Story = {};
-
-/**
- * Use the `inverted` prop to have the slider fill from right to left.
- */
-export const Inverted: Story = {
- args: {
- inverted: true,
- },
-};
-
-/**
- * Use the `disabled` prop to disable the slider.
- */
-export const Disabled: Story = {
- args: {
- disabled: true,
- },
-};
diff --git a/apps/storybook/stories/sonner.stories.tsx b/apps/storybook/stories/sonner.stories.tsx
deleted file mode 100644
index befc40d..0000000
--- a/apps/storybook/stories/sonner.stories.tsx
+++ /dev/null
@@ -1,50 +0,0 @@
-import { action } from '@storybook/addon-actions';
-import type { Meta, StoryObj } from '@storybook/react';
-import { toast } from 'sonner';
-
-import { Toaster } from '@konobangu/design-system/components/ui/sonner';
-
-/**
- * An opinionated toast component for React.
- */
-const meta: Meta = {
- title: 'ui/Sonner',
- component: Toaster,
- tags: ['autodocs'],
- argTypes: {},
- args: {
- position: 'bottom-right',
- },
- parameters: {
- layout: 'fullscreen',
- },
-} satisfies Meta;
-
-export default meta;
-
-type Story = StoryObj;
-
-/**
- * The default form of the toaster.
- */
-export const Default: Story = {
- render: (args) => (
-
-
- toast('Event has been created', {
- description: new Date().toLocaleString(),
- action: {
- label: 'Undo',
- onClick: action('Undo clicked'),
- },
- })
- }
- >
- Show Toast
-
-
-
- ),
-};
diff --git a/apps/storybook/stories/switch.stories.tsx b/apps/storybook/stories/switch.stories.tsx
deleted file mode 100644
index 3a25b89..0000000
--- a/apps/storybook/stories/switch.stories.tsx
+++ /dev/null
@@ -1,47 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import { Switch } from '@konobangu/design-system/components/ui/switch';
-
-/**
- * A control that allows the user to toggle between checked and not checked.
- */
-const meta = {
- title: 'ui/Switch',
- component: Switch,
- tags: ['autodocs'],
- argTypes: {},
- parameters: {
- layout: 'centered',
- },
- render: (args) => (
-
-
-
- Airplane Mode
-
-
- ),
-} satisfies Meta;
-
-export default meta;
-
-type Story = StoryObj;
-
-/**
- * The default form of the switch.
- */
-export const Default: Story = {
- args: {
- id: 'default-switch',
- },
-};
-
-/**
- * Use the `disabled` prop to disable the switch.
- */
-export const Disabled: Story = {
- args: {
- id: 'disabled-switch',
- disabled: true,
- },
-};
diff --git a/apps/storybook/stories/table.stories.tsx b/apps/storybook/stories/table.stories.tsx
deleted file mode 100644
index 1bf2cb7..0000000
--- a/apps/storybook/stories/table.stories.tsx
+++ /dev/null
@@ -1,80 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import {
- Table,
- TableBody,
- TableCaption,
- TableCell,
- TableHead,
- TableHeader,
- TableRow,
-} from '@konobangu/design-system/components/ui/table';
-
-const invoices = [
- {
- invoice: 'INV001',
- paymentStatus: 'Paid',
- totalAmount: '$250.00',
- paymentMethod: 'Credit Card',
- },
- {
- invoice: 'INV002',
- paymentStatus: 'Pending',
- totalAmount: '$150.00',
- paymentMethod: 'PayPal',
- },
- {
- invoice: 'INV003',
- paymentStatus: 'Unpaid',
- totalAmount: '$350.00',
- paymentMethod: 'Bank Transfer',
- },
- {
- invoice: 'INV004',
- paymentStatus: 'Paid',
- totalAmount: '$450.00',
- paymentMethod: 'Credit Card',
- },
-];
-
-/**
- * Powerful table and datagrids built using TanStack Table.
- */
-const meta = {
- title: 'ui/Table',
- component: Table,
- tags: ['autodocs'],
- argTypes: {},
- render: (args) => (
-
- A list of your recent invoices.
-
-
- Invoice
- Status
- Method
- Amount
-
-
-
- {invoices.map((invoice) => (
-
- {invoice.invoice}
- {invoice.paymentStatus}
- {invoice.paymentMethod}
- {invoice.totalAmount}
-
- ))}
-
-
- ),
-} satisfies Meta;
-
-export default meta;
-
-type Story = StoryObj;
-
-/**
- * The default form of the table.
- */
-export const Default: Story = {};
diff --git a/apps/storybook/stories/tabs.stories.tsx b/apps/storybook/stories/tabs.stories.tsx
deleted file mode 100644
index 2359528..0000000
--- a/apps/storybook/stories/tabs.stories.tsx
+++ /dev/null
@@ -1,47 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import {
- Tabs,
- TabsContent,
- TabsList,
- TabsTrigger,
-} from '@konobangu/design-system/components/ui/tabs';
-
-/**
- * A set of layered sections of content—known as tab panels—that are displayed
- * one at a time.
- */
-const meta = {
- title: 'ui/Tabs',
- component: Tabs,
- tags: ['autodocs'],
- argTypes: {},
- args: {
- defaultValue: 'account',
- className: 'w-96',
- },
- render: (args) => (
-
-
- Account
- Password
-
-
- Make changes to your account here.
-
- Change your password here.
-
- ),
- parameters: {
- layout: 'centered',
- },
-} satisfies Meta;
-
-export default meta;
-
-type Story = StoryObj;
-
-/**
- * The default form of the tabs.
- */
-export const Default: Story = {};
diff --git a/apps/storybook/stories/textarea.stories.tsx b/apps/storybook/stories/textarea.stories.tsx
deleted file mode 100644
index 49f5387..0000000
--- a/apps/storybook/stories/textarea.stories.tsx
+++ /dev/null
@@ -1,82 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import { Textarea } from '@konobangu/design-system/components/ui/textarea';
-
-/**
- * Displays a form textarea or a component that looks like a textarea.
- */
-const meta = {
- title: 'ui/Textarea',
- component: Textarea,
- tags: ['autodocs'],
- argTypes: {},
- args: {
- placeholder: 'Type your message here.',
- disabled: false,
- },
-} satisfies Meta;
-
-export default meta;
-
-type Story = StoryObj;
-
-/**
- * The default form of the textarea.
- */
-export const Default: Story = {};
-
-/**
- * Use the `disabled` prop to disable the textarea.
- */
-export const Disabled: Story = {
- args: {
- disabled: true,
- },
-};
-
-/**
- * Use the `Label` component to includes a clear, descriptive label above or
- * alongside the text area to guide users.
- */
-export const WithLabel: Story = {
- render: (args) => (
-
- Your message
-
-
- ),
-};
-
-/**
- * Use a text element below the text area to provide additional instructions
- * or information to users.
- */
-export const WithText: Story = {
- render: (args) => (
-
-
Your Message
-
-
- Your message will be copied to the support team.
-
-
- ),
-};
-
-/**
- * Use the `Button` component to indicate that the text area can be submitted
- * or used to trigger an action.
- */
-export const WithButton: Story = {
- render: (args) => (
-
-
-
- Send Message
-
-
- ),
-};
diff --git a/apps/storybook/stories/toast.stories.tsx b/apps/storybook/stories/toast.stories.tsx
deleted file mode 100644
index db8704b..0000000
--- a/apps/storybook/stories/toast.stories.tsx
+++ /dev/null
@@ -1,94 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import {
- Toast,
- ToastAction,
- type ToastActionElement,
- type ToastProps,
-} from '@konobangu/design-system/components/ui/toast';
-import { Toaster } from '@konobangu/design-system/components/ui/toaster';
-import { useToast } from '@konobangu/design-system/hooks/use-toast';
-
-/**
- * A succinct message that is displayed temporarily.
- */
-const meta = {
- title: 'ui/Toast',
- component: Toast,
- tags: ['autodocs'],
- argTypes: {},
- parameters: {
- layout: 'centered',
- },
- render: (args) => {
- const { toast } = useToast();
- return (
-
- {
- toast(args);
- }}
- >
- Show Toast
-
-
-
- );
- },
-} satisfies Meta;
-
-export default meta;
-
-type Story = Omit, 'args'> & {
- args: Omit;
-};
-
-type ToasterToast = ToastProps & {
- id: string;
- title?: string;
- description?: string;
- action?: ToastActionElement;
-};
-
-/**
- * The default form of the toast.
- */
-export const Default: Story = {
- args: {
- description: 'Your message has been sent.',
- },
-};
-
-/**
- * Use the `title` prop to provide a title for the toast.
- */
-export const WithTitle: Story = {
- args: {
- title: 'Uh oh! Something went wrong.',
- description: 'There was a problem with your request.',
- },
-};
-
-/**
- * Use the `action` prop to provide an action for the toast.
- */
-export const WithAction: Story = {
- args: {
- title: 'Uh oh! Something went wrong.',
- description: 'There was a problem with your request.',
- action: Try again ,
- },
-};
-
-/**
- * Use the `destructive` variant to indicate a destructive action.
- */
-export const Destructive: Story = {
- args: {
- variant: 'destructive',
- title: 'Uh oh! Something went wrong.',
- description: 'There was a problem with your request.',
- action: Try again ,
- },
-};
diff --git a/apps/storybook/stories/toggle-group.stories.tsx b/apps/storybook/stories/toggle-group.stories.tsx
deleted file mode 100644
index 8d54df1..0000000
--- a/apps/storybook/stories/toggle-group.stories.tsx
+++ /dev/null
@@ -1,102 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-import { Bold, Italic, Underline } from 'lucide-react';
-
-import {
- ToggleGroup,
- ToggleGroupItem,
-} from '@konobangu/design-system/components/ui/toggle-group';
-
-/**
- * A set of two-state buttons that can be toggled on or off.
- */
-const meta = {
- title: 'ui/ToggleGroup',
- component: ToggleGroup,
- tags: ['autodocs'],
- argTypes: {
- type: {
- options: ['multiple', 'single'],
- control: { type: 'radio' },
- },
- },
- args: {
- variant: 'default',
- size: 'default',
- type: 'multiple',
- disabled: false,
- },
- render: (args) => (
-
-
-
-
-
-
-
-
-
-
-
- ),
- parameters: {
- layout: 'centered',
- },
-} satisfies Meta;
-
-export default meta;
-
-type Story = StoryObj;
-
-/**
- * The default form of the toggle group.
- */
-export const Default: Story = {};
-
-/**
- * Use the `outline` variant to emphasizing the individuality of each button
- * while keeping them visually cohesive.
- */
-export const Outline: Story = {
- args: {
- variant: 'outline',
- },
-};
-
-/**
- * Use the `single` type to create exclusive selection within the button
- * group, allowing only one button to be active at a time.
- */
-export const Single: Story = {
- args: {
- type: 'single',
- },
-};
-
-/**
- * Use the `sm` size for a compact version of the button group, featuring
- * smaller buttons for spaces with limited real estate.
- */
-export const Small: Story = {
- args: {
- size: 'sm',
- },
-};
-
-/**
- * Use the `lg` size for a more prominent version of the button group, featuring
- * larger buttons for emphasis.
- */
-export const Large: Story = {
- args: {
- size: 'lg',
- },
-};
-
-/**
- * Add the `disabled` prop to a button to prevent interactions.
- */
-export const Disabled: Story = {
- args: {
- disabled: true,
- },
-};
diff --git a/apps/storybook/stories/toggle.stories.tsx b/apps/storybook/stories/toggle.stories.tsx
deleted file mode 100644
index 9f581e4..0000000
--- a/apps/storybook/stories/toggle.stories.tsx
+++ /dev/null
@@ -1,87 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-import { Bold, Italic } from 'lucide-react';
-
-import { Toggle } from '@konobangu/design-system/components/ui/toggle';
-
-/**
- * A two-state button that can be either on or off.
- */
-const meta: Meta = {
- title: 'ui/Toggle',
- component: Toggle,
- tags: ['autodocs'],
- argTypes: {
- children: {
- control: { disable: true },
- },
- },
- args: {
- children: ,
- 'aria-label': 'Toggle bold',
- },
- parameters: {
- layout: 'centered',
- },
-};
-export default meta;
-
-type Story = StoryObj;
-
-/**
- * The default form of the toggle.
- */
-export const Default: Story = {};
-
-/**
- * Use the `outline` variant for a distinct outline, emphasizing the boundary
- * of the selection circle for clearer visibility
- */
-export const Outline: Story = {
- args: {
- variant: 'outline',
- children: ,
- 'aria-label': 'Toggle italic',
- },
-};
-
-/**
- * Use the text element to add a label to the toggle.
- */
-export const WithText: Story = {
- render: (args) => (
-
-
- Italic
-
- ),
- args: { ...Outline.args },
-};
-
-/**
- * Use the `sm` size for a smaller toggle, suitable for interfaces needing
- * compact elements without sacrificing usability.
- */
-export const Small: Story = {
- args: {
- size: 'sm',
- },
-};
-
-/**
- * Use the `lg` size for a larger toggle, offering better visibility and
- * easier interaction for users.
- */
-export const Large: Story = {
- args: {
- size: 'lg',
- },
-};
-
-/**
- * Add the `disabled` prop to prevent interactions with the toggle.
- */
-export const Disabled: Story = {
- args: {
- disabled: true,
- },
-};
diff --git a/apps/storybook/stories/tooltip.stories.tsx b/apps/storybook/stories/tooltip.stories.tsx
deleted file mode 100644
index 5f67002..0000000
--- a/apps/storybook/stories/tooltip.stories.tsx
+++ /dev/null
@@ -1,84 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-import { Plus } from 'lucide-react';
-
-import {
- Tooltip,
- TooltipContent,
- TooltipProvider,
- TooltipTrigger,
-} from '@konobangu/design-system/components/ui/tooltip';
-
-/**
- * A popup that displays information related to an element when the element
- * receives keyboard focus or the mouse hovers over it.
- */
-const meta: Meta = {
- title: 'ui/Tooltip',
- component: TooltipContent,
- tags: ['autodocs'],
- argTypes: {
- side: {
- options: ['top', 'bottom', 'left', 'right'],
- control: {
- type: 'radio',
- },
- },
- children: {
- control: 'text',
- },
- },
- args: {
- side: 'top',
- children: 'Add to library',
- },
- parameters: {
- layout: 'centered',
- },
- render: (args) => (
-
-
-
-
- Add
-
-
-
-
- ),
-} satisfies Meta;
-
-export default meta;
-
-type Story = StoryObj;
-
-/**
- * The default form of the tooltip.
- */
-export const Default: Story = {};
-
-/**
- * Use the `bottom` side to display the tooltip below the element.
- */
-export const Bottom: Story = {
- args: {
- side: 'bottom',
- },
-};
-
-/**
- * Use the `left` side to display the tooltip to the left of the element.
- */
-export const Left: Story = {
- args: {
- side: 'left',
- },
-};
-
-/**
- * Use the `right` side to display the tooltip to the right of the element.
- */
-export const Right: Story = {
- args: {
- side: 'right',
- },
-};
diff --git a/apps/storybook/tailwind.config.ts b/apps/storybook/tailwind.config.ts
deleted file mode 100644
index 9f53cee..0000000
--- a/apps/storybook/tailwind.config.ts
+++ /dev/null
@@ -1 +0,0 @@
-export { config as default } from '@konobangu/tailwind-config/config';
diff --git a/apps/storybook/tsconfig.json b/apps/storybook/tsconfig.json
deleted file mode 100644
index d0d79bd..0000000
--- a/apps/storybook/tsconfig.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "extends": "@konobangu/typescript-config/nextjs.json",
- "compilerOptions": {
- "baseUrl": "."
- },
- "include": [
- "next-env.d.ts",
- "next.config.ts",
- "**/*.ts",
- "**/*.tsx",
- ".next/types/**/*.ts"
- ]
-}
diff --git a/apps/web/.env.development b/apps/web/.env.development
deleted file mode 100644
index a1fee8a..0000000
--- a/apps/web/.env.development
+++ /dev/null
@@ -1,15 +0,0 @@
-# Server
-BETTER_AUTH_SECRET="konobangu"
-DATABASE_URL="postgres://konobangu:konobangu@127.0.0.1:5432/konobangu"
-BETTERSTACK_API_KEY=""
-BETTERSTACK_URL=""
-FLAGS_SECRET=""
-ARCJET_KEY=""
-SVIX_TOKEN=""
-LIVEBLOCKS_SECRET=""
-
-# Client
-NEXT_PUBLIC_APP_URL="http://localhost:5000"
-NEXT_PUBLIC_WEB_URL="http://localhost:5001"
-NEXT_PUBLIC_DOCS_URL="http://localhost:5004"
-NEXT_PUBLIC_VERCEL_PROJECT_PRODUCTION_URL="https://konobangu.com"
\ No newline at end of file
diff --git a/apps/web/.env.example b/apps/web/.env.example
deleted file mode 100644
index ffce2a3..0000000
--- a/apps/web/.env.example
+++ /dev/null
@@ -1,15 +0,0 @@
-# Server
-BETTER_AUTH_SECRET=""
-DATABASE_URL=""
-BETTERSTACK_API_KEY=""
-BETTERSTACK_URL=""
-FLAGS_SECRET=""
-ARCJET_KEY=""
-SVIX_TOKEN=""
-LIVEBLOCKS_SECRET=""
-
-# Client
-NEXT_PUBLIC_APP_URL="http://localhost:5000"
-NEXT_PUBLIC_WEB_URL="http://localhost:5001"
-NEXT_PUBLIC_DOCS_URL="http://localhost:5004"
-NEXT_PUBLIC_VERCEL_PROJECT_PRODUCTION_URL="https://konobangu.com"
\ No newline at end of file
diff --git a/apps/web/.gitignore b/apps/web/.gitignore
deleted file mode 100644
index 5cf5d45..0000000
--- a/apps/web/.gitignore
+++ /dev/null
@@ -1,48 +0,0 @@
-# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
-
-# dependencies
-/node_modules
-/.pnp
-.pnp.js
-
-# testing
-/coverage
-
-# next.js
-/.next/
-/out/
-
-# production
-/build
-
-# misc
-.DS_Store
-*.pem
-
-# debug
-npm-debug.log*
-yarn-debug.log*
-yarn-error.log*
-.pnpm-debug.log*
-
-# local env files
-.env*.local
-
-# vercel
-.vercel
-
-# typescript
-*.tsbuildinfo
-next-env.d.ts
-
-# prisma
-.env
-
-# react.email
-.react-email
-
-# Sentry
-.sentryclirc
-
-# content-collections
-.content-collections
\ No newline at end of file
diff --git a/apps/web/.well-known/vercel/flags/route.ts b/apps/web/.well-known/vercel/flags/route.ts
deleted file mode 100644
index 955eaed..0000000
--- a/apps/web/.well-known/vercel/flags/route.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-import { getFlags } from '@konobangu/feature-flags/access';
-
-export const GET = getFlags;
diff --git a/apps/web/app/(home)/components/cases.tsx b/apps/web/app/(home)/components/cases.tsx
deleted file mode 100644
index 2053add..0000000
--- a/apps/web/app/(home)/components/cases.tsx
+++ /dev/null
@@ -1,53 +0,0 @@
-'use client';
-
-import {
- Carousel,
- type CarouselApi,
- CarouselContent,
- CarouselItem,
-} from '@konobangu/design-system/components/ui/carousel';
-import { useEffect, useState } from 'react';
-
-export const Cases = () => {
- const [api, setApi] = useState();
- const [current, setCurrent] = useState(0);
-
- useEffect(() => {
- if (!api) {
- return;
- }
-
- setTimeout(() => {
- if (api.selectedScrollSnap() + 1 === api.scrollSnapList().length) {
- setCurrent(0);
- api.scrollTo(0);
- } else {
- api.scrollNext();
- setCurrent(current + 1);
- }
- }, 1000);
- }, [api, current]);
-
- return (
-
-
-
-
- Trusted by thousands of businesses worldwide
-
-
-
- {Array.from({ length: 15 }).map((_, index) => (
-
-
- Logo {index + 1}
-
-
- ))}
-
-
-
-
-
- );
-};
diff --git a/apps/web/app/(home)/components/cta.tsx b/apps/web/app/(home)/components/cta.tsx
deleted file mode 100644
index 20106a6..0000000
--- a/apps/web/app/(home)/components/cta.tsx
+++ /dev/null
@@ -1,35 +0,0 @@
-import { Button } from '@konobangu/design-system/components/ui/button';
-import { env } from '@konobangu/env';
-import { MoveRight, PhoneCall } from 'lucide-react';
-import Link from 'next/link';
-
-export const CTA = () => (
-
-
-
-
-
- Try our platform today!
-
-
- Managing a small business today is already tough. Avoid further
- complications by ditching outdated, tedious trade methods. Our goal
- is to streamline SMB trade, making it easier and faster than ever.
-
-
-
-
-
- Jump on a call
-
-
-
-
- Sign up here
-
-
-
-
-
-
-);
diff --git a/apps/web/app/(home)/components/faq.tsx b/apps/web/app/(home)/components/faq.tsx
deleted file mode 100644
index f07c5f6..0000000
--- a/apps/web/app/(home)/components/faq.tsx
+++ /dev/null
@@ -1,55 +0,0 @@
-import {
- Accordion,
- AccordionContent,
- AccordionItem,
- AccordionTrigger,
-} from '@konobangu/design-system/components/ui/accordion';
-import { Button } from '@konobangu/design-system/components/ui/button';
-import { PhoneCall } from 'lucide-react';
-import Link from 'next/link';
-
-export const FAQ = () => (
-
-
-
-
-
-
-
- This is the start of something new
-
-
- Managing a small business today is already tough. Avoid further
- complications by ditching outdated, tedious trade methods. Our
- goal is to streamline SMB trade, making it easier and faster
- than ever.
-
-
-
-
-
- Any questions? Reach out
-
-
-
-
-
-
- {Array.from({ length: 8 }).map((_, index) => (
-
-
- This is the start of something new
-
-
- Managing a small business today is already tough. Avoid further
- complications by ditching outdated, tedious trade methods. Our
- goal is to streamline SMB trade, making it easier and faster
- than ever.
-
-
- ))}
-
-
-
-
-);
diff --git a/apps/web/app/(home)/components/features.tsx b/apps/web/app/(home)/components/features.tsx
deleted file mode 100644
index 2b3dd55..0000000
--- a/apps/web/app/(home)/components/features.tsx
+++ /dev/null
@@ -1,63 +0,0 @@
-import { User } from 'lucide-react';
-
-export const Features = () => (
-
-
-
-
-
-
- Something new!
-
-
- Managing a small business today is already tough.
-
-
-
-
-
-
-
-
Pay supplier invoices
-
- Our goal is to streamline SMB trade, making it easier and faster
- than ever.
-
-
-
-
-
-
-
Pay supplier invoices
-
- Our goal is to streamline SMB trade, making it easier and faster
- than ever.
-
-
-
-
-
-
-
-
Pay supplier invoices
-
- Our goal is to streamline SMB trade, making it easier and faster
- than ever.
-
-
-
-
-
-
-
Pay supplier invoices
-
- Our goal is to streamline SMB trade, making it easier and faster
- than ever.
-
-
-
-
-
-
-
-);
diff --git a/apps/web/app/(home)/components/hero.tsx b/apps/web/app/(home)/components/hero.tsx
deleted file mode 100644
index 709aa85..0000000
--- a/apps/web/app/(home)/components/hero.tsx
+++ /dev/null
@@ -1,63 +0,0 @@
-import { blog } from '@konobangu/cms';
-// import { Feed } from '@konobangu/cms/components/feed';
-import { Button } from '@konobangu/design-system/components/ui/button';
-import { env } from '@konobangu/env';
-import { MoveRight, PhoneCall } from 'lucide-react';
-import { draftMode } from 'next/headers';
-import Link from 'next/link';
-
-export const Hero = async () => {
- const draft = await draftMode();
-
- return (
-
-
-
-
- {/*
- {async ([data]) => {
- 'use server';
-
- return (
-
-
- Read our latest article
-
-
- );
- }}
- */}
-
-
-
- This is the start of something new
-
-
- Managing a small business today is already tough. Avoid further
- complications by ditching outdated, tedious trade methods. Our
- goal is to streamline SMB trade, making it easier and faster than
- ever.
-
-
-
-
-
- Get in touch
-
-
-
-
- Sign up
-
-
-
-
-
-
- );
-};
diff --git a/apps/web/app/(home)/components/stats.tsx b/apps/web/app/(home)/components/stats.tsx
deleted file mode 100644
index f548ab4..0000000
--- a/apps/web/app/(home)/components/stats.tsx
+++ /dev/null
@@ -1,75 +0,0 @@
-import { MoveDownLeft, MoveUpRight } from 'lucide-react';
-
-export const Stats = () => (
-
-
-
-
-
-
- This is the start of something new
-
-
- Managing a small business today is already tough. Avoid further
- complications by ditching outdated, tedious trade methods. Our
- goal is to streamline SMB trade, making it easier and faster than
- ever.
-
-
-
-
-
-
-
-
- 500.000
-
- +20.1%
-
-
-
- Monthly active users
-
-
-
-
-
- 20.105
-
- -2%
-
-
-
- Daily active users
-
-
-
-
-
- $523.520
-
- +8%
-
-
-
- Monthly recurring revenue
-
-
-
-
-
- $1052
-
- +2%
-
-
-
- Cost per acquisition
-
-
-
-
-
-
-
-);
diff --git a/apps/web/app/(home)/components/testimonials.tsx b/apps/web/app/(home)/components/testimonials.tsx
deleted file mode 100644
index 0da5855..0000000
--- a/apps/web/app/(home)/components/testimonials.tsx
+++ /dev/null
@@ -1,78 +0,0 @@
-'use client';
-
-import {
- Avatar,
- AvatarFallback,
- AvatarImage,
-} from '@konobangu/design-system/components/ui/avatar';
-import {
- Carousel,
- type CarouselApi,
- CarouselContent,
- CarouselItem,
-} from '@konobangu/design-system/components/ui/carousel';
-import { User } from 'lucide-react';
-import { useEffect, useState } from 'react';
-
-export const Testimonials = () => {
- const [api, setApi] = useState();
- const [current, setCurrent] = useState(0);
-
- useEffect(() => {
- if (!api) {
- return;
- }
-
- setTimeout(() => {
- if (api.selectedScrollSnap() + 1 === api.scrollSnapList().length) {
- setCurrent(0);
- api.scrollTo(0);
- } else {
- api.scrollNext();
- setCurrent(current + 1);
- }
- }, 4000);
- }, [api, current]);
-
- return (
-
-
-
-
- Trusted by thousands of businesses worldwide
-
-
-
- {Array.from({ length: 15 }).map((_, index) => (
-
-
-
-
-
-
- Best decision
-
-
- Our goal was to streamline SMB trade, making it easier
- and faster than ever and we did it together.
-
-
-
- By
-
-
- CN
-
- John Johnsen
-
-
-
-
- ))}
-
-
-
-
-
- );
-};
diff --git a/apps/web/app/(home)/page.tsx b/apps/web/app/(home)/page.tsx
deleted file mode 100644
index 4a66ff1..0000000
--- a/apps/web/app/(home)/page.tsx
+++ /dev/null
@@ -1,41 +0,0 @@
-import { showBetaFeature } from '@konobangu/feature-flags';
-import { createMetadata } from '@konobangu/seo/metadata';
-import type { Metadata } from 'next';
-import { Cases } from './components/cases';
-import { CTA } from './components/cta';
-import { FAQ } from './components/faq';
-import { Features } from './components/features';
-import { Hero } from './components/hero';
-import { Stats } from './components/stats';
-import { Testimonials } from './components/testimonials';
-
-const meta = {
- title: 'From zero to production in minutes.',
- description:
- "next-forge is a production-grade boilerplate for modern Next.js apps. It's designed to have everything you need to build your new SaaS app as quick as possible. Authentication, billing, analytics, SEO, and more. It's all here.",
-};
-
-export const metadata: Metadata = createMetadata(meta);
-
-const Home = async () => {
- const betaFeature = await showBetaFeature();
-
- return (
- <>
- {betaFeature && (
-
- Beta feature now available
-
- )}
-
-
-
-
-
-
-
- >
- );
-};
-
-export default Home;
diff --git a/apps/web/app/apple-icon.png b/apps/web/app/apple-icon.png
deleted file mode 100644
index d185929..0000000
Binary files a/apps/web/app/apple-icon.png and /dev/null differ
diff --git a/apps/web/app/blog/[slug]/page.tsx b/apps/web/app/blog/[slug]/page.tsx
deleted file mode 100644
index 35d605c..0000000
--- a/apps/web/app/blog/[slug]/page.tsx
+++ /dev/null
@@ -1,132 +0,0 @@
-import { Sidebar } from '@/components/sidebar';
-import { blog } from '@konobangu/cms';
-import { Body } from '@konobangu/cms/components/body';
-import { Feed } from '@konobangu/cms/components/feed';
-import { Image } from '@konobangu/cms/components/image';
-import { TableOfContents } from '@konobangu/cms/components/toc';
-import { env } from '@konobangu/env';
-import { JsonLd } from '@konobangu/seo/json-ld';
-import { createMetadata } from '@konobangu/seo/metadata';
-import { ArrowLeftIcon } from '@radix-ui/react-icons';
-import type { Metadata } from 'next';
-import { draftMode } from 'next/headers';
-import Link from 'next/link';
-import { notFound } from 'next/navigation';
-import Balancer from 'react-wrap-balancer';
-
-type BlogPostProperties = {
- readonly params: Promise<{
- slug: string;
- }>;
-};
-
-export const generateMetadata = async ({
- params,
-}: BlogPostProperties): Promise => {
- const { slug } = await params;
- const post = await blog.getPost(slug);
-
- if (!post) {
- return {};
- }
-
- return createMetadata({
- title: post._title,
- description: post.description,
- image: post.image.url,
- });
-};
-
-export const generateStaticParams = async (): Promise<{ slug: string }[]> => {
- const posts = await blog.getPosts();
-
- return posts.map(({ _slug }) => ({ slug: _slug }));
-};
-
-const BlogPost = async ({ params }: BlogPostProperties) => {
- const { slug } = await params;
- const draft = await draftMode();
-
- return (
-
- {/* biome-ignore lint/suspicious/useAwait: "Server Actions must be async" */}
- {async ([data]) => {
- 'use server';
-
- const [page] = data.blog.posts.items;
-
- if (!page) {
- notFound();
- }
-
- return (
- <>
-
-
-
-
- Back to Blog
-
-
-
-
-
- {page._title}
-
-
- {page.description}
-
- {page.image ? (
-
- ) : undefined}
-
-
-
-
-
-
- }
- readingTime={`${page.body.readingTime} min read`}
- date={new Date(page.date)}
- />
-
-
-
- >
- );
- }}
-
- );
-};
-
-export default BlogPost;
diff --git a/apps/web/app/blog/layout.tsx b/apps/web/app/blog/layout.tsx
deleted file mode 100644
index 2543659..0000000
--- a/apps/web/app/blog/layout.tsx
+++ /dev/null
@@ -1,15 +0,0 @@
-import { Toolbar } from '@konobangu/cms/components/toolbar';
-import type { ReactNode } from 'react';
-
-type BlogLayoutProps = {
- children: ReactNode;
-};
-
-const BlogLayout = ({ children }: BlogLayoutProps) => (
- <>
- {children}
-
- >
-);
-
-export default BlogLayout;
diff --git a/apps/web/app/blog/page.tsx b/apps/web/app/blog/page.tsx
deleted file mode 100644
index 3e496c0..0000000
--- a/apps/web/app/blog/page.tsx
+++ /dev/null
@@ -1,87 +0,0 @@
-// import { blog } from '@konobangu/cms';
-// import { Feed } from '@konobangu/cms/components/feed';
-import { Image } from '@konobangu/cms/components/image';
-import { cn } from '@konobangu/design-system/lib/utils';
-import type { Blog, WithContext } from '@konobangu/seo/json-ld';
-import { JsonLd } from '@konobangu/seo/json-ld';
-import { createMetadata } from '@konobangu/seo/metadata';
-import type { Metadata } from 'next';
-import { draftMode } from 'next/headers';
-import Link from 'next/link';
-
-const title = 'Blog';
-const description = 'Thoughts, ideas, and opinions.';
-
-export const metadata: Metadata = createMetadata({ title, description });
-
-const BlogIndex = async () => {
- const draft = await draftMode();
-
- const jsonLd: WithContext = {
- '@type': 'Blog',
- '@context': 'https://schema.org',
- };
-
- return (
- <>
-
-
-
-
-
- Latest articles
-
-
-
- {/*
- {async ([data]) => {
- 'use server';
-
- if (!data.blog.posts.items.length) {
- return null;
- }
-
- return data.blog.posts.items.map((post, index) => (
-
-
-
-
- {new Date(post.date).toLocaleDateString('en-US', {
- month: 'long',
- day: 'numeric',
- year: 'numeric',
- })}
-
-
-
-
- {post._title}
-
-
- {post.description}
-
-
-
- ));
- }}
- */}
-
-
-
- >
- );
-};
-
-export default BlogIndex;
diff --git a/apps/web/app/components/footer.tsx b/apps/web/app/components/footer.tsx
deleted file mode 100644
index 94f2644..0000000
--- a/apps/web/app/components/footer.tsx
+++ /dev/null
@@ -1,118 +0,0 @@
-import { env } from '@konobangu/env';
-import { Status } from '@konobangu/observability/status';
-import Link from 'next/link';
-
-export const Footer = () => {
- const navigationItems = [
- {
- title: 'Home',
- href: '/',
- description: '',
- },
- {
- title: 'Pages',
- description: 'Managing a small business today is already tough.',
- items: [
- {
- title: 'Blog',
- href: '/blog',
- },
- ],
- },
- {
- title: 'Legal',
- description: 'We stay on top of the latest legal requirements.',
- items: [
- {
- title: 'Terms of Service',
- href: '/legal/terms',
- },
- {
- title: 'Privacy Policy',
- href: '/legal/privacy',
- },
- {
- title: 'Acceptable Use',
- href: '/legal/acceptable-use',
- },
- ],
- },
- ];
-
- if (env.NEXT_PUBLIC_DOCS_URL) {
- navigationItems.at(1)?.items?.push({
- title: 'Docs',
- href: env.NEXT_PUBLIC_DOCS_URL,
- });
- }
-
- return (
-
-
-
-
-
-
-
- next-forge
-
-
- This is the start of something new.
-
-
-
-
-
- {navigationItems.map((item) => (
-
-
- {item.href ? (
-
-
{item.title}
-
- ) : (
-
{item.title}
- )}
- {item.items?.map((subItem) => (
-
-
- {subItem.title}
-
-
- ))}
-
-
- ))}
-
-
-
-
-
- );
-};
diff --git a/apps/web/app/components/header/index.tsx b/apps/web/app/components/header/index.tsx
deleted file mode 100644
index 386cde1..0000000
--- a/apps/web/app/components/header/index.tsx
+++ /dev/null
@@ -1,195 +0,0 @@
-'use client';
-
-import { ModeToggle } from '@konobangu/design-system/components/mode-toggle';
-import { Button } from '@konobangu/design-system/components/ui/button';
-import {
- NavigationMenu,
- NavigationMenuContent,
- NavigationMenuItem,
- NavigationMenuLink,
- NavigationMenuList,
- NavigationMenuTrigger,
-} from '@konobangu/design-system/components/ui/navigation-menu';
-import { env } from '@konobangu/env';
-import { Menu, MoveRight, X } from 'lucide-react';
-import Link from 'next/link';
-import { useState } from 'react';
-
-import Image from 'next/image';
-import Logo from './logo.svg';
-
-export const Header = () => {
- const navigationItems = [
- {
- title: 'Home',
- href: '/',
- description: '',
- },
- {
- title: 'Product',
- description: 'Managing a small business today is already tough.',
- items: [
- {
- title: 'Pricing',
- href: '/pricing',
- },
- {
- title: 'Pricing',
- href: '/pricing',
- },
- {
- title: 'Pricing',
- href: '/pricing',
- },
- {
- title: 'Pricing',
- href: '/pricing',
- },
- ],
- },
- {
- title: 'Blog',
- href: '/blog',
- description: '',
- },
- ];
-
- if (env.NEXT_PUBLIC_DOCS_URL) {
- navigationItems.push({
- title: 'Docs',
- href: env.NEXT_PUBLIC_DOCS_URL,
- description: '',
- });
- }
-
- const [isOpen, setOpen] = useState(false);
- return (
-
-
-
-
-
- {navigationItems.map((item) => (
-
- {item.href ? (
- <>
-
-
- {item.title}
-
-
- >
- ) : (
- <>
-
- {item.title}
-
-
-
-
-
-
{item.title}
-
- {item.description}
-
-
-
- Book a call today
-
-
-
- {item.items?.map((subItem, idx) => (
-
- {subItem.title}
-
-
- ))}
-
-
-
- >
- )}
-
- ))}
-
-
-
-
-
-
- Contact us
-
-
-
-
-
-
- Sign in
-
-
- Get started
-
-
-
-
setOpen(!isOpen)}>
- {isOpen ? : }
-
- {isOpen && (
-
- {navigationItems.map((item) => (
-
-
- {item.href ? (
-
-
{item.title}
-
-
- ) : (
-
{item.title}
- )}
- {item.items?.map((subItem) => (
-
-
- {subItem.title}
-
-
-
- ))}
-
-
- ))}
-
- )}
-
-
-
- );
-};
diff --git a/apps/web/app/components/header/logo.svg b/apps/web/app/components/header/logo.svg
deleted file mode 100644
index 26dd076..0000000
--- a/apps/web/app/components/header/logo.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/apps/web/app/contact/actions/contact.tsx b/apps/web/app/contact/actions/contact.tsx
deleted file mode 100644
index b1c5cdd..0000000
--- a/apps/web/app/contact/actions/contact.tsx
+++ /dev/null
@@ -1,48 +0,0 @@
-'use server';
-
-import { konosend } from '@konobangu/email';
-import { ContactTemplate } from '@konobangu/email/templates/contact';
-import { env } from '@konobangu/env';
-import { parseError } from '@konobangu/observability/error';
-import { createRateLimiter, slidingWindow } from '@konobangu/rate-limit';
-import { headers } from 'next/headers';
-
-export const contact = async (
- name: string,
- email: string,
- message: string
-): Promise<{
- error?: string;
-}> => {
- try {
- if (env.UPSTASH_REDIS_REST_URL && env.UPSTASH_REDIS_REST_TOKEN) {
- const rateLimiter = createRateLimiter({
- limiter: slidingWindow(1, '1d'),
- });
- const head = await headers();
- const ip = head.get('x-forwarded-for');
-
- const { success } = await rateLimiter.limit(`contact_form_${ip}`);
-
- if (!success) {
- throw new Error(
- 'You have reached your request limit. Please try again later.'
- );
- }
- }
-
- await konosend.emails.send({
- from: env.RESEND_FROM,
- to: env.RESEND_FROM,
- subject: 'Contact form submission',
- replyTo: email,
- react: ,
- });
-
- return {};
- } catch (error) {
- const errorMessage = parseError(error);
-
- return { error: errorMessage };
- }
-};
diff --git a/apps/web/app/contact/components/contact-form.tsx b/apps/web/app/contact/components/contact-form.tsx
deleted file mode 100644
index f450ab2..0000000
--- a/apps/web/app/contact/components/contact-form.tsx
+++ /dev/null
@@ -1,116 +0,0 @@
-'use client';
-
-import { Button } from '@konobangu/design-system/components/ui/button';
-import { Calendar } from '@konobangu/design-system/components/ui/calendar';
-import { Input } from '@konobangu/design-system/components/ui/input';
-import { Label } from '@konobangu/design-system/components/ui/label';
-import {
- Popover,
- PopoverContent,
- PopoverTrigger,
-} from '@konobangu/design-system/components/ui/popover';
-import { cn } from '@konobangu/design-system/lib/utils';
-import { format } from 'date-fns';
-import { CalendarIcon, Check, MoveRight } from 'lucide-react';
-import { useState } from 'react';
-
-export const ContactForm = () => {
- const [date, setDate] = useState(new Date());
-
- return (
-
-
-
-
-
-
-
- Something new
-
-
- Managing a small business today is already tough. Avoid
- further complications by ditching outdated, tedious trade
- methods.
-
-
-
-
-
-
-
Easy to use
-
- We've made it easy to use and understand.
-
-
-
-
-
-
-
Fast and reliable
-
- We've made it easy to use and understand.
-
-
-
-
-
-
-
Beautiful and modern
-
- We've made it easy to use and understand.
-
-
-
-
-
-
-
-
Book a meeting
-
-
Date
-
-
-
-
- {date ? format(date, 'PPP') : Pick a date }
-
-
-
-
-
-
-
-
- First name
-
-
-
- Last name
-
-
-
- Upload resume
-
-
-
-
- Book the meeting
-
-
-
-
-
-
- );
-};
diff --git a/apps/web/app/contact/page.tsx b/apps/web/app/contact/page.tsx
deleted file mode 100644
index 82d2a0e..0000000
--- a/apps/web/app/contact/page.tsx
+++ /dev/null
@@ -1,16 +0,0 @@
-import { createMetadata } from '@konobangu/seo/metadata';
-import type { Metadata } from 'next';
-import { ContactForm } from './components/contact-form';
-
-const title = 'Contact';
-const description =
- "Let us know what's on your mind. We'll get back to you as soon as possible.";
-
-export const metadata: Metadata = createMetadata({
- title,
- description,
-});
-
-const Contact = () => ;
-
-export default Contact;
diff --git a/apps/web/app/global-error.tsx b/apps/web/app/global-error.tsx
deleted file mode 100644
index 6e0511e..0000000
--- a/apps/web/app/global-error.tsx
+++ /dev/null
@@ -1,29 +0,0 @@
-'use client';
-
-import { Button } from '@konobangu/design-system/components/ui/button';
-import { fonts } from '@konobangu/design-system/lib/fonts';
-import { captureException } from '@sentry/nextjs';
-import type NextError from 'next/error';
-import { useEffect } from 'react';
-
-type GlobalErrorProperties = {
- readonly error: NextError & { digest?: string };
- readonly reset: () => void;
-};
-
-const GlobalError = ({ error, reset }: GlobalErrorProperties) => {
- useEffect(() => {
- captureException(error);
- }, [error]);
-
- return (
-
-
- Oops, something went wrong
- reset()}>Try again
-
-
- );
-};
-
-export default GlobalError;
diff --git a/apps/web/app/icon.png b/apps/web/app/icon.png
deleted file mode 100644
index d798285..0000000
Binary files a/apps/web/app/icon.png and /dev/null differ
diff --git a/apps/web/app/layout.tsx b/apps/web/app/layout.tsx
deleted file mode 100644
index f2863ed..0000000
--- a/apps/web/app/layout.tsx
+++ /dev/null
@@ -1,30 +0,0 @@
-import '@konobangu/design-system/styles/globals.css';
-import './styles/web.css';
-import { DesignSystemProvider } from '@konobangu/design-system';
-import { fonts } from '@konobangu/design-system/lib/fonts';
-import { cn } from '@konobangu/design-system/lib/utils';
-import type { ReactNode } from 'react';
-import { Footer } from './components/footer';
-import { Header } from './components/header';
-
-type RootLayoutProperties = {
- readonly children: ReactNode;
-};
-
-const RootLayout = ({ children }: RootLayoutProperties) => (
-
-
-
-
- {children}
-
-
-
-
-);
-
-export default RootLayout;
diff --git a/apps/web/app/legal/[slug]/page.tsx b/apps/web/app/legal/[slug]/page.tsx
deleted file mode 100644
index 130b1e9..0000000
--- a/apps/web/app/legal/[slug]/page.tsx
+++ /dev/null
@@ -1,98 +0,0 @@
-import { Sidebar } from '@/components/sidebar';
-import { legal } from '@konobangu/cms';
-import { Body } from '@konobangu/cms/components/body';
-// import { Feed } from '@konobangu/cms/components/feed';
-import { TableOfContents } from '@konobangu/cms/components/toc';
-import { createMetadata } from '@konobangu/seo/metadata';
-import { ArrowLeftIcon } from '@radix-ui/react-icons';
-import type { Metadata } from 'next';
-import { draftMode } from 'next/headers';
-import Link from 'next/link';
-import { notFound } from 'next/navigation';
-import Balancer from 'react-wrap-balancer';
-
-type LegalPageProperties = {
- readonly params: Promise<{
- slug: string;
- }>;
-};
-
-export const generateMetadata = async ({
- params,
-}: LegalPageProperties): Promise => {
- const { slug } = await params;
- const post = await legal.getPost(slug);
-
- if (!post) {
- return {};
- }
-
- return createMetadata({
- title: post._title,
- description: post.description,
- });
-};
-
-export const generateStaticParams = async (): Promise<{ slug: string }[]> => {
- const posts = await legal.getPosts();
-
- return posts.map(({ _slug }) => ({ slug: _slug }));
-};
-
-const LegalPage = async ({ params }: LegalPageProperties) => {
- const { slug } = await params;
- const draft = await draftMode();
-
- return (
- <>>
- )
-
- // return (
- //
- // {/* biome-ignore lint/suspicious/useAwait: "Server Actions must be async" */}
- // {async ([data]) => {
- // 'use server';
-
- // const [page] = data.legalPages.items;
-
- // if (!page) {
- // notFound();
- // }
-
- // return (
- //
- //
- //
- // Back to Blog
- //
- //
- // {page._title}
- //
- //
- // {page.description}
- //
- //
- //
- // }
- // readingTime={`${page.body.readingTime} min read`}
- // date={new Date()}
- // />
- //
- //
- //
- // );
- // }}
- //
- // );
-};
-
-export default LegalPage;
diff --git a/apps/web/app/legal/layout.tsx b/apps/web/app/legal/layout.tsx
deleted file mode 100644
index e8d6530..0000000
--- a/apps/web/app/legal/layout.tsx
+++ /dev/null
@@ -1,15 +0,0 @@
-import { Toolbar } from '@konobangu/cms/components/toolbar';
-import type { ReactNode } from 'react';
-
-type LegalLayoutProps = {
- children: ReactNode;
-};
-
-const LegalLayout = ({ children }: LegalLayoutProps) => (
- <>
- {children}
-
- >
-);
-
-export default LegalLayout;
diff --git a/apps/web/app/opengraph-image.png b/apps/web/app/opengraph-image.png
deleted file mode 100644
index c79169f..0000000
Binary files a/apps/web/app/opengraph-image.png and /dev/null differ
diff --git a/apps/web/app/pricing/page.tsx b/apps/web/app/pricing/page.tsx
deleted file mode 100644
index a6151d7..0000000
--- a/apps/web/app/pricing/page.tsx
+++ /dev/null
@@ -1,156 +0,0 @@
-import { Button } from '@konobangu/design-system/components/ui/button';
-import { env } from '@konobangu/env';
-import { Check, Minus, MoveRight, PhoneCall } from 'lucide-react';
-import Link from 'next/link';
-
-const Pricing = () => (
-
-
-
-
-
- Prices that make sense!
-
-
- Managing a small business today is already tough.
-
-
-
-
-
-
Startup
-
- Our goal is to streamline SMB trade, making it easier and faster
- than ever for everyone and everywhere.
-
-
- $40
- / month
-
-
-
- Try it
-
-
-
-
-
Growth
-
- Our goal is to streamline SMB trade, making it easier and faster
- than ever for everyone and everywhere.
-
-
- $40
- / month
-
-
-
- Try it
-
-
-
-
-
Enterprise
-
- Our goal is to streamline SMB trade, making it easier and faster
- than ever for everyone and everywhere.
-
-
- $40
- / month
-
-
-
- Contact us
-
-
-
-
- Features
-
-
-
-
- {/* New Line */}
-
SSO
-
-
-
-
-
-
-
-
-
- {/* New Line */}
-
- AI Assistant
-
-
-
-
-
-
-
-
-
-
- {/* New Line */}
-
- Version Control
-
-
-
-
-
-
-
-
-
-
- {/* New Line */}
-
- Members
-
-
-
-
- {/* New Line */}
-
- Multiplayer Mode
-
-
-
-
-
-
-
-
-
-
- {/* New Line */}
-
- Orchestration
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-);
-
-export default Pricing;
diff --git a/apps/web/app/robots.ts b/apps/web/app/robots.ts
deleted file mode 100644
index 86877f8..0000000
--- a/apps/web/app/robots.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { env } from '@konobangu/env';
-import type { MetadataRoute } from 'next';
-
-export default function robots(): MetadataRoute.Robots {
- return {
- rules: {
- userAgent: '*',
- allow: '/',
- },
- sitemap: new URL(
- '/sitemap.xml',
- env.NEXT_PUBLIC_VERCEL_PROJECT_PRODUCTION_URL
- ).href,
- };
-}
diff --git a/apps/web/app/sitemap.ts b/apps/web/app/sitemap.ts
deleted file mode 100644
index 9ed06e8..0000000
--- a/apps/web/app/sitemap.ts
+++ /dev/null
@@ -1,49 +0,0 @@
-import fs from 'node:fs';
-import { env } from '@konobangu/env';
-import type { MetadataRoute } from 'next';
-
-const appFolders = fs.readdirSync('app', { withFileTypes: true });
-const pages = appFolders
- .filter((file) => file.isDirectory())
- .filter((folder) => !folder.name.startsWith('_'))
- .filter((folder) => !folder.name.startsWith('('))
- .map((folder) => folder.name);
-
- const blogs = fs
- .readdirSync('content/blog', { withFileTypes: true })
- .filter((file) => !file.isDirectory())
- .filter((file) => !file.name.startsWith('_'))
- .filter((file) => !file.name.startsWith('('))
- .map((file) => file.name.replace('.mdx', ''));
-
-const legals = fs
- .readdirSync('content/legal', { withFileTypes: true })
- .filter((file) => !file.isDirectory())
- .filter((file) => !file.name.startsWith('_'))
- .filter((file) => !file.name.startsWith('('))
- .map((file) => file.name.replace('.mdx', ''));
-
-const sitemap = async (): Promise => [
- {
- url: env.NEXT_PUBLIC_VERCEL_PROJECT_PRODUCTION_URL,
- lastModified: new Date(),
- },
- ...pages.map((page) => ({
- url: new URL(page, env.NEXT_PUBLIC_VERCEL_PROJECT_PRODUCTION_URL).href,
- lastModified: new Date(),
- })),
- ...blogs.map((blog) => ({
- url: new URL(`blog/${blog}`, env.NEXT_PUBLIC_VERCEL_PROJECT_PRODUCTION_URL)
- .href,
- lastModified: new Date(),
- })),
- ...legals.map((legal) => ({
- url: new URL(
- `legal/${legal}`,
- env.NEXT_PUBLIC_VERCEL_PROJECT_PRODUCTION_URL
- ).href,
- lastModified: new Date(),
- })),
-];
-
-export default sitemap;
diff --git a/apps/web/app/styles/web.css b/apps/web/app/styles/web.css
deleted file mode 100644
index 51f7ff6..0000000
--- a/apps/web/app/styles/web.css
+++ /dev/null
@@ -1,36 +0,0 @@
-.shiki {
- background-color: var(--shiki-light-bg);
- color: var(--shiki-light);
- @apply border-border;
-}
-
-.shiki span {
- color: var(--shiki-light);
-}
-
-.dark .shiki {
- background-color: var(--shiki-dark-bg);
- color: var(--shiki-dark);
-}
-
-.dark .shiki span {
- color: var(--shiki-dark);
-}
-
-.shiki code {
- display: grid;
- font-size: 13px;
- counter-reset: line;
-}
-
-.shiki .line:before {
- content: counter(line);
- counter-increment: line;
-
- @apply inline-block w-4 mr-8 text-muted-foreground text-right;
-}
-
-.shiki[title]:before {
- content: attr(title);
- @apply inline-block text-muted-foreground text-right mb-6 text-sm;
-}
diff --git a/apps/web/components/sidebar.tsx b/apps/web/components/sidebar.tsx
deleted file mode 100644
index f13b314..0000000
--- a/apps/web/components/sidebar.tsx
+++ /dev/null
@@ -1,50 +0,0 @@
-import { capitalize } from '@konobangu/design-system/lib/utils';
-import type { ReactNode } from 'react';
-
-type SidebarProperties = {
- readonly date: Date;
- readonly readingTime: string;
- readonly tags?: string[];
- readonly toc?: ReactNode;
-};
-
-export const Sidebar = async ({
- date,
- readingTime,
- tags,
- toc: Toc,
-}: SidebarProperties) => (
-
-
-
Published
-
- {new Intl.DateTimeFormat('en-US', {
- month: 'short',
- day: 'numeric',
- year: 'numeric',
- timeZone: 'America/New_York',
- }).format(date)}
-
-
-
-
Reading Time
-
{readingTime}
-
- {tags && (
-
-
Tags
-
- {tags.map(capitalize).join(', ')}
-
-
- )}
- {Toc ? (
-
- ) : undefined}
-
-);
diff --git a/apps/web/content-collections.ts b/apps/web/content-collections.ts
deleted file mode 100644
index f2626ee..0000000
--- a/apps/web/content-collections.ts
+++ /dev/null
@@ -1 +0,0 @@
-export { default } from '@konobangu/cms/collections';
\ No newline at end of file
diff --git a/apps/web/content/blog/my-first-blog.mdx b/apps/web/content/blog/my-first-blog.mdx
deleted file mode 100644
index ab5b583..0000000
--- a/apps/web/content/blog/my-first-blog.mdx
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: 'My First Post'
-description: 'This is my first blog post'
-date: 2024-10-23
-image: /blog/my-first-post.png
----
\ No newline at end of file
diff --git a/apps/web/instrumentation.ts b/apps/web/instrumentation.ts
deleted file mode 100644
index 99d8478..0000000
--- a/apps/web/instrumentation.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-import { initializeSentry } from '@konobangu/next-config/instrumentation';
-
-export const register = initializeSentry();
diff --git a/apps/web/middleware.ts b/apps/web/middleware.ts
deleted file mode 100644
index 53e550c..0000000
--- a/apps/web/middleware.ts
+++ /dev/null
@@ -1,47 +0,0 @@
-import { authMiddleware } from '@konobangu/auth/middleware';
-import { env } from '@konobangu/env';
-import { parseError } from '@konobangu/observability/error';
-import { secure } from '@konobangu/security';
-import {
- noseconeConfig,
- noseconeMiddleware,
-} from '@konobangu/security/middleware';
-import { type NextRequest, NextResponse } from 'next/server';
-
-export const config = {
- // matcher tells Next.js which routes to run the middleware on. This runs the
- // middleware on all routes except for static assets and Posthog ingest
- matcher: ['/((?!_next/static|_next/image|ingest|favicon.ico).*)'],
-};
-
-const securityHeaders = noseconeMiddleware(noseconeConfig);
-
-export const middleware = async (request: NextRequest) => {
- const beforeMiddleware = async (request: NextRequest) => {
- if (!env.ARCJET_KEY) {
- return securityHeaders();
- }
-
- try {
- await secure(
- [
- // See https://docs.arcjet.com/bot-protection/identifying-bots
- 'CATEGORY:SEARCH_ENGINE', // Allow search engines
- 'CATEGORY:PREVIEW', // Allow preview links to show OG images
- 'CATEGORY:MONITOR', // Allow uptime monitoring services
- ],
- request
- );
-
- return securityHeaders();
- } catch (error) {
- const message = parseError(error);
-
- return NextResponse.json({ error: message }, { status: 403 });
- }
- };
-
- const response = await beforeMiddleware(request);
-
- return authMiddleware(response as any);
-};
diff --git a/apps/web/next.config.ts b/apps/web/next.config.ts
deleted file mode 100644
index 7cd5d6a..0000000
--- a/apps/web/next.config.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-import { withCMS } from '@konobangu/cms/next-config';
-import { env } from '@konobangu/env';
-import { config, withAnalyzer, withSentry } from '@konobangu/next-config';
-import type { NextConfig } from 'next';
-
-let nextConfig: NextConfig = { ...config };
-
-nextConfig.images?.remotePatterns?.push({
- protocol: 'https',
- hostname: 'assets.basehub.com',
-});
-
-if (process.env.NODE_ENV === 'production') {
- const redirects: NextConfig['redirects'] = async () => [
- {
- source: '/legal',
- destination: '/legal/privacy',
- statusCode: 301,
- },
- ];
-
- nextConfig.redirects = redirects;
-}
-
-if (env.VERCEL) {
- nextConfig = withSentry(nextConfig);
-}
-
-if (env.ANALYZE === 'true') {
- nextConfig = withAnalyzer(nextConfig);
-}
-
-export default withCMS(nextConfig);
diff --git a/apps/web/package.json b/apps/web/package.json
deleted file mode 100644
index 45a5e01..0000000
--- a/apps/web/package.json
+++ /dev/null
@@ -1,54 +0,0 @@
-{
- "name": "web",
- "private": true,
- "scripts": {
- "dev": "next dev -p 5001 --turbopack",
- "build": "next build",
- "start": "next start",
- "analyze": "ANALYZE=true pnpm build",
- "clean": "git clean -xdf .cache .turbo dist node_modules",
- "typecheck": "tsc --noEmit --emitDeclarationOnly false"
- },
- "dependencies": {
- "@arcjet/next": "1.0.0-alpha.34",
- "@content-collections/core": "^0.8.0",
- "@content-collections/mdx": "^0.2.0",
- "@content-collections/next": "^0.2.4",
- "@konobangu/cms": "workspace:*",
- "@konobangu/design-system": "workspace:*",
- "@konobangu/email": "workspace:*",
- "@konobangu/env": "workspace:*",
- "@konobangu/feature-flags": "workspace:*",
- "@konobangu/next-config": "workspace:*",
- "@konobangu/observability": "workspace:*",
- "@konobangu/rate-limit": "workspace:*",
- "@konobangu/seo": "workspace:*",
- "@konobangu/tailwind-config": "workspace:*",
- "@radix-ui/react-icons": "^1.3.2",
- "@sentry/nextjs": "^8.48.0",
- "date-fns": "^4.1.0",
- "fumadocs-core": "^14.7.4",
- "import-in-the-middle": "^1.12.0",
- "lucide-react": "^0.468.0",
- "mdx-bundler": "^10.0.3",
- "react": "^19.0.0",
- "react-dom": "^19.0.0",
- "react-wrap-balancer": "^1.1.1",
- "reading-time": "^1.5.0",
- "require-in-the-middle": "^7.4.0",
- "sharp": "^0.33.5",
- "shiki": "^1.27.0",
- "sqip": "1.0.0-alpha.51",
- "sqip-plugin-data-uri": "1.0.0-alpha.52",
- "sqip-plugin-primitive": "1.0.0-alpha.53",
- "sqip-plugin-svgo": "1.0.0-alpha.54"
- },
- "devDependencies": {
- "@konobangu/typescript-config": "workspace:*",
- "@types/node": "22.10.1",
- "@types/react": "19.0.1",
- "@types/react-dom": "19.0.2",
- "tailwindcss": "^3.4.17",
- "typescript": "^5.7.3"
- }
-}
diff --git a/apps/web/postcss.config.mjs b/apps/web/postcss.config.mjs
deleted file mode 100644
index d799a05..0000000
--- a/apps/web/postcss.config.mjs
+++ /dev/null
@@ -1 +0,0 @@
-export { default } from '@konobangu/design-system/postcss.config.mjs';
diff --git a/apps/web/tailwind.config.ts b/apps/web/tailwind.config.ts
deleted file mode 100644
index 9f53cee..0000000
--- a/apps/web/tailwind.config.ts
+++ /dev/null
@@ -1 +0,0 @@
-export { config as default } from '@konobangu/tailwind-config/config';
diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json
deleted file mode 100644
index 2b51198..0000000
--- a/apps/web/tsconfig.json
+++ /dev/null
@@ -1,20 +0,0 @@
-{
- "extends": [
- "@konobangu/typescript-config/nextjs.json",
- "@konobangu/cms/typescript-config.json"
- ],
- "compilerOptions": {
- "baseUrl": ".",
- "paths": {
- "@/*": ["./*"],
- "@konobangu/*": ["../../packages/*"]
- }
- },
- "include": [
- "next-env.d.ts",
- "next.config.ts",
- "**/*.ts",
- "**/*.tsx",
- ".next/types/**/*.ts"
- ]
-}
diff --git a/apps/web/vercel.json b/apps/web/vercel.json
deleted file mode 100644
index f6218dd..0000000
--- a/apps/web/vercel.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "crons": []
-}
diff --git a/apps/webui/.gitignore b/apps/webui/.gitignore
new file mode 100644
index 0000000..6781085
--- /dev/null
+++ b/apps/webui/.gitignore
@@ -0,0 +1,15 @@
+# Local
+.DS_Store
+*.local
+*.log*
+
+# Dist
+node_modules
+dist/
+
+# IDE
+.vscode/*
+!.vscode/extensions.json
+.idea
+.env.local
+.env.*.lcoal
diff --git a/apps/webui/README.md b/apps/webui/README.md
new file mode 100644
index 0000000..5fc0306
--- /dev/null
+++ b/apps/webui/README.md
@@ -0,0 +1,29 @@
+# Rsbuild project
+
+## Setup
+
+Install the dependencies:
+
+```bash
+pnpm install
+```
+
+## Get started
+
+Start the dev server:
+
+```bash
+pnpm dev
+```
+
+Build the app for production:
+
+```bash
+pnpm build
+```
+
+Preview the production build locally:
+
+```bash
+pnpm preview
+```
diff --git a/apps/webui/index.html b/apps/webui/index.html
new file mode 100644
index 0000000..596c6c0
--- /dev/null
+++ b/apps/webui/index.html
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/webui/package.json b/apps/webui/package.json
new file mode 100644
index 0000000..9c41598
--- /dev/null
+++ b/apps/webui/package.json
@@ -0,0 +1,31 @@
+{
+ "name": "webui",
+ "version": "1.0.0",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "build": "rsbuild build",
+ "dev": "rsbuild dev",
+ "preview": "rsbuild preview"
+ },
+ "dependencies": {
+ "@ark-ui/solid": "^4.10.2",
+ "@corvu/drawer": "^0.2.3",
+ "@corvu/otp-field": "^0.1.4",
+ "@corvu/resizable": "^0.2.4",
+ "@kobalte/core": "^0.13.9",
+ "@kobalte/tailwindcss": "^0.9.0",
+ "@solid-primitives/refs": "^1.1.0",
+ "@tanstack/solid-router": "^1.112.2",
+ "chart.js": "^4.4.8",
+ "cmdk-solid": "^1.1.2",
+ "embla-carousel-solid": "^8.5.2",
+ "solid-js": "^1.9.5",
+ "solid-sonner": "^0.2.8",
+ "tailwindcss": "^3"
+ },
+ "devDependencies": {
+ "@rsbuild/plugin-solid": "^1.0.5",
+ "@tanstack/react-router": "^1.112.0"
+ }
+}
diff --git a/apps/recorder/postcss.config.js b/apps/webui/postcss.config.mjs
similarity index 95%
rename from apps/recorder/postcss.config.js
rename to apps/webui/postcss.config.mjs
index 021fabe..616868c 100644
--- a/apps/recorder/postcss.config.js
+++ b/apps/webui/postcss.config.mjs
@@ -2,4 +2,4 @@ export default {
plugins: {
tailwindcss: {},
},
-}
+};
diff --git a/packages/database/migrate/migrator.ts b/apps/webui/public/.gitkeep
similarity index 100%
rename from packages/database/migrate/migrator.ts
rename to apps/webui/public/.gitkeep
diff --git a/apps/webui/public/assets/404.html b/apps/webui/public/assets/404.html
new file mode 100644
index 0000000..dbd7df4
--- /dev/null
+++ b/apps/webui/public/assets/404.html
@@ -0,0 +1,7 @@
+
+
+
+ not found :-(
+
+
+
\ No newline at end of file
diff --git a/apps/webui/public/assets/favicon.ico b/apps/webui/public/assets/favicon.ico
new file mode 100644
index 0000000..a314325
Binary files /dev/null and b/apps/webui/public/assets/favicon.ico differ
diff --git a/apps/webui/rsbuild.config.ts b/apps/webui/rsbuild.config.ts
new file mode 100644
index 0000000..0a94869
--- /dev/null
+++ b/apps/webui/rsbuild.config.ts
@@ -0,0 +1,44 @@
+import { defineConfig } from '@rsbuild/core';
+import { pluginBabel } from '@rsbuild/plugin-babel';
+import { pluginSolid } from '@rsbuild/plugin-solid';
+import { TanStackRouterRspack } from '@tanstack/router-plugin/rspack';
+
+export default defineConfig({
+ html: {
+ favicon: './public/assets/favicon.ico',
+ },
+ plugins: [
+ pluginBabel({
+ include: /\.(?:jsx|tsx)$/,
+ }),
+ pluginSolid(),
+ ],
+ tools: {
+ rspack: {
+ plugins: [
+ TanStackRouterRspack({ target: 'solid', autoCodeSplitting: true }),
+ ],
+ },
+ },
+ source: {
+ entry: {
+ index: './src/main.tsx',
+ },
+ define: {
+ 'process.env.AUTH_TYPE': JSON.stringify(process.env.AUTH_TYPE),
+ 'process.env.OIDC_CLIENT_ID': JSON.stringify(process.env.OIDC_CLIENT_ID),
+ 'process.env.OIDC_CLIENT_SECRET': JSON.stringify(
+ process.env.OIDC_CLIENT_SECRET
+ ),
+ 'process.env.OIDC_ISSUER': JSON.stringify(process.env.OIDC_ISSUER),
+ 'process.env.OIDC_AUDIENCE': JSON.stringify(process.env.OIDC_AUDIENCE),
+ 'process.env.OIDC_EXTRA_SCOPES': JSON.stringify(
+ process.env.OIDC_EXTRA_SCOPES
+ ),
+ },
+ },
+ server: {
+ host: '0.0.0.0',
+ port: 5000,
+ },
+});
diff --git a/apps/webui/src/app.css b/apps/webui/src/app.css
new file mode 100644
index 0000000..e1c56e4
--- /dev/null
+++ b/apps/webui/src/app.css
@@ -0,0 +1,114 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+@layer base {
+ :root {
+ --background: 0 0% 100%;
+ --foreground: 240 10% 3.9%;
+
+ --muted: 240 4.8% 95.9%;
+ --muted-foreground: 240 3.8% 46.1%;
+
+ --popover: 0 0% 100%;
+ --popover-foreground: 240 10% 3.9%;
+
+ --border: 240 5.9% 90%;
+ --input: 240 5.9% 90%;
+
+ --card: 0 0% 100%;
+ --card-foreground: 240 10% 3.9%;
+
+ --primary: 240 5.9% 10%;
+ --primary-foreground: 0 0% 98%;
+
+ --secondary: 240 4.8% 95.9%;
+ --secondary-foreground: 240 5.9% 10%;
+
+ --accent: 240 4.8% 95.9%;
+ --accent-foreground: 240 5.9% 10%;
+
+ --destructive: 0 84.2% 60.2%;
+ --destructive-foreground: 0 0% 98%;
+
+ --info: 204 94% 94%;
+ --info-foreground: 199 89% 48%;
+
+ --success: 149 80% 90%;
+ --success-foreground: 160 84% 39%;
+
+ --warning: 48 96% 89%;
+ --warning-foreground: 25 95% 53%;
+
+ --error: 0 93% 94%;
+ --error-foreground: 0 84% 60%;
+
+ --ring: 240 5.9% 10%;
+
+ --radius: 0.5rem;
+ }
+
+ .dark,
+ [data-kb-theme="dark"] {
+ --background: 240 10% 3.9%;
+ --foreground: 0 0% 98%;
+
+ --muted: 240 3.7% 15.9%;
+ --muted-foreground: 240 5% 64.9%;
+
+ --accent: 240 3.7% 15.9%;
+ --accent-foreground: 0 0% 98%;
+
+ --popover: 240 10% 3.9%;
+ --popover-foreground: 0 0% 98%;
+
+ --border: 240 3.7% 15.9%;
+ --input: 240 3.7% 15.9%;
+
+ --card: 240 10% 3.9%;
+ --card-foreground: 0 0% 98%;
+
+ --primary: 0 0% 98%;
+ --primary-foreground: 240 5.9% 10%;
+
+ --secondary: 240 3.7% 15.9%;
+ --secondary-foreground: 0 0% 98%;
+
+ --destructive: 0 62.8% 30.6%;
+ --destructive-foreground: 0 0% 98%;
+
+ --info: 204 94% 94%;
+ --info-foreground: 199 89% 48%;
+
+ --success: 149 80% 90%;
+ --success-foreground: 160 84% 39%;
+
+ --warning: 48 96% 89%;
+ --warning-foreground: 25 95% 53%;
+
+ --error: 0 93% 94%;
+ --error-foreground: 0 84% 60%;
+
+ --ring: 240 4.9% 83.9%;
+
+ --radius: 0.5rem;
+ }
+}
+
+@layer base {
+ * {
+ @apply border-border;
+ }
+ body {
+ @apply bg-background text-foreground;
+ font-feature-settings:
+ "rlig" 1,
+ "calt" 1;
+ }
+}
+
+@media (max-width: 640px) {
+ .container {
+ @apply px-4;
+ }
+}
diff --git a/apps/webui/src/components/ui/accordion.tsx b/apps/webui/src/components/ui/accordion.tsx
new file mode 100644
index 0000000..2c00fad
--- /dev/null
+++ b/apps/webui/src/components/ui/accordion.tsx
@@ -0,0 +1,83 @@
+import type { JSX, ValidComponent } from "solid-js"
+import { splitProps } from "solid-js"
+
+import * as AccordionPrimitive from "@kobalte/core/accordion"
+import type { PolymorphicProps } from "@kobalte/core/polymorphic"
+
+import { cn } from "~/styles/utils"
+
+const Accordion = AccordionPrimitive.Root
+
+type AccordionItemProps =
+ AccordionPrimitive.AccordionItemProps & {
+ class?: string | undefined
+ }
+
+const AccordionItem = (
+ props: PolymorphicProps>
+) => {
+ const [local, others] = splitProps(props as AccordionItemProps, ["class"])
+ return
+}
+
+type AccordionTriggerProps =
+ AccordionPrimitive.AccordionTriggerProps & {
+ class?: string | undefined
+ children?: JSX.Element
+ }
+
+const AccordionTrigger = (
+ props: PolymorphicProps>
+) => {
+ const [local, others] = splitProps(props as AccordionTriggerProps, ["class", "children"])
+ return (
+
+ svg]:rotate-180",
+ local.class
+ )}
+ {...others}
+ >
+ {local.children}
+
+
+
+
+
+ )
+}
+
+type AccordionContentProps =
+ AccordionPrimitive.AccordionContentProps & {
+ class?: string | undefined
+ children?: JSX.Element
+ }
+
+const AccordionContent = (
+ props: PolymorphicProps>
+) => {
+ const [local, others] = splitProps(props as AccordionContentProps, ["class", "children"])
+ return (
+
+ {local.children}
+
+ )
+}
+
+export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }
diff --git a/apps/webui/src/components/ui/alert-dialog.tsx b/apps/webui/src/components/ui/alert-dialog.tsx
new file mode 100644
index 0000000..83e19c6
--- /dev/null
+++ b/apps/webui/src/components/ui/alert-dialog.tsx
@@ -0,0 +1,112 @@
+import type { JSX, ValidComponent } from "solid-js"
+import { splitProps } from "solid-js"
+
+import * as AlertDialogPrimitive from "@kobalte/core/alert-dialog"
+import type { PolymorphicProps } from "@kobalte/core/polymorphic"
+
+import { cn } from "~/styles/utils"
+
+const AlertDialog = AlertDialogPrimitive.Root
+const AlertDialogTrigger = AlertDialogPrimitive.Trigger
+const AlertDialogPortal = AlertDialogPrimitive.Portal
+
+type AlertDialogOverlayProps =
+ AlertDialogPrimitive.AlertDialogOverlayProps & {
+ class?: string | undefined
+ }
+
+const AlertDialogOverlay = (
+ props: PolymorphicProps>
+) => {
+ const [local, others] = splitProps(props as AlertDialogOverlayProps, ["class"])
+ return (
+
+ )
+}
+
+type AlertDialogContentProps =
+ AlertDialogPrimitive.AlertDialogContentProps & {
+ class?: string | undefined
+ children?: JSX.Element
+ }
+
+const AlertDialogContent = (
+ props: PolymorphicProps>
+) => {
+ const [local, others] = splitProps(props as AlertDialogContentProps, ["class", "children"])
+ return (
+
+
+
+ {local.children}
+
+
+
+
+
+ Close
+
+
+
+ )
+}
+
+type AlertDialogTitleProps =
+ AlertDialogPrimitive.AlertDialogTitleProps & {
+ class?: string | undefined
+ }
+
+const AlertDialogTitle = (
+ props: PolymorphicProps>
+) => {
+ const [local, others] = splitProps(props as AlertDialogTitleProps, ["class"])
+ return
+}
+
+type AlertDialogDescriptionProps =
+ AlertDialogPrimitive.AlertDialogDescriptionProps & {
+ class?: string | undefined
+ }
+
+const AlertDialogDescription = (
+ props: PolymorphicProps>
+) => {
+ const [local, others] = splitProps(props as AlertDialogDescriptionProps, ["class"])
+ return (
+
+ )
+}
+
+export {
+ AlertDialog,
+ AlertDialogPortal,
+ AlertDialogOverlay,
+ AlertDialogTrigger,
+ AlertDialogContent,
+ AlertDialogTitle,
+ AlertDialogDescription
+}
diff --git a/apps/webui/src/components/ui/alert.tsx b/apps/webui/src/components/ui/alert.tsx
new file mode 100644
index 0000000..da0f173
--- /dev/null
+++ b/apps/webui/src/components/ui/alert.tsx
@@ -0,0 +1,50 @@
+import type { Component, ComponentProps, ValidComponent } from "solid-js"
+import { splitProps } from "solid-js"
+
+import * as AlertPrimitive from "@kobalte/core/alert"
+import type { PolymorphicProps } from "@kobalte/core/polymorphic"
+import type { VariantProps } from "class-variance-authority"
+import { cva } from "class-variance-authority"
+
+import { cn } from "~/styles/utils"
+
+const alertVariants = cva(
+ "relative w-full rounded-lg border p-4 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground [&>svg~*]:pl-7",
+ {
+ variants: {
+ variant: {
+ default: "bg-background text-foreground",
+ destructive:
+ "border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive"
+ }
+ },
+ defaultVariants: {
+ variant: "default"
+ }
+ }
+)
+
+type AlertRootProps = AlertPrimitive.AlertRootProps &
+ VariantProps & { class?: string | undefined }
+
+const Alert = (props: PolymorphicProps>) => {
+ const [local, others] = splitProps(props as AlertRootProps, ["class", "variant"])
+ return (
+
+ )
+}
+
+const AlertTitle: Component> = (props) => {
+ const [local, others] = splitProps(props, ["class"])
+ return
+}
+
+const AlertDescription: Component> = (props) => {
+ const [local, others] = splitProps(props, ["class"])
+ return
+}
+
+export { Alert, AlertTitle, AlertDescription }
diff --git a/apps/webui/src/components/ui/aspect-ratio.tsx b/apps/webui/src/components/ui/aspect-ratio.tsx
new file mode 100644
index 0000000..f5a8a31
--- /dev/null
+++ b/apps/webui/src/components/ui/aspect-ratio.tsx
@@ -0,0 +1,31 @@
+import type { Component, ComponentProps } from "solid-js"
+import { mergeProps, splitProps } from "solid-js"
+
+type AspectRatioProps = ComponentProps<"div"> & { ratio?: number }
+
+const AspectRatio: Component = (rawProps) => {
+ const props = mergeProps({ ratio: 1 / 1 }, rawProps)
+ const [local, others] = splitProps(props, ["ratio"])
+ return (
+
+ )
+}
+
+export { AspectRatio }
diff --git a/apps/webui/src/components/ui/avatar.tsx b/apps/webui/src/components/ui/avatar.tsx
new file mode 100644
index 0000000..0918339
--- /dev/null
+++ b/apps/webui/src/components/ui/avatar.tsx
@@ -0,0 +1,51 @@
+import type { ValidComponent } from "solid-js"
+import { splitProps } from "solid-js"
+
+import * as ImagePrimitive from "@kobalte/core/image"
+import type { PolymorphicProps } from "@kobalte/core/polymorphic"
+
+import { cn } from "~/styles/utils"
+
+type AvatarRootProps = ImagePrimitive.ImageRootProps & {
+ class?: string | undefined
+}
+
+const Avatar = (
+ props: PolymorphicProps>
+) => {
+ const [local, others] = splitProps(props as AvatarRootProps, ["class"])
+ return (
+
+ )
+}
+
+type AvatarImageProps = ImagePrimitive.ImageImgProps & {
+ class?: string | undefined
+}
+
+const AvatarImage = (
+ props: PolymorphicProps>
+) => {
+ const [local, others] = splitProps(props as AvatarImageProps, ["class"])
+ return
+}
+
+type AvatarFallbackProps =
+ ImagePrimitive.ImageFallbackProps & { class?: string | undefined }
+
+const AvatarFallback = (
+ props: PolymorphicProps>
+) => {
+ const [local, others] = splitProps(props as AvatarFallbackProps, ["class"])
+ return (
+
+ )
+}
+
+export { Avatar, AvatarImage, AvatarFallback }
diff --git a/apps/webui/src/components/ui/badge-delta.tsx b/apps/webui/src/components/ui/badge-delta.tsx
new file mode 100644
index 0000000..fd2d205
--- /dev/null
+++ b/apps/webui/src/components/ui/badge-delta.tsx
@@ -0,0 +1,144 @@
+import type { Component, JSXElement } from "solid-js"
+import { createEffect, on, splitProps } from "solid-js"
+
+import type { VariantProps } from "class-variance-authority"
+import { cva } from "class-variance-authority"
+
+import { cn } from "~/styles/utils"
+import type { BadgeProps } from "~/components/ui/badge"
+import { Badge } from "~/components/ui/badge"
+
+type DeltaType = "increase" | "moderateIncrease" | "unchanged" | "moderateDecrease" | "decrease"
+
+const badgeDeltaVariants = cva("", {
+ variants: {
+ variant: {
+ success: "bg-success text-success-foreground hover:bg-success",
+ warning: "bg-warning text-warning-foreground hover:bg-warning",
+ error: "bg-error text-error-foreground hover:bg-error"
+ }
+ }
+})
+type DeltaVariant = NonNullable["variant"]>
+
+const iconMap: { [key in DeltaType]: (props: { class?: string }) => JSXElement } = {
+ increase: (props) => (
+
+
+
+
+
+ ),
+ moderateIncrease: (props) => (
+
+
+
+
+ ),
+ unchanged: (props) => (
+
+
+
+
+
+ ),
+ moderateDecrease: (props) => (
+
+
+
+
+ ),
+ decrease: (props) => (
+
+
+
+
+
+ )
+}
+
+const variantMap: { [key in DeltaType]: DeltaVariant } = {
+ increase: "success",
+ moderateIncrease: "success",
+ unchanged: "warning",
+ moderateDecrease: "error",
+ decrease: "error"
+}
+
+type BadgeDeltaProps = Omit & {
+ deltaType: DeltaType
+}
+
+const BadgeDelta: Component = (props) => {
+ const [local, others] = splitProps(props, ["class", "children", "deltaType"])
+
+ // eslint-disable-next-line solid/reactivity
+ let Icon = iconMap[local.deltaType]
+ createEffect(
+ on(
+ () => local.deltaType,
+ () => {
+ Icon = iconMap[local.deltaType]
+ }
+ )
+ )
+
+ return (
+
+
+
+ {local.children}
+
+
+ )
+}
+
+export { BadgeDelta }
diff --git a/apps/webui/src/components/ui/badge.tsx b/apps/webui/src/components/ui/badge.tsx
new file mode 100644
index 0000000..041a37c
--- /dev/null
+++ b/apps/webui/src/components/ui/badge.tsx
@@ -0,0 +1,48 @@
+import type { Component, ComponentProps } from "solid-js"
+import { splitProps } from "solid-js"
+
+import type { VariantProps } from "class-variance-authority"
+import { cva } from "class-variance-authority"
+
+import { cn } from "~/styles/utils"
+
+const badgeVariants = cva(
+ "inline-flex items-center rounded-md 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",
+ secondary: "border-transparent bg-secondary text-secondary-foreground",
+ outline: "text-foreground",
+ success: "border-success-foreground bg-success text-success-foreground",
+ warning: "border-warning-foreground bg-warning text-warning-foreground",
+ error: "border-error-foreground bg-error text-error-foreground"
+ }
+ },
+ defaultVariants: {
+ variant: "default"
+ }
+ }
+)
+
+type BadgeProps = ComponentProps<"div"> &
+ VariantProps & {
+ round?: boolean
+ }
+
+const Badge: Component = (props) => {
+ const [local, others] = splitProps(props, ["class", "variant", "round"])
+ return (
+
+ )
+}
+
+export type { BadgeProps }
+export { Badge, badgeVariants }
diff --git a/apps/webui/src/components/ui/bar-list.tsx b/apps/webui/src/components/ui/bar-list.tsx
new file mode 100644
index 0000000..5dff5b2
--- /dev/null
+++ b/apps/webui/src/components/ui/bar-list.tsx
@@ -0,0 +1,96 @@
+import type { ComponentProps, JSX } from "solid-js"
+import { For, mergeProps, Show, splitProps } from "solid-js"
+import { Dynamic } from "solid-js/web"
+
+import { cn } from "~/styles/utils"
+
+type Bar = T & {
+ value: number
+ name: JSX.Element
+ icon?: (props: ComponentProps<"svg">) => JSX.Element
+ href?: string
+ target?: string
+}
+
+type SortOrder = "ascending" | "descending" | "none"
+
+type ValueFormatter = (value: number) => string
+
+const defaultValueFormatter: ValueFormatter = (value: number) => value.toString()
+
+type BarListProps = ComponentProps<"div"> & {
+ data: Bar[]
+ valueFormatter?: ValueFormatter
+ sortOrder?: SortOrder
+}
+
+const BarList = (rawProps: BarListProps) => {
+ const props = mergeProps(
+ {
+ valueFormatter: defaultValueFormatter,
+ sortOrder: "descending" as SortOrder
+ },
+ rawProps
+ )
+ const [local, others] = splitProps(props, ["class", "data", "valueFormatter", "sortOrder"])
+
+ const sortedData = () => {
+ if (local.sortOrder === "none") {
+ return local.data
+ }
+ return local.data.sort((a, b) =>
+ local.sortOrder === "ascending" ? a.value - b.value : b.value - a.value
+ )
+ }
+
+ const widths = () => {
+ const maxValue = Math.max(...sortedData().map((item) => item.value), 0)
+ return sortedData().map((item) =>
+ item.value === 0 ? 0 : Math.max((item.value / maxValue) * 100, 2)
+ )
+ }
+
+ return (
+
+
+ {(bar, idx) => {
+ return (
+
+
+
+
+ {(icon) => }
+
+
{bar.name}}>
+ {(href) => (
+
+ {bar.name}
+
+ )}
+
+
+
+
{local.valueFormatter(bar.value)}
+
+ )
+ }}
+
+
+ )
+}
+
+export { BarList }
diff --git a/apps/webui/src/components/ui/breadcrumb.tsx b/apps/webui/src/components/ui/breadcrumb.tsx
new file mode 100644
index 0000000..4fba21b
--- /dev/null
+++ b/apps/webui/src/components/ui/breadcrumb.tsx
@@ -0,0 +1,111 @@
+import type { Component, ComponentProps, JSX, ValidComponent } from "solid-js"
+import { Show, splitProps } from "solid-js"
+
+import type { PolymorphicProps } from "@kobalte/core"
+import * as BreadcrumbPrimitive from "@kobalte/core/breadcrumbs"
+
+import { cn } from "~/styles/utils"
+
+const Breadcrumb = BreadcrumbPrimitive.Root
+
+const BreadcrumbList: Component> = (props) => {
+ const [local, others] = splitProps(props, ["class"])
+ return (
+
+ )
+}
+
+const BreadcrumbItem: Component> = (props) => {
+ const [local, others] = splitProps(props, ["class"])
+ return
+}
+
+type BreadcrumbLinkProps =
+ BreadcrumbPrimitive.BreadcrumbsLinkProps & { class?: string | undefined }
+
+const BreadcrumbLink = (
+ props: PolymorphicProps>
+) => {
+ const [local, others] = splitProps(props as BreadcrumbLinkProps, ["class"])
+ return (
+
+ )
+}
+
+type BreadcrumbSeparatorProps =
+ BreadcrumbPrimitive.BreadcrumbsSeparatorProps & {
+ class?: string | undefined
+ children?: JSX.Element
+ }
+
+const BreadcrumbSeparator = (
+ props: PolymorphicProps>
+) => {
+ const [local, others] = splitProps(props as BreadcrumbSeparatorProps, ["class", "children"])
+ return (
+ svg]:size-3.5", local.class)} {...others}>
+
+
+
+ }
+ >
+ {local.children}
+
+
+ )
+}
+
+const BreadcrumbEllipsis: Component> = (props) => {
+ const [local, others] = splitProps(props, ["class"])
+ return (
+
+
+
+
+
+
+ More
+
+ )
+}
+
+export {
+ Breadcrumb,
+ BreadcrumbList,
+ BreadcrumbItem,
+ BreadcrumbLink,
+ BreadcrumbSeparator,
+ BreadcrumbEllipsis
+}
diff --git a/apps/webui/src/components/ui/button.tsx b/apps/webui/src/components/ui/button.tsx
new file mode 100644
index 0000000..3e29aa4
--- /dev/null
+++ b/apps/webui/src/components/ui/button.tsx
@@ -0,0 +1,53 @@
+import type { JSX, ValidComponent } from "solid-js"
+import { splitProps } from "solid-js"
+
+import * as ButtonPrimitive from "@kobalte/core/button"
+import type { PolymorphicProps } from "@kobalte/core/polymorphic"
+import type { VariantProps } from "class-variance-authority"
+import { cva } from "class-variance-authority"
+
+import { cn } from "~/styles/utils"
+
+const buttonVariants = cva(
+ "inline-flex items-center justify-center gap-2 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 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
+ {
+ 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 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 px-3 text-xs",
+ lg: "h-11 px-8",
+ icon: "size-10"
+ }
+ },
+ defaultVariants: {
+ variant: "default",
+ size: "default"
+ }
+ }
+)
+
+type ButtonProps = ButtonPrimitive.ButtonRootProps &
+ VariantProps & { class?: string | undefined; children?: JSX.Element }
+
+const Button = (
+ props: PolymorphicProps>
+) => {
+ const [local, others] = splitProps(props as ButtonProps, ["variant", "size", "class"])
+ return (
+
+ )
+}
+
+export type { ButtonProps }
+export { Button, buttonVariants }
diff --git a/apps/webui/src/components/ui/callout.tsx b/apps/webui/src/components/ui/callout.tsx
new file mode 100644
index 0000000..da8575d
--- /dev/null
+++ b/apps/webui/src/components/ui/callout.tsx
@@ -0,0 +1,40 @@
+import type { Component, ComponentProps } from "solid-js"
+import { splitProps } from "solid-js"
+
+import type { VariantProps } from "class-variance-authority"
+import { cva } from "class-variance-authority"
+
+import { cn } from "~/styles/utils"
+
+const calloutVariants = cva("rounded-md border-l-4 p-2 pl-4", {
+ variants: {
+ variant: {
+ default: "border-info-foreground bg-info text-info-foreground",
+ success: "border-success-foreground bg-success text-success-foreground",
+ warning: "border-warning-foreground bg-warning text-warning-foreground",
+ error: "border-error-foreground bg-error text-error-foreground"
+ }
+ },
+ defaultVariants: {
+ variant: "default"
+ }
+})
+
+type CalloutProps = ComponentProps<"div"> & VariantProps
+
+const Callout: Component = (props) => {
+ const [local, others] = splitProps(props, ["class", "variant"])
+ return
+}
+
+const CalloutTitle: Component> = (props) => {
+ const [local, others] = splitProps(props, ["class"])
+ return
+}
+
+const CalloutContent: Component> = (props) => {
+ const [local, others] = splitProps(props, ["class"])
+ return
+}
+
+export { Callout, CalloutTitle, CalloutContent }
diff --git a/apps/webui/src/components/ui/card.tsx b/apps/webui/src/components/ui/card.tsx
new file mode 100644
index 0000000..d62ac75
--- /dev/null
+++ b/apps/webui/src/components/ui/card.tsx
@@ -0,0 +1,43 @@
+import type { Component, ComponentProps } from "solid-js"
+import { splitProps } from "solid-js"
+
+import { cn } from "~/styles/utils"
+
+const Card: Component> = (props) => {
+ const [local, others] = splitProps(props, ["class"])
+ return (
+
+ )
+}
+
+const CardHeader: Component> = (props) => {
+ const [local, others] = splitProps(props, ["class"])
+ return
+}
+
+const CardTitle: Component> = (props) => {
+ const [local, others] = splitProps(props, ["class"])
+ return (
+
+ )
+}
+
+const CardDescription: Component> = (props) => {
+ const [local, others] = splitProps(props, ["class"])
+ return
+}
+
+const CardContent: Component> = (props) => {
+ const [local, others] = splitProps(props, ["class"])
+ return
+}
+
+const CardFooter: Component> = (props) => {
+ const [local, others] = splitProps(props, ["class"])
+ return
+}
+
+export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
diff --git a/apps/webui/src/components/ui/carousel.tsx b/apps/webui/src/components/ui/carousel.tsx
new file mode 100644
index 0000000..22cb7d1
--- /dev/null
+++ b/apps/webui/src/components/ui/carousel.tsx
@@ -0,0 +1,263 @@
+import type { Accessor, Component, ComponentProps, VoidProps } from "solid-js"
+import {
+ createContext,
+ createEffect,
+ createMemo,
+ createSignal,
+ mergeProps,
+ splitProps,
+ useContext
+} from "solid-js"
+
+import type { CreateEmblaCarouselType } from "embla-carousel-solid"
+import createEmblaCarousel from "embla-carousel-solid"
+
+import { cn } from "~/styles/utils"
+import type { ButtonProps } from "~/components/ui/button"
+import { Button } from "~/components/ui/button"
+
+export type CarouselApi = CreateEmblaCarouselType[1]
+
+type UseCarouselParameters = Parameters
+type CarouselOptions = NonNullable
+type CarouselPlugin = NonNullable
+
+type CarouselProps = {
+ opts?: ReturnType
+ plugins?: ReturnType
+ orientation?: "horizontal" | "vertical"
+ setApi?: (api: CarouselApi) => void
+}
+
+type CarouselContextProps = {
+ carouselRef: ReturnType[0]
+ api: ReturnType[1]
+ scrollPrev: () => void
+ scrollNext: () => void
+ canScrollPrev: Accessor
+ canScrollNext: Accessor
+} & CarouselProps
+
+const CarouselContext = createContext | null>(null)
+
+const useCarousel = () => {
+ const context = useContext(CarouselContext)
+
+ if (!context) {
+ throw new Error("useCarousel must be used within a ")
+ }
+
+ return context()
+}
+
+const Carousel: Component> = (rawProps) => {
+ const props = mergeProps<(CarouselProps & ComponentProps<"div">)[]>(
+ { orientation: "horizontal" },
+ rawProps
+ )
+
+ const [local, others] = splitProps(props, [
+ "orientation",
+ "opts",
+ "setApi",
+ "plugins",
+ "class",
+ "children"
+ ])
+
+ const [carouselRef, api] = createEmblaCarousel(
+ () => ({
+ ...local.opts,
+ axis: local.orientation === "horizontal" ? "x" : "y"
+ }),
+ () => (local.plugins === undefined ? [] : local.plugins)
+ )
+ const [canScrollPrev, setCanScrollPrev] = createSignal(false)
+ const [canScrollNext, setCanScrollNext] = createSignal(false)
+
+ const onSelect = (api: NonNullable>) => {
+ setCanScrollPrev(api.canScrollPrev())
+ setCanScrollNext(api.canScrollNext())
+ }
+
+ const scrollPrev = () => {
+ api()?.scrollPrev()
+ }
+
+ const scrollNext = () => {
+ api()?.scrollNext()
+ }
+
+ const handleKeyDown = (event: KeyboardEvent) => {
+ if (event.key === "ArrowLeft") {
+ event.preventDefault()
+ scrollPrev()
+ } else if (event.key === "ArrowRight") {
+ event.preventDefault()
+ scrollNext()
+ }
+ }
+
+ createEffect(() => {
+ if (!api() || !local.setApi) {
+ return
+ }
+ local.setApi(api)
+ })
+
+ createEffect(() => {
+ if (!api()) {
+ return
+ }
+
+ onSelect(api()!)
+ api()!.on("reInit", onSelect)
+ api()!.on("select", onSelect)
+
+ return () => {
+ api()?.off("select", onSelect)
+ }
+ })
+
+ const value = createMemo(
+ () =>
+ ({
+ carouselRef,
+ api,
+ opts: local.opts,
+ orientation: local.orientation || (local.opts?.axis === "y" ? "vertical" : "horizontal"),
+ scrollPrev,
+ scrollNext,
+ canScrollPrev,
+ canScrollNext
+ }) satisfies CarouselContextProps
+ )
+
+ return (
+
+
+ {local.children}
+
+
+ )
+}
+
+const CarouselContent: Component> = (props) => {
+ const [local, others] = splitProps(props, ["class"])
+ const { carouselRef, orientation } = useCarousel()
+
+ return (
+
+ )
+}
+
+const CarouselItem: Component> = (props) => {
+ const [local, others] = splitProps(props, ["class"])
+ const { orientation } = useCarousel()
+
+ return (
+
+ )
+}
+
+type CarouselButtonProps = VoidProps
+
+const CarouselPrevious: Component = (rawProps) => {
+ const props = mergeProps({ variant: "outline", size: "icon" }, rawProps)
+ const [local, others] = splitProps(props, ["class", "variant", "size"])
+ const { orientation, scrollPrev, canScrollPrev } = useCarousel()
+
+ return (
+
+
+
+
+
+
+ Previous slide
+
+ )
+}
+
+const CarouselNext: Component = (rawProps) => {
+ const props = mergeProps({ variant: "outline", size: "icon" }, rawProps)
+ const [local, others] = splitProps(props, ["class", "variant", "size"])
+ const { orientation, scrollNext, canScrollNext } = useCarousel()
+
+ return (
+
+
+
+
+
+
+ Next slide
+
+ )
+}
+
+export { Carousel, CarouselContent, CarouselItem, CarouselPrevious, CarouselNext }
diff --git a/apps/webui/src/components/ui/charts.tsx b/apps/webui/src/components/ui/charts.tsx
new file mode 100644
index 0000000..5abab3a
--- /dev/null
+++ b/apps/webui/src/components/ui/charts.tsx
@@ -0,0 +1,292 @@
+import type { Component } from "solid-js"
+import { createEffect, createSignal, mergeProps, on, onCleanup, onMount } from "solid-js"
+import { unwrap } from "solid-js/store"
+
+import type { Ref } from "@solid-primitives/refs"
+import { mergeRefs } from "@solid-primitives/refs"
+import type {
+ ChartComponent,
+ ChartData,
+ ChartItem,
+ ChartOptions,
+ Plugin as ChartPlugin,
+ ChartType,
+ ChartTypeRegistry,
+ TooltipModel
+} from "chart.js"
+import {
+ ArcElement,
+ BarController,
+ BarElement,
+ BubbleController,
+ CategoryScale,
+ Chart,
+ Colors,
+ DoughnutController,
+ Filler,
+ Legend,
+ LinearScale,
+ LineController,
+ LineElement,
+ PieController,
+ PointElement,
+ PolarAreaController,
+ RadarController,
+ RadialLinearScale,
+ ScatterController,
+ Tooltip
+} from "chart.js"
+
+type TypedChartProps = {
+ data: ChartData
+ options?: ChartOptions
+ plugins?: ChartPlugin[]
+ ref?: Ref
+ width?: number | undefined
+ height?: number | undefined
+}
+
+type ChartProps = TypedChartProps & {
+ type: ChartType
+}
+
+type ChartContext = {
+ chart: Chart
+ tooltip: TooltipModel
+}
+
+const BaseChart: Component = (rawProps) => {
+ const [canvasRef, setCanvasRef] = createSignal()
+ const [chart, setChart] = createSignal()
+
+ const props = mergeProps(
+ {
+ width: 512,
+ height: 512,
+ options: { responsive: true } as ChartOptions,
+ plugins: [] as ChartPlugin[]
+ },
+ rawProps
+ )
+
+ const init = () => {
+ const ctx = canvasRef()?.getContext("2d") as ChartItem
+ const config = unwrap(props)
+ const chart = new Chart(ctx, {
+ type: config.type,
+ data: config.data,
+ options: config.options,
+ plugins: config.plugins
+ })
+ setChart(chart)
+ }
+
+ onMount(() => init())
+
+ createEffect(
+ on(
+ () => props.data,
+ () => {
+ chart()!.data = props.data
+ chart()!.update()
+ },
+ { defer: true }
+ )
+ )
+
+ createEffect(
+ on(
+ () => props.options,
+ () => {
+ chart()!.options = props.options
+ chart()!.update()
+ },
+ { defer: true }
+ )
+ )
+
+ createEffect(
+ on(
+ [() => props.width, () => props.height],
+ () => {
+ chart()!.resize(props.width, props.height)
+ },
+ { defer: true }
+ )
+ )
+
+ createEffect(
+ on(
+ () => props.type,
+ () => {
+ const dimensions = [chart()!.width, chart()!.height]
+ chart()!.destroy()
+ init()
+ chart()!.resize(...dimensions)
+ },
+ { defer: true }
+ )
+ )
+
+ onCleanup(() => {
+ chart()?.destroy()
+ mergeRefs(props.ref, null)
+ })
+
+ Chart.register(Colors, Filler, Legend, Tooltip)
+ return (
+ setCanvasRef(el))}
+ height={props.height}
+ width={props.width}
+ />
+ )
+}
+
+function showTooltip(context: ChartContext) {
+ let el = document.getElementById("chartjs-tooltip")
+ if (!el) {
+ el = document.createElement("div")
+ el.id = "chartjs-tooltip"
+ document.body.appendChild(el)
+ }
+
+ const model = context.tooltip
+ if (model.opacity === 0 || !model.body) {
+ el.style.opacity = "0"
+ return
+ }
+
+ el.className = `p-2 bg-card text-card-foreground rounded-lg border shadow-sm text-sm ${
+ model.yAlign ?? `no-transform`
+ }`
+
+ let content = ""
+
+ model.title.forEach((title) => {
+ content += `${title} `
+ })
+
+ content += ``
+ const body = model.body.flatMap((body) => body.lines)
+ body.forEach((line, i) => {
+ const colors = model.labelColors[i]
+ content += `
+
+
+ ${line}
+
`
+ })
+ content += `
`
+
+ el.innerHTML = content
+
+ const pos = context.chart.canvas.getBoundingClientRect()
+ el.style.opacity = "1"
+ el.style.position = "absolute"
+ el.style.left = `${pos.left + window.scrollX + model.caretX}px`
+ el.style.top = `${pos.top + window.scrollY + model.caretY}px`
+ el.style.pointerEvents = "none"
+}
+
+function createTypedChart(
+ type: ChartType,
+ components: ChartComponent[]
+): Component {
+ const chartsWithScales: ChartType[] = ["bar", "line", "scatter"]
+ const chartsWithLegends: ChartType[] = ["bar", "line"]
+
+ const options: ChartOptions = {
+ responsive: true,
+ maintainAspectRatio: false,
+ scales: chartsWithScales.includes(type)
+ ? {
+ x: {
+ border: { display: false },
+ grid: { display: false }
+ },
+ y: {
+ border: {
+ dash: [3],
+ dashOffset: 3,
+ display: false
+ },
+ grid: {
+ color: "hsla(240, 3.8%, 46.1%, 0.4)"
+ }
+ }
+ }
+ : {},
+ plugins: {
+ legend: chartsWithLegends.includes(type)
+ ? {
+ display: true,
+ align: "end",
+ labels: {
+ usePointStyle: true,
+ boxWidth: 6,
+ boxHeight: 6,
+ color: "hsl(240, 3.8%, 46.1%)",
+ font: { size: 14 }
+ }
+ }
+ : { display: false },
+ tooltip: {
+ enabled: false,
+ external: (context) => showTooltip(context)
+ }
+ }
+ }
+
+ Chart.register(...components)
+ return (props) =>
+}
+
+const BarChart = /* #__PURE__ */ createTypedChart("bar", [
+ BarController,
+ BarElement,
+ CategoryScale,
+ LinearScale
+])
+const BubbleChart = /* #__PURE__ */ createTypedChart("bubble", [
+ BubbleController,
+ PointElement,
+ LinearScale
+])
+const DonutChart = /* #__PURE__ */ createTypedChart("doughnut", [DoughnutController, ArcElement])
+const LineChart = /* #__PURE__ */ createTypedChart("line", [
+ LineController,
+ LineElement,
+ PointElement,
+ CategoryScale,
+ LinearScale
+])
+const PieChart = /* #__PURE__ */ createTypedChart("pie", [PieController, ArcElement])
+const PolarAreaChart = /* #__PURE__ */ createTypedChart("polarArea", [
+ PolarAreaController,
+ ArcElement,
+ RadialLinearScale
+])
+const RadarChart = /* #__PURE__ */ createTypedChart("radar", [
+ RadarController,
+ LineElement,
+ PointElement,
+ RadialLinearScale
+])
+const ScatterChart = /* #__PURE__ */ createTypedChart("scatter", [
+ ScatterController,
+ PointElement,
+ LinearScale
+])
+
+export {
+ BaseChart as Chart,
+ BarChart,
+ BubbleChart,
+ DonutChart,
+ LineChart,
+ PieChart,
+ PolarAreaChart,
+ RadarChart,
+ ScatterChart
+}
diff --git a/apps/webui/src/components/ui/checkbox.tsx b/apps/webui/src/components/ui/checkbox.tsx
new file mode 100644
index 0000000..184d8d0
--- /dev/null
+++ b/apps/webui/src/components/ui/checkbox.tsx
@@ -0,0 +1,60 @@
+import type { ValidComponent } from "solid-js"
+import { Match, splitProps, Switch } from "solid-js"
+
+import * as CheckboxPrimitive from "@kobalte/core/checkbox"
+import type { PolymorphicProps } from "@kobalte/core/polymorphic"
+
+import { cn } from "~/styles/utils"
+
+type CheckboxRootProps =
+ CheckboxPrimitive.CheckboxRootProps & { class?: string | undefined }
+
+const Checkbox = (
+ props: PolymorphicProps>
+) => {
+ const [local, others] = splitProps(props as CheckboxRootProps, ["class"])
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+export { Checkbox }
diff --git a/apps/webui/src/components/ui/collapsible.tsx b/apps/webui/src/components/ui/collapsible.tsx
new file mode 100644
index 0000000..e8b8082
--- /dev/null
+++ b/apps/webui/src/components/ui/collapsible.tsx
@@ -0,0 +1,9 @@
+import * as CollapsiblePrimitive from "@kobalte/core/collapsible"
+
+const Collapsible = CollapsiblePrimitive.Root
+
+const CollapsibleTrigger = CollapsiblePrimitive.Trigger
+
+const CollapsibleContent = CollapsiblePrimitive.Content
+
+export { Collapsible, CollapsibleTrigger, CollapsibleContent }
diff --git a/apps/webui/src/components/ui/combobox.tsx b/apps/webui/src/components/ui/combobox.tsx
new file mode 100644
index 0000000..d2b8617
--- /dev/null
+++ b/apps/webui/src/components/ui/combobox.tsx
@@ -0,0 +1,192 @@
+import type { JSX, ValidComponent } from "solid-js"
+import { Show, splitProps } from "solid-js"
+
+import * as ComboboxPrimitive from "@kobalte/core/combobox"
+import type { PolymorphicProps } from "@kobalte/core/polymorphic"
+
+import { cn } from "~/styles/utils"
+
+const Combobox = ComboboxPrimitive.Root
+const ComboboxItemLabel = ComboboxPrimitive.ItemLabel
+const ComboboxHiddenSelect = ComboboxPrimitive.HiddenSelect
+
+type ComboboxItemProps = ComboboxPrimitive.ComboboxItemProps & {
+ class?: string | undefined
+}
+
+const ComboboxItem = (
+ props: PolymorphicProps>
+) => {
+ const [local, others] = splitProps(props as ComboboxItemProps, ["class"])
+ return (
+
+ )
+}
+
+type ComboboxItemIndicatorProps =
+ ComboboxPrimitive.ComboboxItemIndicatorProps & {
+ children?: JSX.Element
+ }
+
+const ComboboxItemIndicator = (
+ props: PolymorphicProps>
+) => {
+ const [local, others] = splitProps(props as ComboboxItemIndicatorProps, ["children"])
+ return (
+
+
+
+
+ }
+ >
+ {(children) => children()}
+
+
+ )
+}
+
+type ComboboxSectionProps =
+ ComboboxPrimitive.ComboboxSectionProps & { class?: string | undefined }
+
+const ComboboxSection = (
+ props: PolymorphicProps>
+) => {
+ const [local, others] = splitProps(props as ComboboxSectionProps, ["class"])
+ return (
+
+ )
+}
+
+type ComboboxControlProps<
+ U,
+ T extends ValidComponent = "div"
+> = ComboboxPrimitive.ComboboxControlProps & {
+ class?: string | undefined
+}
+
+const ComboboxControl =