diff --git a/packages/admin/.tanstack/tmp/57794a11-f0abc49f852aebe0077e70caa4e1d479 b/packages/admin/.tanstack/tmp/57794a11-f0abc49f852aebe0077e70caa4e1d479 deleted file mode 100644 index ac5f67e..0000000 --- a/packages/admin/.tanstack/tmp/57794a11-f0abc49f852aebe0077e70caa4e1d479 +++ /dev/null @@ -1,323 +0,0 @@ -/* eslint-disable */ - -// @ts-nocheck - -// noinspection JSUnusedGlobalSymbols - -// This file was automatically generated by TanStack Router. -// You should NOT make any changes in this file as it will be overwritten. -// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. - -import { Route as rootRouteImport } from './routes/__root' -import { Route as LoginRouteImport } from './routes/login' -import { Route as AuthenticatedRouteImport } from './routes/_authenticated' -import { Route as AuthenticatedIndexRouteImport } from './routes/_authenticated/index' -import { Route as AuthenticatedMembersRouteImport } from './routes/_authenticated/members' -import { Route as AuthenticatedAccountsIndexRouteImport } from './routes/_authenticated/accounts/index' -import { Route as AuthenticatedAccountsNewRouteImport } from './routes/_authenticated/accounts/new' -import { Route as AuthenticatedAccountsAccountIdRouteImport } from './routes/_authenticated/accounts/$accountId' -import { Route as AuthenticatedAccountsAccountIdIndexRouteImport } from './routes/_authenticated/accounts/$accountId/index' -import { Route as AuthenticatedAccountsAccountIdTaxExemptionsRouteImport } from './routes/_authenticated/accounts/$accountId/tax-exemptions' -import { Route as AuthenticatedAccountsAccountIdProcessorLinksRouteImport } from './routes/_authenticated/accounts/$accountId/processor-links' -import { Route as AuthenticatedAccountsAccountIdPaymentMethodsRouteImport } from './routes/_authenticated/accounts/$accountId/payment-methods' -import { Route as AuthenticatedAccountsAccountIdMembersRouteImport } from './routes/_authenticated/accounts/$accountId/members' - -const LoginRoute = LoginRouteImport.update({ - id: '/login', - path: '/login', - getParentRoute: () => rootRouteImport, -} as any) -const AuthenticatedRoute = AuthenticatedRouteImport.update({ - id: '/_authenticated', - getParentRoute: () => rootRouteImport, -} as any) -const AuthenticatedIndexRoute = AuthenticatedIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => AuthenticatedRoute, -} as any) -const AuthenticatedMembersRoute = AuthenticatedMembersRouteImport.update({ - id: '/members', - path: '/members', - getParentRoute: () => AuthenticatedRoute, -} as any) -const AuthenticatedAccountsIndexRoute = - AuthenticatedAccountsIndexRouteImport.update({ - id: '/accounts/', - path: '/accounts/', - getParentRoute: () => AuthenticatedRoute, - } as any) -const AuthenticatedAccountsNewRoute = - AuthenticatedAccountsNewRouteImport.update({ - id: '/accounts/new', - path: '/accounts/new', - getParentRoute: () => AuthenticatedRoute, - } as any) -const AuthenticatedAccountsAccountIdRoute = - AuthenticatedAccountsAccountIdRouteImport.update({ - id: '/accounts/$accountId', - path: '/accounts/$accountId', - getParentRoute: () => AuthenticatedRoute, - } as any) -const AuthenticatedAccountsAccountIdIndexRoute = - AuthenticatedAccountsAccountIdIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => AuthenticatedAccountsAccountIdRoute, - } as any) -const AuthenticatedAccountsAccountIdTaxExemptionsRoute = - AuthenticatedAccountsAccountIdTaxExemptionsRouteImport.update({ - id: '/tax-exemptions', - path: '/tax-exemptions', - getParentRoute: () => AuthenticatedAccountsAccountIdRoute, - } as any) -const AuthenticatedAccountsAccountIdProcessorLinksRoute = - AuthenticatedAccountsAccountIdProcessorLinksRouteImport.update({ - id: '/processor-links', - path: '/processor-links', - getParentRoute: () => AuthenticatedAccountsAccountIdRoute, - } as any) -const AuthenticatedAccountsAccountIdPaymentMethodsRoute = - AuthenticatedAccountsAccountIdPaymentMethodsRouteImport.update({ - id: '/payment-methods', - path: '/payment-methods', - getParentRoute: () => AuthenticatedAccountsAccountIdRoute, - } as any) -const AuthenticatedAccountsAccountIdMembersRoute = - AuthenticatedAccountsAccountIdMembersRouteImport.update({ - id: '/members', - path: '/members', - getParentRoute: () => AuthenticatedAccountsAccountIdRoute, - } as any) - -export interface FileRoutesByFullPath { - '/': typeof AuthenticatedIndexRoute - '/login': typeof LoginRoute - '/members': typeof AuthenticatedMembersRoute - '/accounts/$accountId': typeof AuthenticatedAccountsAccountIdRouteWithChildren - '/accounts/new': typeof AuthenticatedAccountsNewRoute - '/accounts/': typeof AuthenticatedAccountsIndexRoute - '/accounts/$accountId/members': typeof AuthenticatedAccountsAccountIdMembersRoute - '/accounts/$accountId/payment-methods': typeof AuthenticatedAccountsAccountIdPaymentMethodsRoute - '/accounts/$accountId/processor-links': typeof AuthenticatedAccountsAccountIdProcessorLinksRoute - '/accounts/$accountId/tax-exemptions': typeof AuthenticatedAccountsAccountIdTaxExemptionsRoute - '/accounts/$accountId/': typeof AuthenticatedAccountsAccountIdIndexRoute -} -export interface FileRoutesByTo { - '/login': typeof LoginRoute - '/members': typeof AuthenticatedMembersRoute - '/': typeof AuthenticatedIndexRoute - '/accounts/new': typeof AuthenticatedAccountsNewRoute - '/accounts': typeof AuthenticatedAccountsIndexRoute - '/accounts/$accountId/members': typeof AuthenticatedAccountsAccountIdMembersRoute - '/accounts/$accountId/payment-methods': typeof AuthenticatedAccountsAccountIdPaymentMethodsRoute - '/accounts/$accountId/processor-links': typeof AuthenticatedAccountsAccountIdProcessorLinksRoute - '/accounts/$accountId/tax-exemptions': typeof AuthenticatedAccountsAccountIdTaxExemptionsRoute - '/accounts/$accountId': typeof AuthenticatedAccountsAccountIdIndexRoute -} -export interface FileRoutesById { - __root__: typeof rootRouteImport - '/_authenticated': typeof AuthenticatedRouteWithChildren - '/login': typeof LoginRoute - '/_authenticated/members': typeof AuthenticatedMembersRoute - '/_authenticated/': typeof AuthenticatedIndexRoute - '/_authenticated/accounts/$accountId': typeof AuthenticatedAccountsAccountIdRouteWithChildren - '/_authenticated/accounts/new': typeof AuthenticatedAccountsNewRoute - '/_authenticated/accounts/': typeof AuthenticatedAccountsIndexRoute - '/_authenticated/accounts/$accountId/members': typeof AuthenticatedAccountsAccountIdMembersRoute - '/_authenticated/accounts/$accountId/payment-methods': typeof AuthenticatedAccountsAccountIdPaymentMethodsRoute - '/_authenticated/accounts/$accountId/processor-links': typeof AuthenticatedAccountsAccountIdProcessorLinksRoute - '/_authenticated/accounts/$accountId/tax-exemptions': typeof AuthenticatedAccountsAccountIdTaxExemptionsRoute - '/_authenticated/accounts/$accountId/': typeof AuthenticatedAccountsAccountIdIndexRoute -} -export interface FileRouteTypes { - fileRoutesByFullPath: FileRoutesByFullPath - fullPaths: - | '/' - | '/login' - | '/members' - | '/accounts/$accountId' - | '/accounts/new' - | '/accounts/' - | '/accounts/$accountId/members' - | '/accounts/$accountId/payment-methods' - | '/accounts/$accountId/processor-links' - | '/accounts/$accountId/tax-exemptions' - | '/accounts/$accountId/' - fileRoutesByTo: FileRoutesByTo - to: - | '/login' - | '/members' - | '/' - | '/accounts/new' - | '/accounts' - | '/accounts/$accountId/members' - | '/accounts/$accountId/payment-methods' - | '/accounts/$accountId/processor-links' - | '/accounts/$accountId/tax-exemptions' - | '/accounts/$accountId' - id: - | '__root__' - | '/_authenticated' - | '/login' - | '/_authenticated/members' - | '/_authenticated/' - | '/_authenticated/accounts/$accountId' - | '/_authenticated/accounts/new' - | '/_authenticated/accounts/' - | '/_authenticated/accounts/$accountId/members' - | '/_authenticated/accounts/$accountId/payment-methods' - | '/_authenticated/accounts/$accountId/processor-links' - | '/_authenticated/accounts/$accountId/tax-exemptions' - | '/_authenticated/accounts/$accountId/' - fileRoutesById: FileRoutesById -} -export interface RootRouteChildren { - AuthenticatedRoute: typeof AuthenticatedRouteWithChildren - LoginRoute: typeof LoginRoute -} - -declare module '@tanstack/react-router' { - interface FileRoutesByPath { - '/login': { - id: '/login' - path: '/login' - fullPath: '/login' - preLoaderRoute: typeof LoginRouteImport - parentRoute: typeof rootRouteImport - } - '/_authenticated': { - id: '/_authenticated' - path: '' - fullPath: '/' - preLoaderRoute: typeof AuthenticatedRouteImport - parentRoute: typeof rootRouteImport - } - '/_authenticated/': { - id: '/_authenticated/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof AuthenticatedIndexRouteImport - parentRoute: typeof AuthenticatedRoute - } - '/_authenticated/members': { - id: '/_authenticated/members' - path: '/members' - fullPath: '/members' - preLoaderRoute: typeof AuthenticatedMembersRouteImport - parentRoute: typeof AuthenticatedRoute - } - '/_authenticated/accounts/': { - id: '/_authenticated/accounts/' - path: '/accounts' - fullPath: '/accounts/' - preLoaderRoute: typeof AuthenticatedAccountsIndexRouteImport - parentRoute: typeof AuthenticatedRoute - } - '/_authenticated/accounts/new': { - id: '/_authenticated/accounts/new' - path: '/accounts/new' - fullPath: '/accounts/new' - preLoaderRoute: typeof AuthenticatedAccountsNewRouteImport - parentRoute: typeof AuthenticatedRoute - } - '/_authenticated/accounts/$accountId': { - id: '/_authenticated/accounts/$accountId' - path: '/accounts/$accountId' - fullPath: '/accounts/$accountId' - preLoaderRoute: typeof AuthenticatedAccountsAccountIdRouteImport - parentRoute: typeof AuthenticatedRoute - } - '/_authenticated/accounts/$accountId/': { - id: '/_authenticated/accounts/$accountId/' - path: '/' - fullPath: '/accounts/$accountId/' - preLoaderRoute: typeof AuthenticatedAccountsAccountIdIndexRouteImport - parentRoute: typeof AuthenticatedAccountsAccountIdRoute - } - '/_authenticated/accounts/$accountId/tax-exemptions': { - id: '/_authenticated/accounts/$accountId/tax-exemptions' - path: '/tax-exemptions' - fullPath: '/accounts/$accountId/tax-exemptions' - preLoaderRoute: typeof AuthenticatedAccountsAccountIdTaxExemptionsRouteImport - parentRoute: typeof AuthenticatedAccountsAccountIdRoute - } - '/_authenticated/accounts/$accountId/processor-links': { - id: '/_authenticated/accounts/$accountId/processor-links' - path: '/processor-links' - fullPath: '/accounts/$accountId/processor-links' - preLoaderRoute: typeof AuthenticatedAccountsAccountIdProcessorLinksRouteImport - parentRoute: typeof AuthenticatedAccountsAccountIdRoute - } - '/_authenticated/accounts/$accountId/payment-methods': { - id: '/_authenticated/accounts/$accountId/payment-methods' - path: '/payment-methods' - fullPath: '/accounts/$accountId/payment-methods' - preLoaderRoute: typeof AuthenticatedAccountsAccountIdPaymentMethodsRouteImport - parentRoute: typeof AuthenticatedAccountsAccountIdRoute - } - '/_authenticated/accounts/$accountId/members': { - id: '/_authenticated/accounts/$accountId/members' - path: '/members' - fullPath: '/accounts/$accountId/members' - preLoaderRoute: typeof AuthenticatedAccountsAccountIdMembersRouteImport - parentRoute: typeof AuthenticatedAccountsAccountIdRoute - } - } -} - -interface AuthenticatedAccountsAccountIdRouteChildren { - AuthenticatedAccountsAccountIdMembersRoute: typeof AuthenticatedAccountsAccountIdMembersRoute - AuthenticatedAccountsAccountIdPaymentMethodsRoute: typeof AuthenticatedAccountsAccountIdPaymentMethodsRoute - AuthenticatedAccountsAccountIdProcessorLinksRoute: typeof AuthenticatedAccountsAccountIdProcessorLinksRoute - AuthenticatedAccountsAccountIdTaxExemptionsRoute: typeof AuthenticatedAccountsAccountIdTaxExemptionsRoute - AuthenticatedAccountsAccountIdIndexRoute: typeof AuthenticatedAccountsAccountIdIndexRoute -} - -const AuthenticatedAccountsAccountIdRouteChildren: AuthenticatedAccountsAccountIdRouteChildren = - { - AuthenticatedAccountsAccountIdMembersRoute: - AuthenticatedAccountsAccountIdMembersRoute, - AuthenticatedAccountsAccountIdPaymentMethodsRoute: - AuthenticatedAccountsAccountIdPaymentMethodsRoute, - AuthenticatedAccountsAccountIdProcessorLinksRoute: - AuthenticatedAccountsAccountIdProcessorLinksRoute, - AuthenticatedAccountsAccountIdTaxExemptionsRoute: - AuthenticatedAccountsAccountIdTaxExemptionsRoute, - AuthenticatedAccountsAccountIdIndexRoute: - AuthenticatedAccountsAccountIdIndexRoute, - } - -const AuthenticatedAccountsAccountIdRouteWithChildren = - AuthenticatedAccountsAccountIdRoute._addFileChildren( - AuthenticatedAccountsAccountIdRouteChildren, - ) - -interface AuthenticatedRouteChildren { - AuthenticatedMembersRoute: typeof AuthenticatedMembersRoute - AuthenticatedIndexRoute: typeof AuthenticatedIndexRoute - AuthenticatedAccountsAccountIdRoute: typeof AuthenticatedAccountsAccountIdRouteWithChildren - AuthenticatedAccountsNewRoute: typeof AuthenticatedAccountsNewRoute - AuthenticatedAccountsIndexRoute: typeof AuthenticatedAccountsIndexRoute -} - -const AuthenticatedRouteChildren: AuthenticatedRouteChildren = { - AuthenticatedMembersRoute: AuthenticatedMembersRoute, - AuthenticatedIndexRoute: AuthenticatedIndexRoute, - AuthenticatedAccountsAccountIdRoute: - AuthenticatedAccountsAccountIdRouteWithChildren, - AuthenticatedAccountsNewRoute: AuthenticatedAccountsNewRoute, - AuthenticatedAccountsIndexRoute: AuthenticatedAccountsIndexRoute, -} - -const AuthenticatedRouteWithChildren = AuthenticatedRoute._addFileChildren( - AuthenticatedRouteChildren, -) - -const rootRouteChildren: RootRouteChildren = { - AuthenticatedRoute: AuthenticatedRouteWithChildren, - LoginRoute: LoginRoute, -} -export const routeTree = rootRouteImport - ._addFileChildren(rootRouteChildren) - ._addFileTypes() diff --git a/packages/admin/src/routeTree.gen.ts b/packages/admin/src/routeTree.gen.ts index ac5f67e..2ebccb2 100644 --- a/packages/admin/src/routeTree.gen.ts +++ b/packages/admin/src/routeTree.gen.ts @@ -13,6 +13,7 @@ import { Route as LoginRouteImport } from './routes/login' import { Route as AuthenticatedRouteImport } from './routes/_authenticated' import { Route as AuthenticatedIndexRouteImport } from './routes/_authenticated/index' import { Route as AuthenticatedMembersRouteImport } from './routes/_authenticated/members' +import { Route as AuthenticatedHelpRouteImport } from './routes/_authenticated/help' import { Route as AuthenticatedAccountsIndexRouteImport } from './routes/_authenticated/accounts/index' import { Route as AuthenticatedAccountsNewRouteImport } from './routes/_authenticated/accounts/new' import { Route as AuthenticatedAccountsAccountIdRouteImport } from './routes/_authenticated/accounts/$accountId' @@ -41,6 +42,11 @@ const AuthenticatedMembersRoute = AuthenticatedMembersRouteImport.update({ path: '/members', getParentRoute: () => AuthenticatedRoute, } as any) +const AuthenticatedHelpRoute = AuthenticatedHelpRouteImport.update({ + id: '/help', + path: '/help', + getParentRoute: () => AuthenticatedRoute, +} as any) const AuthenticatedAccountsIndexRoute = AuthenticatedAccountsIndexRouteImport.update({ id: '/accounts/', @@ -93,6 +99,7 @@ const AuthenticatedAccountsAccountIdMembersRoute = export interface FileRoutesByFullPath { '/': typeof AuthenticatedIndexRoute '/login': typeof LoginRoute + '/help': typeof AuthenticatedHelpRoute '/members': typeof AuthenticatedMembersRoute '/accounts/$accountId': typeof AuthenticatedAccountsAccountIdRouteWithChildren '/accounts/new': typeof AuthenticatedAccountsNewRoute @@ -105,6 +112,7 @@ export interface FileRoutesByFullPath { } export interface FileRoutesByTo { '/login': typeof LoginRoute + '/help': typeof AuthenticatedHelpRoute '/members': typeof AuthenticatedMembersRoute '/': typeof AuthenticatedIndexRoute '/accounts/new': typeof AuthenticatedAccountsNewRoute @@ -119,6 +127,7 @@ export interface FileRoutesById { __root__: typeof rootRouteImport '/_authenticated': typeof AuthenticatedRouteWithChildren '/login': typeof LoginRoute + '/_authenticated/help': typeof AuthenticatedHelpRoute '/_authenticated/members': typeof AuthenticatedMembersRoute '/_authenticated/': typeof AuthenticatedIndexRoute '/_authenticated/accounts/$accountId': typeof AuthenticatedAccountsAccountIdRouteWithChildren @@ -135,6 +144,7 @@ export interface FileRouteTypes { fullPaths: | '/' | '/login' + | '/help' | '/members' | '/accounts/$accountId' | '/accounts/new' @@ -147,6 +157,7 @@ export interface FileRouteTypes { fileRoutesByTo: FileRoutesByTo to: | '/login' + | '/help' | '/members' | '/' | '/accounts/new' @@ -160,6 +171,7 @@ export interface FileRouteTypes { | '__root__' | '/_authenticated' | '/login' + | '/_authenticated/help' | '/_authenticated/members' | '/_authenticated/' | '/_authenticated/accounts/$accountId' @@ -207,6 +219,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof AuthenticatedMembersRouteImport parentRoute: typeof AuthenticatedRoute } + '/_authenticated/help': { + id: '/_authenticated/help' + path: '/help' + fullPath: '/help' + preLoaderRoute: typeof AuthenticatedHelpRouteImport + parentRoute: typeof AuthenticatedRoute + } '/_authenticated/accounts/': { id: '/_authenticated/accounts/' path: '/accounts' @@ -294,6 +313,7 @@ const AuthenticatedAccountsAccountIdRouteWithChildren = ) interface AuthenticatedRouteChildren { + AuthenticatedHelpRoute: typeof AuthenticatedHelpRoute AuthenticatedMembersRoute: typeof AuthenticatedMembersRoute AuthenticatedIndexRoute: typeof AuthenticatedIndexRoute AuthenticatedAccountsAccountIdRoute: typeof AuthenticatedAccountsAccountIdRouteWithChildren @@ -302,6 +322,7 @@ interface AuthenticatedRouteChildren { } const AuthenticatedRouteChildren: AuthenticatedRouteChildren = { + AuthenticatedHelpRoute: AuthenticatedHelpRoute, AuthenticatedMembersRoute: AuthenticatedMembersRoute, AuthenticatedIndexRoute: AuthenticatedIndexRoute, AuthenticatedAccountsAccountIdRoute: diff --git a/packages/admin/src/routes/_authenticated.tsx b/packages/admin/src/routes/_authenticated.tsx index d81d696..831fcea 100644 --- a/packages/admin/src/routes/_authenticated.tsx +++ b/packages/admin/src/routes/_authenticated.tsx @@ -14,7 +14,7 @@ import { DropdownMenuSubTrigger, DropdownMenuSubContent, } from '@/components/ui/dropdown-menu' -import { Users, UserRound, Sun, Moon, Monitor, LogOut, User, Palette } from 'lucide-react' +import { Users, UserRound, HelpCircle, Sun, Moon, Monitor, LogOut, User, Palette } from 'lucide-react' export const Route = createFileRoute('/_authenticated')({ beforeLoad: () => { @@ -69,6 +69,14 @@ function AuthenticatedLayout() { Members + + + Help +
diff --git a/packages/admin/src/routes/_authenticated/help.tsx b/packages/admin/src/routes/_authenticated/help.tsx new file mode 100644 index 0000000..4fbe5d5 --- /dev/null +++ b/packages/admin/src/routes/_authenticated/help.tsx @@ -0,0 +1,148 @@ +import { createFileRoute } from '@tanstack/react-router' +import { useState } from 'react' +import { getWikiCategories, getWikiPage, type WikiPage } from '@/wiki' +import { Input } from '@/components/ui/input' +import { Search } from 'lucide-react' + +export const Route = createFileRoute('/_authenticated/help')({ + validateSearch: (search: Record) => ({ + page: (search.page as string) || 'getting-started', + }), + component: HelpPage, +}) + +function renderMarkdown(content: string) { + // Simple markdown to HTML — handles headers, bold, lists, tips, notes + return content + .split('\n\n') + .map((block, i) => { + const trimmed = block.trim() + if (!trimmed) return null + + // Headers + if (trimmed.startsWith('## ')) + return

{trimmed.slice(3)}

+ if (trimmed.startsWith('# ')) + return

{trimmed.slice(2)}

+ + // Lists + if (trimmed.match(/^\d+\./m) || trimmed.startsWith('- ')) { + const items = trimmed.split('\n').filter(Boolean) + const isOrdered = items[0].match(/^\d+\./) + const Tag = isOrdered ? 'ol' : 'ul' + return ( + + {items.map((item, j) => ( +
  • + {renderInline(item.replace(/^(\d+\.\s*|-\s*)/, ''))} +
  • + ))} +
    + ) + } + + // Paragraph + return

    {renderInline(trimmed)}

    + }) + .filter(Boolean) +} + +function renderInline(text: string) { + // Bold + const parts = text.split(/(\*\*[^*]+\*\*)/) + return parts.map((part, i) => { + if (part.startsWith('**') && part.endsWith('**')) { + return {part.slice(2, -2)} + } + return part + }) +} + +function HelpPage() { + const categories = getWikiCategories() + const search = Route.useSearch() + const navigate = Route.useNavigate() + const currentPage = getWikiPage(search.page) + const [searchQuery, setSearchQuery] = useState('') + + const allPages = categories.flatMap((c) => c.pages) + const filteredPages = searchQuery + ? allPages.filter( + (p) => + p.title.toLowerCase().includes(searchQuery.toLowerCase()) || + p.content.toLowerCase().includes(searchQuery.toLowerCase()), + ) + : null + + function goToPage(slug: string) { + navigate({ search: { page: slug } }) + setSearchQuery('') + } + + return ( +
    + {/* Sidebar */} +
    +
    + + setSearchQuery(e.target.value)} + className="pl-9 text-sm" + /> +
    + + {filteredPages ? ( +
    + {filteredPages.length === 0 ? ( +

    No results

    + ) : ( + filteredPages.map((p) => ( + + )) + )} +
    + ) : ( + categories.map((cat) => ( +
    +

    + {cat.name} +

    +
    + {cat.pages.map((p) => ( + + ))} +
    +
    + )) + )} +
    + + {/* Content */} +
    + {currentPage ? ( +
    {renderMarkdown(currentPage.content)}
    + ) : ( +

    Page not found

    + )} +
    +
    + ) +} diff --git a/packages/admin/src/wiki/index.ts b/packages/admin/src/wiki/index.ts new file mode 100644 index 0000000..2c0ffca --- /dev/null +++ b/packages/admin/src/wiki/index.ts @@ -0,0 +1,203 @@ +export interface WikiPage { + slug: string + title: string + category: string + content: string +} + +export interface WikiCategory { + name: string + pages: WikiPage[] +} + +const pages: WikiPage[] = [ + { + slug: 'getting-started', + title: 'Getting Started', + category: 'General', + content: ` +# Getting Started with Forte + +Welcome to Forte — your music store management platform. + +## Signing In + +1. Open Forte in your browser +2. Enter your email and password +3. Click **Sign in** + +If you don't have an account, ask your store manager to create one for you. + +## Navigation + +Use the sidebar on the left to navigate between sections: + +- **Accounts** — manage customer accounts and their members +- **Members** — find and manage individual people across all accounts +- **Help** — you're here! + +## Need Help? + +If you can't find what you're looking for, contact your store manager or system administrator. + `.trim(), + }, + { + slug: 'accounts-overview', + title: 'Accounts Overview', + category: 'Accounts', + content: ` +# Accounts + +An **account** is a billing entity — it could be a family, a business, a school, or an individual person. All billing, invoices, and payments are tied to an account. + +## Creating an Account + +1. Go to **Accounts** in the sidebar +2. Click **New Account** in the top right +3. Fill in the account name (e.g. "Smith Family" or "Lincoln Elementary") +4. Optionally add email, phone, and address +5. Add the **primary contact** — this is the main person on the account +6. Click **Create Account** + +**Tip:** If you leave the account name blank and fill in the primary contact, the account name will be automatically set to the person's name. + +## Account Details + +Click any account in the list to see its details. The detail page has tabs: + +- **Overview** — edit account name, contact info, billing mode +- **Members** — people on the account (family members, students, etc.) +- **Payment Methods** — cards on file +- **Tax Exemptions** — tax-exempt certificates +- **Processor Links** — payment processor connections + +## Searching + +Use the search bar to find accounts by name, email, phone, or account number. You can also find accounts by searching for a member's name. + +## Account Numbers + +Every account gets a unique 6-digit number automatically. This number appears in the account list and can be used for quick lookup. + `.trim(), + }, + { + slug: 'members-overview', + title: 'Members Overview', + category: 'Accounts', + content: ` +# Members + +A **member** is an individual person associated with an account. This could be a parent, a child, a student, a band director — anyone who takes lessons, rents instruments, or needs to be tracked. + +## Adding a Member + +1. Go to an account's **Members** tab +2. Click **Add Member** +3. Enter first and last name (required) +4. Optionally add email, phone, date of birth +5. Click **Add Member** + +**Tip:** If you don't provide email, phone, or address, the member will automatically inherit these from the account. + +## Minor Flag + +If a member is under 18, they're flagged as a **Minor**. This happens automatically if you enter a date of birth. If the family prefers not to share a birthday, you can check the "This person is a minor" box instead. + +## Member Numbers + +Like accounts, every member gets a unique 6-digit number for quick reference. + +## Finding Members + +Use the **Members** page in the sidebar to search across all members in all accounts. Click a member to go to their account. + +## Moving a Member + +If a member needs to be moved to a different account (e.g. a student aging out and getting their own account), ask your administrator — members can be reassigned between accounts. + `.trim(), + }, + { + slug: 'payment-methods', + title: 'Payment Methods', + category: 'Accounts', + content: ` +# Payment Methods + +Payment methods are cards on file for an account. These are used for recurring billing (rentals, lessons) and in-store purchases. + +## Adding a Payment Method + +1. Go to an account's **Payment Methods** tab +2. Click **Add Method** +3. Select the processor (Stripe or Global Payments) +4. Enter the payment method ID from the processor +5. Optionally enter card brand, last four digits, and expiration +6. Click **Add Payment Method** + +**Note:** Card details are stored securely with the payment processor — Forte only keeps a reference and display info (last 4 digits, brand). + +## Default Payment Method + +One payment method can be marked as the **default**. Click the star icon next to a method to make it the default. The previous default is automatically unset. + +## Needs Update Flag + +If a payment method was migrated from an old system, it may show a "Needs Update" badge. This means the customer needs to re-enter their card information. + `.trim(), + }, + { + slug: 'tax-exemptions', + title: 'Tax Exemptions', + category: 'Accounts', + content: ` +# Tax Exemptions + +Schools, churches, and resellers may be exempt from sales tax. Forte tracks tax exemption certificates per account. + +## Adding a Tax Exemption + +1. Go to an account's **Tax Exemptions** tab +2. Click **Add Exemption** +3. Enter the certificate number (required) +4. Optionally add certificate type (e.g. "resale"), issuing state, and expiration date +5. Click **Add Tax Exemption** + +The exemption starts in **Pending** status. + +## Approving an Exemption + +A manager must verify the certificate and approve it: + +1. Click the **check mark** icon next to a pending exemption +2. The status changes to **Approved** + +## Revoking an Exemption + +If a certificate expires or is no longer valid: + +1. Click the **X** icon next to an approved exemption +2. Enter a reason for revoking +3. The status changes back to **None** + +All approvals and revocations are logged with who did it and when. + `.trim(), + }, +] + +export function getWikiPages(): WikiPage[] { + return pages +} + +export function getWikiPage(slug: string): WikiPage | undefined { + return pages.find((p) => p.slug === slug) +} + +export function getWikiCategories(): WikiCategory[] { + const categoryMap = new Map() + for (const page of pages) { + const existing = categoryMap.get(page.category) ?? [] + existing.push(page) + categoryMap.set(page.category, existing) + } + return Array.from(categoryMap.entries()).map(([name, pages]) => ({ name, pages })) +}