diff --git a/.depcheckrc b/.depcheckrc deleted file mode 100644 index 1e44b72..0000000 --- a/.depcheckrc +++ /dev/null @@ -1,2 +0,0 @@ -ignores: ["eslint", "babel-*", "depcheck", "@types/node", "@types/react-dom", "autoprefixer", "postcss"] -skip-missing: false diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index 1e48203..0000000 --- a/.editorconfig +++ /dev/null @@ -1,10 +0,0 @@ -# editorconfig.org -root = true - -[*] -charset = utf-8 -end_of_line = lf -indent_size = 2 -indent_style = space -insert_final_newline = true -trim_trailing_whitespace = true \ No newline at end of file diff --git a/.env.example b/.env.example index eacd76b..26b65f3 100644 --- a/.env.example +++ b/.env.example @@ -1,2 +1,2 @@ -NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY= -CLERK_SECRET_KEY= +SERVER_PB="http://localhost:8090" +PUBLIC_CLIENT_PB="http://127.0.0.1:8090" diff --git a/.eslintignore b/.eslintignore index 26f5c15..3897265 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1 +1,13 @@ -.github +.DS_Store +node_modules +/build +/.svelte-kit +/package +.env +.env.* +!.env.example + +# Ignore files for PNPM, NPM and YARN +pnpm-lock.yaml +package-lock.json +yarn.lock diff --git a/.eslintrc.cjs b/.eslintrc.cjs new file mode 100644 index 0000000..58c2ca9 --- /dev/null +++ b/.eslintrc.cjs @@ -0,0 +1,37 @@ +/** @type { import("eslint").Linter.Config } */ +module.exports = { + root: true, + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:svelte/recommended', + 'prettier' + ], + parser: '@typescript-eslint/parser', + plugins: ['@typescript-eslint'], + parserOptions: { + sourceType: 'module', + ecmaVersion: 2020, + extraFileExtensions: ['.svelte'] + }, + env: { + browser: true, + es2017: true, + node: true + }, + overrides: [ + { + files: ['*.svelte'], + parser: 'svelte-eslint-parser', + parserOptions: { + parser: '@typescript-eslint/parser' + } + } + ], + rules: { + '@typescript-eslint/no-unused-vars': [ + 'warn', + { varsIgnorePattern: '^\\$\\$(Props|Events|Slots)$' } + ] + } +}; diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index bffb357..0000000 --- a/.eslintrc.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "next/core-web-vitals" -} diff --git a/.github/dependabot.yml b/.github/dependabot.yaml similarity index 92% rename from .github/dependabot.yml rename to .github/dependabot.yaml index 1323e41..5661b31 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yaml @@ -11,4 +11,4 @@ updates: schedule: interval: "weekly" reviewers: - - "bartvdbraak" + - "bartvdbraak" \ No newline at end of file diff --git a/.github/renovate.json b/.github/renovate.json new file mode 100644 index 0000000..7269f0b --- /dev/null +++ b/.github/renovate.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": ["config:base"], + "reviewers": ["bartvdbraak"] +} \ No newline at end of file diff --git a/.github/workflows/gh-pages-cleanup.yaml b/.github/workflows/gh-pages-cleanup.yaml deleted file mode 100644 index c19ab30..0000000 --- a/.github/workflows/gh-pages-cleanup.yaml +++ /dev/null @@ -1,34 +0,0 @@ -name: Cleanup GitHub Pages on Branch Deletion - -on: - delete - -permissions: - contents: write - -concurrency: - group: "pages" - cancel-in-progress: false - -jobs: - cleanup: - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v3.5.3 - - - name: Delete directory in gh-pages - if: github.event.ref_type == 'branch' - run: | - git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com" - git config --global user.name "github-actions[bot]" - git fetch --all - git checkout gh-pages - branchName=$(echo "${{ github.event.ref }}" | sed -e 's,^refs/heads/,,') - if [ -d "./${branchName}" ]; then - git rm -rf --ignore-unmatch "${branchName}" - git commit -m "Cleanup directory for deleted branch ${branchName}" - git push origin gh-pages - else - echo "Directory doesn't exist for branch ${branchName}" - fi diff --git a/.github/workflows/lighthouse-report.yaml b/.github/workflows/lighthouse-report.yaml deleted file mode 100644 index aa65f6b..0000000 --- a/.github/workflows/lighthouse-report.yaml +++ /dev/null @@ -1,152 +0,0 @@ -name: Lighthouse report - -on: - pull_request: - -permissions: - contents: write - pages: write - id-token: write - pull-requests: write - -concurrency: - group: "pages" - cancel-in-progress: false - -jobs: - unlighthouse: - environment: - name: github-pages - url: https://${{ github.repository_owner }}.github.io/${{ github.event.repository.name }}/${{ env.BRANCH_NAME }} - env: - BRANCH_NAME: ${{ github.head_ref || github.ref_name }} - runs-on: ubuntu-latest - strategy: - matrix: - node-version: [18] - steps: - - name: Sticky Comment on Pull Request - uses: marocchino/sticky-pull-request-comment@v2.7.0 - with: - header: unlighthouse - message: | - ⚡️ Lighthouse report - - ![Loading](https://github.com/bartvdbraak/omnidash/assets/3996360/8e85bc78-53ac-41de-bdb6-bedfe8c6d8c1) - - - name: Checkout Git repository - uses: actions/checkout@v3.5.3 - - - name: Setup pnpm - uses: pnpm/action-setup@v2.4.0 - with: - version: 8.6.2 - - - name: Setup Node.js - uses: actions/setup-node@v3.7.0 - with: - node-version: ${{ matrix.node-version }} - cache: pnpm - - - name: Install Dependencies - run: pnpm add -g @unlighthouse/cli puppeteer - - - name: Retrieve Vercel Preview URL - uses: zentered/vercel-preview-url@v1.1.9 - id: vercel_preview_url - env: - VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }} - with: - vercel_project_id: ${{ vars.VERCEL_PROJECT_ID }} - - - name: Await Vercel Deployment - uses: UnlyEd/github-action-await-vercel@v1.2.43 - env: - VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }} - with: - deployment-url: ${{ steps.vercel_preview_url.outputs.preview_url }} - timeout: 120 - - - name: Build Unlighthouse report - run: | - unlighthouse-ci \ - --router-prefix "${{ github.event.repository.name }}/${{ env.BRANCH_NAME }}" \ - --site "${{ steps.vercel_preview_url.outputs.preview_url }}" \ - --reporter jsonExpanded \ - --build-static - - - name: Deploy report to GitHub pages - uses: peaceiris/actions-gh-pages@v3.9.3 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: ./.unlighthouse - destination_dir: ${{ env.BRANCH_NAME }} - - - name: Format lighthouse score - id: format_lighthouse_score - uses: actions/github-script@v6.4.1 - with: - github-token: ${{secrets.GITHUB_TOKEN}} - script: | - const fs = require('fs'); - - const result = JSON.parse(fs.readFileSync('.unlighthouse/ci-result.json', 'utf8')); - - const formatScore = score => `${Math.round(score * 100)} (${score})`; - const getEmoji = score => score >= 0.9 ? '🟢' : score >= 0.5 ? '🟠' : '🔴'; - - const score = res => `${getEmoji(res)} ${formatScore(res)}`; - - const reportUrl = `https://${{ github.repository_owner }}.github.io/${{ github.event.repository.name }}/${{ env.BRANCH_NAME }}`; - - const comment = [ - `⚡️ Lighthouse report for the changes in this PR:`, - '| Category | Score |', - '| --- | --- |', - `| Performance | ${score(result.summary.categories.performance.averageScore)} |`, - `| Accessibility | ${score(result.summary.categories.accessibility.averageScore)} |`, - `| Best practices | ${score(result.summary.categories['best-practices'].averageScore)} |`, - `| SEO | ${score(result.summary.categories.seo.averageScore)} |`, - `| *Overall* | ${score(result.summary.score)} |`, - '', - '*Lighthouse scores for individual routes:*', - '', - '| Path | Performance | Accessibility | Best practices | SEO | Overall |', - '| --- | --- | --- | --- | --- | --- |', - `${result.routes.map(route => `| ${route.path} | ${score(route.categories.performance.score)} | ${score(route.categories.accessibility.score)} | ${score(route.categories['best-practices'].score)} | ${score(route.categories.seo.score)} | ${score(route.score)} |`).join('\n')}`, - '', - '*Lighthouse metrics:*', - '', - '| Metric | Average Value |', - '| --- | --- |', - `${Object.entries(result.summary.metrics).map(([metric, { averageNumericValue }]) => `| ${metric} | ${averageNumericValue} |`).join('\n')}`, - '', - `View the full Lighthouse report [here](${reportUrl}).`, - '', - 'Learn more about the Lighthouse metrics:', - '- [Largest Contentful Paint](https://web.dev/lighthouse-largest-contentful-paint/)', - '- [Cumulative Layout Shift](https://web.dev/cls/)', - '- [First Contentful Paint](https://web.dev/first-contentful-paint/)', - '- [Total Blocking Time](https://web.dev/lighthouse-total-blocking-time/)', - '- [Max Potential First Input Delay](https://web.dev/lighthouse-max-potential-fid/)', - '- [Time to Interactive](https://web.dev/interactive/)', - ].join('\n'); - - core.setOutput("comment", comment); - - - name: Sticky Comment on Pull Request with result - uses: marocchino/sticky-pull-request-comment@v2.7.0 - with: - header: unlighthouse - message: | - ${{ steps.format_lighthouse_score.outputs.comment }} - - - name: Sticky Comment on Pull Request with failure - uses: marocchino/sticky-pull-request-comment@v2.7.0 - if: ${{ failure() }} - with: - header: unlighthouse - message: | - ⚡️ Lighthouse report failed - - See deployment for any errors diff --git a/.github/workflows/lint-deps-check.yaml b/.github/workflows/linting.yaml similarity index 59% rename from .github/workflows/lint-deps-check.yaml rename to .github/workflows/linting.yaml index fb7a33a..4bb5963 100644 --- a/.github/workflows/lint-deps-check.yaml +++ b/.github/workflows/linting.yaml @@ -1,12 +1,6 @@ -name: Linting and Dependency Check +name: Linting -on: - push: - branches: - - main - pull_request: - branches: - - main +on: [pull_request] permissions: checks: write @@ -18,18 +12,18 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - node-version: [18, 20] + node-version: [18] steps: - name: Checkout Git repository - uses: actions/checkout@v3.5.3 + uses: actions/checkout@v4.1.1 - name: Setup pnpm - uses: pnpm/action-setup@v2.4.0 + uses: pnpm/action-setup@v3.0.0 with: - version: 8.6.2 + version: latest - name: Setup Node.js - uses: actions/setup-node@v3.7.0 + uses: actions/setup-node@v4.0.2 with: node-version: ${{ matrix.node-version }} cache: pnpm @@ -41,7 +35,4 @@ jobs: uses: wearerequired/lint-action@v2.3.0 with: eslint: true - prettier: true - - - name: Run dependency check - run: npx depcheck + prettier: true \ No newline at end of file diff --git a/.github/workflows/unlighthouse.yaml b/.github/workflows/unlighthouse.yaml new file mode 100644 index 0000000..6738805 --- /dev/null +++ b/.github/workflows/unlighthouse.yaml @@ -0,0 +1,170 @@ +name: Unlighthouse + +on: + push: + branches: main + pull_request: + +permissions: + pull-requests: write + +jobs: + unlighthouse: + runs-on: ubuntu-latest + env: + COMMENT_ID: unlighthouse-node${{matrix.node-version}} + PORT: 8000 + CLOUDFLARE_PROJECT: hellobart-unlighthouse + strategy: + matrix: + node-version: [20] + steps: + - name: Create initial comment + uses: marocchino/sticky-pull-request-comment@v2.9.0 + if: github.ref != 'refs/heads/main' + with: + header: ${{ env.COMMENT_ID }} + message: | + ⚡️ Lighthouse report + + ![loading](https://github.com/bartvdbraak/hellob.art/assets/3996360/0e00b3fc-d5f9-490b-9aa7-07cb4b59f85f) + + - name: Set variables based on trigger + run: | + if [[ ${{ github.ref == 'refs/heads/main' }} == true ]]; then + echo "CLOUDFLARE_BRANCH=main" >> $GITHUB_ENV + echo "CLOUDFLARE_URL=https://${{ env.CLOUDFLARE_PROJECT }}.pages.dev" >> $GITHUB_ENV + else + echo "CLOUDFLARE_BRANCH=pull-${{ github.event.pull_request.number }}" >> $GITHUB_ENV + echo "CLOUDFLARE_URL=https://pull-${{ github.event.pull_request.number }}.${{ env.CLOUDFLARE_PROJECT }}.pages.dev" >> $GITHUB_ENV + fi + + - name: Checkout repository + uses: actions/checkout@v4.1.1 + + - name: Setup pnpm + uses: pnpm/action-setup@v3.0.0 + with: + version: latest + + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4.0.2 + with: + node-version: ${{ matrix.node-version }} + cache: 'pnpm' + + - name: Retrieve Vercel Preview URL + uses: zentered/vercel-preview-url@v1.1.9 + id: vercel_preview_url + env: + VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }} + with: + vercel_project_id: ${{ vars.VERCEL_PROJECT_ID }} + + - name: Await Vercel Deployment + uses: UnlyEd/github-action-await-vercel@v1.2.43 + env: + VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }} + with: + deployment-url: ${{ steps.vercel_preview_url.outputs.preview_url }} + timeout: 360 + + - name: Install Dependencies + run: pnpm install -g @unlighthouse/cli puppeteer + + - name: Run Unlighthouse + run: | + unlighthouse-ci \ + --site "${{ github.ref == 'refs/heads/main' && 'https://hellob.art' || steps.vercel_preview_url.outputs.preview_url }}" \ + --reporter jsonExpanded \ + --build-static + + - name: Upload report to Cloudflare pages + uses: cloudflare/wrangler-action@v3.4.1 + with: + apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} + command: pages deploy .unlighthouse --project-name="${{ env.CLOUDFLARE_PROJECT }}" --branch=${{ env.CLOUDFLARE_BRANCH }} + + - name: Create result content + id: create_result_content + uses: actions/github-script@v7.0.1 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const fs = require('fs'); + + const result = JSON.parse(fs.readFileSync('.unlighthouse/ci-result.json', 'utf8')); + + const formatScore = score => `${Math.round(score * 100)} (${score})`; + const getEmoji = score => score >= 0.9 ? '🟢' : score >= 0.5 ? '🟠' : '🔴'; + const getColor = score => score >= 0.9 ? '4c1' : score >= 0.5 ? 'ffa400' : 'eb0f00'; + + const score = res => `${getEmoji(res)} ${formatScore(res)}`; + + const reportUrl = `${{ env.CLOUDFLARE_URL }}`; + + const comment = [ + `⚡️ Lighthouse report for the changes in this PR:`, + '| Category | Score |', + '| --- | --- |', + `| Performance | ${score(result.summary.categories.performance.averageScore)} |`, + `| Accessibility | ${score(result.summary.categories.accessibility.averageScore)} |`, + `| Best practices | ${score(result.summary.categories['best-practices'].averageScore)} |`, + `| SEO | ${score(result.summary.categories.seo.averageScore)} |`, + `| *Overall* | ${score(result.summary.score)} |`, + '', + '*Lighthouse scores for individual routes:*', + '', + '| Path | Performance | Accessibility | Best practices | SEO | Overall |', + '| --- | --- | --- | --- | --- | --- |', + `${result.routes.map(route => `| ${route.path} | ${score(route.categories.performance.score)} | ${score(route.categories.accessibility.score)} | ${score(route.categories['best-practices'].score)} | ${score(route.categories.seo.score)} | ${score(route.score)} |`).join('\n')}`, + '', + '*Lighthouse metrics:*', + '', + '| Metric | Average Value |', + '| --- | --- |', + `${Object.entries(result.summary.metrics).map(([metric, { averageNumericValue }]) => `| ${metric} | ${averageNumericValue} |`).join('\n')}`, + '', + `View the full Lighthouse report [here](${reportUrl}).`, + ].join('\n'); + + core.setOutput("comment", comment); + core.setOutput("score", `${Math.round(result.summary.score * 100)}`); + core.setOutput("scoreColor", getColor(result.summary.score)); + + - name: Update comment with result + uses: marocchino/sticky-pull-request-comment@v2.9.0 + if: github.ref != 'refs/heads/main' + with: + header: ${{ env.COMMENT_ID }} + message: ${{ steps.create_result_content.outputs.comment }} + + - name: Create Lighthouse Score badge + uses: schneegans/dynamic-badges-action@v1.7.0 + if: github.ref == 'refs/heads/main' + with: + auth: ${{ secrets.GIST_SECRET }} + gistID: 795a3d6af5b0db5754cf7279898c3c16 + filename: hellob.art-unlighthouse.json + namedLogo: Lighthouse + label: lighthouse + message: ${{ steps.create_result_content.outputs.score }} + color: ${{ steps.create_result_content.outputs.scoreColor }} + + - name: Update comment on failure + uses: marocchino/sticky-pull-request-comment@v2.9.0 + if: failure() && github.ref != 'refs/heads/main' + with: + header: ${{ env.COMMENT_ID }} + message: | + ⚡️ Lighthouse report failed + + See deployment for any errors + + - name: Update comment on cancel + uses: marocchino/sticky-pull-request-comment@v2.9.0 + if: cancelled() && github.ref != 'refs/heads/main' + with: + header: ${{ env.COMMENT_ID }} + message: | + ⚡️ Lighthouse report cancelled \ No newline at end of file diff --git a/.gitignore b/.gitignore index f3f1459..6635cf5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,40 +1,10 @@ -# 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* -pnpm-debug.log* -yarn-error.log* -pnpm-error.log* - -# local env files -.env*.local - -# vercel -.vercel - -# typescript -*.tsbuildinfo -next-env.d.ts - -# unlighthouse -.unlighthouse +node_modules +/build +/.svelte-kit +/package +.env +.env.* +!.env.example +vite.config.js.timestamp-* +vite.config.ts.timestamp-* diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..b6f27f1 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +engine-strict=true diff --git a/.prettierignore b/.prettierignore index 2689d80..cc41cea 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,15 +1,4 @@ -cache -.cache -package.json -package-lock.json +# Ignore files for PNPM, NPM and YARN pnpm-lock.yaml -public -CHANGELOG.md -.yarn -dist -node_modules -.next -build -.contentlayer -.github -.unlighthouse +package-lock.json +yarn.lock diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..7ebb855 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,15 @@ +{ + "useTabs": true, + "singleQuote": true, + "trailingComma": "none", + "printWidth": 100, + "plugins": ["prettier-plugin-svelte", "prettier-plugin-tailwindcss"], + "overrides": [ + { + "files": "*.svelte", + "options": { + "parser": "svelte" + } + } + ] +} diff --git a/LICENSE b/LICENSE index f288702..9c58981 100644 --- a/LICENSE +++ b/LICENSE @@ -631,8 +631,8 @@ to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. - - Copyright (C) + Omnidash is a self-hostable dashboard using connectors to a multitude of ticketing systems. + Copyright (C) 2024 Bart van der Braak This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: - Copyright (C) + Omnidash Copyright (C) 2024 Bart van der Braak This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. @@ -671,4 +671,4 @@ into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read -. +. \ No newline at end of file diff --git a/app/(authenticated)/(app)/DesktopSidebar.tsx b/app/(authenticated)/(app)/DesktopSidebar.tsx deleted file mode 100644 index a5efc75..0000000 --- a/app/(authenticated)/(app)/DesktopSidebar.tsx +++ /dev/null @@ -1,117 +0,0 @@ -import { Logo } from "@/components/logo"; -import { Button } from "@/components/ui/button"; -import { ScrollArea } from "@/components/ui/scroll-area"; -import { - BarChart, - Database, - FileKey, - Filter, - FormInput, - Home, -} from "lucide-react"; -import { ChannelLink } from "./channelLink"; -import { TeamSwitcher } from "./TeamSwitcher"; -import Link from "next/link"; -type Props = { - navigation: { - href: string; - external?: boolean; - label: string; - }[]; - - channels: { - name: string; - }[]; -}; - -export const DesktopSidebar: React.FC = ({ navigation, channels }) => { - return ( - - ); -}; diff --git a/app/(authenticated)/(app)/MobileSidebar.tsx b/app/(authenticated)/(app)/MobileSidebar.tsx deleted file mode 100644 index 1bb565a..0000000 --- a/app/(authenticated)/(app)/MobileSidebar.tsx +++ /dev/null @@ -1,130 +0,0 @@ -"use client"; - -import { Logo } from "@/components/logo"; -import { Button } from "@/components/ui/button"; -import { ScrollArea } from "@/components/ui/scroll-area"; -import { BarChart, Database, FileKey, Filter, Home, Menu } from "lucide-react"; -import { ChannelLink } from "./channelLink"; -import { TeamSwitcher } from "./TeamSwitcher"; -import Link from "next/link"; -import { - Sheet, - SheetContent, - SheetFooter, - SheetHeader, - SheetTitle, - SheetTrigger, -} from "@/components/ui/sheet"; -type Props = { - navigation: { - href: string; - external?: boolean; - label: string; - }[]; - - channels: { - name: string; - }[]; -}; - -export const MobileSidebar: React.FC = ({ channels }) => { - return ( -
- -
- - - -
- - - - {" "} - - Omnidash - - {/* - Make changes to your profile here. Click save when you're done. - */} - -
-
-

- {/* Events */} -

-
- - - - - - - - - - - -
-
-
-

- Events -

- -
- {channels.map((channel) => ( - - ))} -
-
-
-
- - - -
-
-
- ); -}; diff --git a/app/(authenticated)/(app)/TeamSwitcher.tsx b/app/(authenticated)/(app)/TeamSwitcher.tsx deleted file mode 100644 index e53c749..0000000 --- a/app/(authenticated)/(app)/TeamSwitcher.tsx +++ /dev/null @@ -1,94 +0,0 @@ -"use client"; - -import { - DropdownMenuTrigger, - DropdownMenu, - DropdownMenuContent, - DropdownMenuGroup, - DropdownMenuItem, -} from "@/components/ui/dropdown-menu"; -import { ChevronsUpDown, LogOut } from "lucide-react"; -import { useRouter } from "next/navigation"; -import { useState } from "react"; -import { Loading } from "@/components/loading"; - -import { - useAuth, - useOrganization, - useOrganizationList, - useUser, -} from "@clerk/clerk-react"; -import { Avatar, AvatarImage } from "@/components/ui/avatar"; -import { AvatarFallback } from "@radix-ui/react-avatar"; - -type Props = {}; - -export const TeamSwitcher: React.FC = (): JSX.Element => { - const { setActive, organizationList } = useOrganizationList(); - const { organization: currentOrg } = useOrganization(); - - const { signOut } = useAuth(); - const { user } = useUser(); - - const router = useRouter(); - - const [loading, setLoading] = useState(false); - - async function changeOrg(id: string | null) { - if (!setActive) { - return; - } - try { - setLoading(true); - await setActive({ organization: id }); - router.refresh(); - } finally { - setLoading(false); - } - } - - return ( - - {loading ? ( - - ) : ( - -
- - {user?.profileImageUrl ? ( - - ) : null} - - {(currentOrg?.slug ?? user?.username ?? "") - .slice(0, 2) - .toUpperCase() ?? "P"} - - - {currentOrg?.name ?? "Personal"} -
- {/* */} - -
- )} - - - - - - - -
- ); -}; diff --git a/app/(authenticated)/(app)/channelLink.tsx b/app/(authenticated)/(app)/channelLink.tsx deleted file mode 100644 index d1ac988..0000000 --- a/app/(authenticated)/(app)/channelLink.tsx +++ /dev/null @@ -1,28 +0,0 @@ -"use client"; - -import Link from "next/link"; -import { useSelectedLayoutSegments } from "next/navigation"; -import { Hash } from "lucide-react"; - -import { Button } from "@/components/ui/button"; - -type Props = { - href: string; - channelName: string | null; -}; - -export const ChannelLink: React.FC = ({ href, channelName }) => { - const isActive = channelName === useSelectedLayoutSegments().at(1); - return ( - - - - ); -}; diff --git a/app/(authenticated)/(app)/layout.tsx b/app/(authenticated)/(app)/layout.tsx deleted file mode 100644 index a306e5c..0000000 --- a/app/(authenticated)/(app)/layout.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { DesktopSidebar } from "./DesktopSidebar"; -import { MobileSidebar } from "./MobileSidebar"; -import { getTenantId } from "@/lib/auth"; -import { Fragment } from "react"; - -interface LayoutProps { - children: React.ReactNode; -} - -export default async function Layout({ children }: LayoutProps) { - const tenantId = getTenantId(); - - return ( - - - - - -
{children}
-
- ); -} diff --git a/app/(authenticated)/(app)/overview/loading.tsx b/app/(authenticated)/(app)/overview/loading.tsx deleted file mode 100644 index f37ff65..0000000 --- a/app/(authenticated)/(app)/overview/loading.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import { Loading as Spinner } from "@/components/loading"; - -export default function Loading() { - return ( -
- -
- ); -} diff --git a/app/(authenticated)/(app)/overview/page.tsx b/app/(authenticated)/(app)/overview/page.tsx deleted file mode 100644 index 3e6674b..0000000 --- a/app/(authenticated)/(app)/overview/page.tsx +++ /dev/null @@ -1,111 +0,0 @@ -import { cn } from "@/lib/utils"; - -export default async function Page(_props: { params: { tenantSlug: string } }) { - const stats: { - label: string; - value: string; - }[] = [ - { - label: "Total Channels", - value: "0", - }, - { - label: "Total Events (7 days)", - value: "0", - }, - ]; - return ( -
-
- {/* Stats */} -
-
-
-
- {/*
-
-
*/} -

- - {"Personal Account"} - -

-
- {/*

{channel.description}

*/} -
-
- test -
-
-
= 4, - } - )} - > - {" "} - {stats.map((stat, statIdx) => ( -
-
- {stat.label} -
- {/*
- {stat.change} -
*/} -
- {stat.value} -
-
- ))} -
-
-
- -
- {/* Recent activity table */} -
-
-

- Recent events -

-
-
-
-
- - - - - - - - - -
EventContentMore details
-
-
-
-
-
-
- ); -} diff --git a/app/(authenticated)/layout.tsx b/app/(authenticated)/layout.tsx deleted file mode 100644 index e6eed60..0000000 --- a/app/(authenticated)/layout.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import { Particles } from "@/components/landing/particles"; -import { - ClerkProvider, - SignIn, - SignedIn, - SignedOut, -} from "@clerk/nextjs/app-beta"; - -export default function AppLayout({ children }: { children: React.ReactNode }) { - return ( - - {children} - -
- - - -
-
-
- ); -} diff --git a/app/(landing)/css/additional-styles/theme.css b/app/(landing)/css/additional-styles/theme.css deleted file mode 100644 index edcd5c7..0000000 --- a/app/(landing)/css/additional-styles/theme.css +++ /dev/null @@ -1,82 +0,0 @@ -/* Custom AOS distance */ -@media screen { - html:not(.no-js) [data-aos="fade-up"] { - -webkit-transform: translate3d(0, 14px, 0); - transform: translate3d(0, 14px, 0); - } - - html:not(.no-js) [data-aos="fade-down"] { - -webkit-transform: translate3d(0, -14px, 0); - transform: translate3d(0, -14px, 0); - } - - html:not(.no-js) [data-aos="fade-right"] { - -webkit-transform: translate3d(-14px, 0, 0); - transform: translate3d(-14px, 0, 0); - } - - html:not(.no-js) [data-aos="fade-left"] { - -webkit-transform: translate3d(14px, 0, 0); - transform: translate3d(14px, 0, 0); - } - - html:not(.no-js) [data-aos="fade-up-right"] { - -webkit-transform: translate3d(-14px, 14px, 0); - transform: translate3d(-14px, 14px, 0); - } - - html:not(.no-js) [data-aos="fade-up-left"] { - -webkit-transform: translate3d(14px, 14px, 0); - transform: translate3d(14px, 14px, 0); - } - - html:not(.no-js) [data-aos="fade-down-right"] { - -webkit-transform: translate3d(-14px, -14px, 0); - transform: translate3d(-14px, -14px, 0); - } - - html:not(.no-js) [data-aos="fade-down-left"] { - -webkit-transform: translate3d(14px, -14px, 0); - transform: translate3d(14px, -14px, 0); - } - - html:not(.no-js) [data-aos="zoom-in-up"] { - -webkit-transform: translate3d(0, 14px, 0) scale(0.6); - transform: translate3d(0, 14px, 0) scale(0.6); - } - - html:not(.no-js) [data-aos="zoom-in-down"] { - -webkit-transform: translate3d(0, -14px, 0) scale(0.6); - transform: translate3d(0, -14px, 0) scale(0.6); - } - - html:not(.no-js) [data-aos="zoom-in-right"] { - -webkit-transform: translate3d(-14px, 0, 0) scale(0.6); - transform: translate3d(-14px, 0, 0) scale(0.6); - } - - html:not(.no-js) [data-aos="zoom-in-left"] { - -webkit-transform: translate3d(14px, 0, 0) scale(0.6); - transform: translate3d(14px, 0, 0) scale(0.6); - } - - html:not(.no-js) [data-aos="zoom-out-up"] { - -webkit-transform: translate3d(0, 14px, 0) scale(1.2); - transform: translate3d(0, 14px, 0) scale(1.2); - } - - html:not(.no-js) [data-aos="zoom-out-down"] { - -webkit-transform: translate3d(0, -14px, 0) scale(1.2); - transform: translate3d(0, -14px, 0) scale(1.2); - } - - html:not(.no-js) [data-aos="zoom-out-right"] { - -webkit-transform: translate3d(-14px, 0, 0) scale(1.2); - transform: translate3d(-14px, 0, 0) scale(1.2); - } - - html:not(.no-js) [data-aos="zoom-out-left"] { - -webkit-transform: translate3d(14px, 0, 0) scale(1.2); - transform: translate3d(14px, 0, 0) scale(1.2); - } -} diff --git a/app/(landing)/css/style.css b/app/(landing)/css/style.css deleted file mode 100644 index 2f68700..0000000 --- a/app/(landing)/css/style.css +++ /dev/null @@ -1,13 +0,0 @@ -@import "tailwindcss/base"; -@import "tailwindcss/components"; - -@import "additional-styles/theme.css"; - -@import "tailwindcss/utilities"; - -/* Additional Tailwind directives: https://tailwindcss.com/docs/functions-and-directives/#responsive */ -@layer utilities { - .rtl { - direction: rtl; - } -} diff --git a/app/(landing)/layout.tsx b/app/(landing)/layout.tsx deleted file mode 100644 index 614158d..0000000 --- a/app/(landing)/layout.tsx +++ /dev/null @@ -1,66 +0,0 @@ -"use client"; - -import { useEffect } from "react"; - -import AOS from "aos"; -import "aos/dist/aos.css"; -import "./css/style.css"; - -import { Header } from "@/components/landing/ui/header"; -import Link from "next/link"; - -export default function DefaultLayout({ - children, -}: { - children: React.ReactNode; -}) { - useEffect(() => { - AOS.init({ - once: true, - disable: "phone", - duration: 1000, - easing: "ease-out-cubic", - }); - }); - - return ( - <> -
- -
{children}
- -
- -
-
-
- - Github - - - - -
-

- © {new Date().getUTCFullYear()} All rights reserved. -

-
-
-
- - ); -} diff --git a/app/(landing)/page.tsx b/app/(landing)/page.tsx deleted file mode 100644 index e669d04..0000000 --- a/app/(landing)/page.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import { Hero } from "@/components/landing/hero"; -import { Features } from "@/components/landing/features"; -import { Cta } from "@/components/landing/cta"; - -export default function Page() { - return ( -
- - - -
- ); -} diff --git a/app/layout.tsx b/app/layout.tsx deleted file mode 100644 index ea03a49..0000000 --- a/app/layout.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import { Inter } from "next/font/google"; -import LocalFont from "next/font/local"; - -import { ThemeProvider } from "@/components/theme-provider"; -import "tailwindcss/tailwind.css"; -import { ToastProvider } from "../toast-provider"; - -import { Metadata } from "next"; - -export const metadata: Metadata = { - title: { - default: "Omnidash", - template: "%s | Omnidash", - }, - description: "Open Source Multi-client Ticket Dashboard", - metadataBase: new URL("https://omnidash.io"), - openGraph: { - title: "Omnidash", - description: "Open Source Multi-client Ticket Dashboard", - url: "https://omnidash.io", - siteName: "omnidash.io", - locale: "en-US", - type: "website", - }, - robots: { - index: true, - follow: true, - googleBot: { - index: true, - follow: true, - "max-video-preview": -1, - "max-image-preview": "large", - "max-snippet": -1, - }, - }, - twitter: { - title: "Omnidash", - card: "summary_large_image", - }, - icons: { - shortcut: "/favicon.png", - }, -}; - -interface RootLayoutProps { - children: React.ReactNode; -} - -const inter = Inter({ - subsets: ["latin"], - variable: "--font-inter", -}); - -const calSans = LocalFont({ - src: "../public/fonts/CalSans-SemiBold.ttf", - variable: "--font-calsans", -}); -interface RootLayoutProps { - children: React.ReactNode; -} - -export default async function RootLayout({ children }: RootLayoutProps) { - return ( - <> - - - - - {children} - - - - - ); -} diff --git a/backend/.gitignore b/backend/.gitignore new file mode 100644 index 0000000..6937217 --- /dev/null +++ b/backend/.gitignore @@ -0,0 +1 @@ +/pb_data \ No newline at end of file diff --git a/backend/LICENSE.md b/backend/LICENSE.md new file mode 100644 index 0000000..e3b8465 --- /dev/null +++ b/backend/LICENSE.md @@ -0,0 +1,17 @@ +The MIT License (MIT) +Copyright (c) 2022 - present, Gani Georgiev + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software +and associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/backend/pb_migrations/1706833675_updated_users.js b/backend/pb_migrations/1706833675_updated_users.js new file mode 100644 index 0000000..17dd6b3 --- /dev/null +++ b/backend/pb_migrations/1706833675_updated_users.js @@ -0,0 +1,33 @@ +/// +migrate((db) => { + const dao = new Dao(db) + const collection = dao.findCollectionByNameOrId("_pb_users_auth_") + + // add + collection.schema.addField(new SchemaField({ + "system": false, + "id": "rncq13xn", + "name": "appearance_mode", + "type": "select", + "required": false, + "presentable": false, + "unique": false, + "options": { + "maxSelect": 1, + "values": [ + "light", + "dark" + ] + } + })) + + return dao.saveCollection(collection) +}, (db) => { + const dao = new Dao(db) + const collection = dao.findCollectionByNameOrId("_pb_users_auth_") + + // remove + collection.schema.removeField("rncq13xn") + + return dao.saveCollection(collection) +}) diff --git a/backend/pb_migrations/1706834435_updated_users.js b/backend/pb_migrations/1706834435_updated_users.js new file mode 100644 index 0000000..f6eb82e --- /dev/null +++ b/backend/pb_migrations/1706834435_updated_users.js @@ -0,0 +1,49 @@ +/// +migrate((db) => { + const dao = new Dao(db) + const collection = dao.findCollectionByNameOrId("_pb_users_auth_") + + // update + collection.schema.addField(new SchemaField({ + "system": false, + "id": "rncq13xn", + "name": "appearance_mode", + "type": "select", + "required": false, + "presentable": false, + "unique": false, + "options": { + "maxSelect": 1, + "values": [ + "light", + "dark", + "system" + ] + } + })) + + return dao.saveCollection(collection) +}, (db) => { + const dao = new Dao(db) + const collection = dao.findCollectionByNameOrId("_pb_users_auth_") + + // update + collection.schema.addField(new SchemaField({ + "system": false, + "id": "rncq13xn", + "name": "appearance_mode", + "type": "select", + "required": false, + "presentable": false, + "unique": false, + "options": { + "maxSelect": 1, + "values": [ + "light", + "dark" + ] + } + })) + + return dao.saveCollection(collection) +}) diff --git a/backend/pb_migrations/1707253263_updated_users.js b/backend/pb_migrations/1707253263_updated_users.js new file mode 100644 index 0000000..feced8f --- /dev/null +++ b/backend/pb_migrations/1707253263_updated_users.js @@ -0,0 +1,50 @@ +/// +migrate((db) => { + const dao = new Dao(db) + const collection = dao.findCollectionByNameOrId("_pb_users_auth_") + + // update + collection.schema.addField(new SchemaField({ + "system": false, + "id": "rncq13xn", + "name": "appearanceMode", + "type": "select", + "required": false, + "presentable": false, + "unique": false, + "options": { + "maxSelect": 1, + "values": [ + "light", + "dark", + "system" + ] + } + })) + + return dao.saveCollection(collection) +}, (db) => { + const dao = new Dao(db) + const collection = dao.findCollectionByNameOrId("_pb_users_auth_") + + // update + collection.schema.addField(new SchemaField({ + "system": false, + "id": "rncq13xn", + "name": "appearance_mode", + "type": "select", + "required": false, + "presentable": false, + "unique": false, + "options": { + "maxSelect": 1, + "values": [ + "light", + "dark", + "system" + ] + } + })) + + return dao.saveCollection(collection) +}) diff --git a/backend/pocketbase b/backend/pocketbase new file mode 100755 index 0000000..81c0939 Binary files /dev/null and b/backend/pocketbase differ diff --git a/components.json b/components.json new file mode 100644 index 0000000..a032c44 --- /dev/null +++ b/components.json @@ -0,0 +1,13 @@ +{ + "$schema": "https://shadcn-svelte.com/schema.json", + "style": "new-york", + "tailwind": { + "config": "tailwind.config.js", + "css": "src/app.pcss", + "baseColor": "neutral" + }, + "aliases": { + "components": "$lib/components", + "utils": "$lib/utils" + } +} diff --git a/components/icons.tsx b/components/icons.tsx deleted file mode 100644 index a28a128..0000000 --- a/components/icons.tsx +++ /dev/null @@ -1,117 +0,0 @@ -import { - AlertTriangle, - ArrowRight, - Check, - ChevronLeft, - ChevronRight, - Circle, - ClipboardCheck, - Copy, - CreditCard, - File, - FileText, - HelpCircle, - Image, - Laptop, - Loader2, - LucideProps, - Moon, - MoreVertical, - Pizza, - Plus, - Settings, - SunMedium, - Trash, - Twitter, - User, - X, - type Icon as LucideIcon, -} from "lucide-react"; - -export type Icon = LucideIcon; - -export const Icons = { - logo: (props: LucideProps) => ( - - - - ), - close: X, - spinner: Loader2, - chevronLeft: ChevronLeft, - chevronRight: ChevronRight, - trash: Trash, - post: FileText, - page: File, - media: Image, - settings: Settings, - billing: CreditCard, - ellipsis: MoreVertical, - add: Plus, - warning: AlertTriangle, - user: User, - arrowRight: ArrowRight, - help: HelpCircle, - pizza: Pizza, - twitter: Twitter, - check: Check, - copy: Copy, - copyDone: ClipboardCheck, - sun: SunMedium, - moon: Moon, - laptop: Laptop, - gitHub: (props: LucideProps) => ( - - - - ), - radix: (props: LucideProps) => ( - - - - - - ), - npm: (props: LucideProps) => ( - - - - ), - yarn: (props: LucideProps) => ( - - - - ), - pnpm: (props: LucideProps) => ( - - - - ), - react: (props: LucideProps) => ( - - - - ), - tailwind: (props: LucideProps) => ( - - - - ), -}; diff --git a/components/landing/cta.tsx b/components/landing/cta.tsx deleted file mode 100644 index c23b34f..0000000 --- a/components/landing/cta.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import { ArrowRight } from "lucide-react"; -import Link from "next/link"; - -export const Cta: React.FC = () => { - return ( -
-
-
- {/* Radial gradient */} -
- ); -}; diff --git a/components/landing/features.tsx b/components/landing/features.tsx deleted file mode 100644 index 3c06cc3..0000000 --- a/components/landing/features.tsx +++ /dev/null @@ -1,110 +0,0 @@ -import Image from "next/image"; -import GlowTop from "@/public/images/glow-top.svg"; -import { Eye, Unplug, Compass, Zap } from "lucide-react"; - -export const Features: React.FC = () => { - const features = [ - { - icon: Unplug, - name: "Effortless Consolidation", - description: - "Consolidate all tickets from multiple platforms and clients effortlessly", - }, - { - icon: Eye, - name: "Unparalleled Visibility", - description: - "Gain complete control and visibility over your ticketing operations", - }, - { - icon: Compass, - name: "Intuitive Navigation", - description: - "Seamlessly navigate and find tickets with smart filters and advanced search", - }, - { - icon: Zap, - name: "Enhanced Efficiency", - description: - "Maximize productivity and resource allocation in ticket management", - }, - ]; - return ( -
-
- - -
-
- {/* Section content */} -
- {/* Content */} -
- {/* Content #1 */} -
-
- Centralized view of all tickets -
-
-

- Reduce Context Switching -

-

- Empower your operations teams with by consolidating all ticket - information in one place. Seamlessly filter, sort, and - customize ticket views to meet their unique needs. -

-
- {features.map((feature) => ( -
-
- -

- {feature.name} -

-
-

- {feature.description} -

-
- ))} -
-
- -
-
- App screenshot -
-
-
-
-
-
-
- ); -}; diff --git a/components/landing/hero.tsx b/components/landing/hero.tsx deleted file mode 100644 index 622ef58..0000000 --- a/components/landing/hero.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import { Particles } from "./particles"; -import ReactWrapBalancer from "react-wrap-balancer"; -import Link from "next/link"; -import { ArrowRight } from "lucide-react"; - -export const Hero: React.FC = () => { - return ( -
-
- {/* Particles animation */} - - -
- {/* Hero content */} -
-
-
- - - Omnidash is Open Source{" "} - - -> - - - -
-
-

- - One Dashboard, Countless Solutions - -

-

- Tame ticket overload and keep your operation teams sane -

-
- - Get Started{" "} - - - - - Star on GitHub - -
-
-
-
-
- ); -}; diff --git a/components/landing/particles.tsx b/components/landing/particles.tsx deleted file mode 100644 index 9dbb50e..0000000 --- a/components/landing/particles.tsx +++ /dev/null @@ -1,258 +0,0 @@ -/* eslint-disable react-hooks/exhaustive-deps */ -"use client"; - -import React, { useRef, useEffect } from "react"; -import MousePosition from "./utils/mouse-position"; - -interface ParticlesProps { - className?: string; - quantity?: number; - staticity?: number; - ease?: number; - refresh?: boolean; - color?: string; - vx?: number; - vy?: number; -} -function hexToRgb(hex: string): number[] { - // Remove the "#" character from the beginning of the hex color code - hex = hex.replace("#", ""); - - // Convert the hex color code to an integer - const hexInt = parseInt(hex, 16); - - // Extract the red, green, and blue components from the hex color code - const red = (hexInt >> 16) & 255; - const green = (hexInt >> 8) & 255; - const blue = hexInt & 255; - - // Return an array of the RGB values - return [red, green, blue]; -} - -export const Particles: React.FC = ({ - className = "", - quantity = 30, - staticity = 50, - ease = 50, - refresh = false, - color = "#ffffff", - vx = 0, - vy = 0, -}) => { - const canvasRef = useRef(null); - const canvasContainerRef = useRef(null); - const context = useRef(null); - const circles = useRef([]); - const mousePosition = MousePosition(); - const mouse = useRef<{ x: number; y: number }>({ x: 0, y: 0 }); - const canvasSize = useRef<{ w: number; h: number }>({ w: 0, h: 0 }); - const dpr = typeof window !== "undefined" ? window.devicePixelRatio : 1; - - useEffect(() => { - if (canvasRef.current) { - context.current = canvasRef.current.getContext("2d"); - } - initCanvas(); - animate(); - window.addEventListener("resize", initCanvas); - - return () => { - window.removeEventListener("resize", initCanvas); - }; - }, []); - - useEffect(() => { - onMouseMove(); - }, [mousePosition.x, mousePosition.y]); - - useEffect(() => { - initCanvas(); - }, [refresh]); - - const initCanvas = () => { - resizeCanvas(); - drawParticles(); - }; - - const onMouseMove = () => { - if (canvasRef.current) { - const rect = canvasRef.current.getBoundingClientRect(); - const { w, h } = canvasSize.current; - const x = mousePosition.x - rect.left - w / 2; - const y = mousePosition.y - rect.top - h / 2; - const inside = x < w / 2 && x > -w / 2 && y < h / 2 && y > -h / 2; - if (inside) { - mouse.current.x = x; - mouse.current.y = y; - } - } - }; - - type Circle = { - x: number; - y: number; - translateX: number; - translateY: number; - size: number; - alpha: number; - targetAlpha: number; - dx: number; - dy: number; - magnetism: number; - }; - - const resizeCanvas = () => { - if (canvasContainerRef.current && canvasRef.current && context.current) { - circles.current.length = 0; - canvasSize.current.w = canvasContainerRef.current.offsetWidth; - canvasSize.current.h = canvasContainerRef.current.offsetHeight; - canvasRef.current.width = canvasSize.current.w * dpr; - canvasRef.current.height = canvasSize.current.h * dpr; - canvasRef.current.style.width = `${canvasSize.current.w}px`; - canvasRef.current.style.height = `${canvasSize.current.h}px`; - context.current.scale(dpr, dpr); - } - }; - - const circleParams = (): Circle => { - const x = Math.floor(Math.random() * canvasSize.current.w); - const y = Math.floor(Math.random() * canvasSize.current.h); - const translateX = 0; - const translateY = 0; - const size = Math.floor(Math.random() * 2) + 1; - const alpha = 0; - const targetAlpha = parseFloat((Math.random() * 0.6 + 0.1).toFixed(1)); - const dx = (Math.random() - 0.5) * 0.2; - const dy = (Math.random() - 0.5) * 0.2; - const magnetism = 0.1 + Math.random() * 4; - return { - x, - y, - translateX, - translateY, - size, - alpha, - targetAlpha, - dx, - dy, - magnetism, - }; - }; - - const rgb = hexToRgb(color); - - const drawCircle = (circle: Circle, update = false) => { - if (context.current) { - const { x, y, translateX, translateY, size, alpha } = circle; - context.current.translate(translateX, translateY); - context.current.beginPath(); - context.current.arc(x, y, size, 0, 2 * Math.PI); - context.current.fillStyle = `rgba(${rgb.join(", ")}, ${alpha})`; - context.current.fill(); - context.current.setTransform(dpr, 0, 0, dpr, 0, 0); - - if (!update) { - circles.current.push(circle); - } - } - }; - - const clearContext = () => { - if (context.current) { - context.current.clearRect( - 0, - 0, - canvasSize.current.w, - canvasSize.current.h - ); - } - }; - - const drawParticles = () => { - clearContext(); - const particleCount = quantity; - for (let i = 0; i < particleCount; i++) { - const circle = circleParams(); - drawCircle(circle); - } - }; - - const remapValue = ( - value: number, - start1: number, - end1: number, - start2: number, - end2: number - ): number => { - const remapped = - ((value - start1) * (end2 - start2)) / (end1 - start1) + start2; - return remapped > 0 ? remapped : 0; - }; - - const animate = () => { - clearContext(); - circles.current.forEach((circle: Circle, i: number) => { - // Handle the alpha value - const edge = [ - circle.x + circle.translateX - circle.size, // distance from left edge - canvasSize.current.w - circle.x - circle.translateX - circle.size, // distance from right edge - circle.y + circle.translateY - circle.size, // distance from top edge - canvasSize.current.h - circle.y - circle.translateY - circle.size, // distance from bottom edge - ]; - const closestEdge = edge.reduce((a, b) => Math.min(a, b)); - const remapClosestEdge = parseFloat( - remapValue(closestEdge, 0, 20, 0, 1).toFixed(2) - ); - if (remapClosestEdge > 1) { - circle.alpha += 0.02; - if (circle.alpha > circle.targetAlpha) { - circle.alpha = circle.targetAlpha; - } - } else { - circle.alpha = circle.targetAlpha * remapClosestEdge; - } - circle.x += circle.dx + vx; - circle.y += circle.dy + vy; - circle.translateX += - (mouse.current.x / (staticity / circle.magnetism) - circle.translateX) / - ease; - circle.translateY += - (mouse.current.y / (staticity / circle.magnetism) - circle.translateY) / - ease; - // circle gets out of the canvas - if ( - circle.x < -circle.size || - circle.x > canvasSize.current.w + circle.size || - circle.y < -circle.size || - circle.y > canvasSize.current.h + circle.size - ) { - // remove the circle from the array - circles.current.splice(i, 1); - // create a new circle - const newCircle = circleParams(); - drawCircle(newCircle); - // update the circle position - } else { - drawCircle( - { - ...circle, - x: circle.x, - y: circle.y, - translateX: circle.translateX, - translateY: circle.translateY, - alpha: circle.alpha, - }, - true - ); - } - }); - window.requestAnimationFrame(animate); - }; - - return ( - - ); -}; diff --git a/components/landing/ui/header.tsx b/components/landing/ui/header.tsx deleted file mode 100644 index 711863a..0000000 --- a/components/landing/ui/header.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import Link from "next/link"; -import { Logo } from "@/components/logo"; - -export const Header: React.FC = () => { - return ( -
-
-
- - - - - {/* Desktop navigation */} - -
-
-
- ); -}; diff --git a/components/landing/utils/mouse-position.tsx b/components/landing/utils/mouse-position.tsx deleted file mode 100644 index b3f827d..0000000 --- a/components/landing/utils/mouse-position.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import { useState, useEffect } from "react"; - -interface MousePosition { - x: number; - y: number; -} - -export default function useMousePosition(): MousePosition { - const [mousePosition, setMousePosition] = useState({ - x: 0, - y: 0, - }); - - useEffect(() => { - const handleMouseMove = (event: MouseEvent) => { - setMousePosition({ x: event.clientX, y: event.clientY }); - }; - - window.addEventListener("mousemove", handleMouseMove); - - return () => { - window.removeEventListener("mousemove", handleMouseMove); - }; - }, []); - - return mousePosition; -} diff --git a/components/loading.tsx b/components/loading.tsx deleted file mode 100644 index d86f385..0000000 --- a/components/loading.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import React, { SVGProps } from "react"; - -export function Loading({ - width = 24, - height = 24, - dur = "0.75s", -}: SVGProps): JSX.Element { - return ( - - - - - - - - - - - - ); -} diff --git a/components/logo.tsx b/components/logo.tsx deleted file mode 100644 index 92162ee..0000000 --- a/components/logo.tsx +++ /dev/null @@ -1,24 +0,0 @@ -type Props = { - className?: string; -}; -export const Logo: React.FC = ({ className }) => { - return ( - - - - - - - ); -}; diff --git a/components/mobile-nav.tsx b/components/mobile-nav.tsx deleted file mode 100644 index 8000e78..0000000 --- a/components/mobile-nav.tsx +++ /dev/null @@ -1,79 +0,0 @@ -"use client"; - -import * as React from "react"; -import Link from "next/link"; - -// import { docsConfig } from "@/config/docs" -import { cn } from "@/lib/utils"; -import { Icons } from "@/components/icons"; -import { Button } from "@/components/ui/button"; -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuGroup, - DropdownMenuItem, - DropdownMenuLabel, - DropdownMenuSeparator, - DropdownMenuTrigger, -} from "@/components/ui/dropdown-menu"; -import { ScrollArea } from "@/components/ui/scroll-area"; - -export function MobileNav() { - return ( - - - - - - - - Omnidash - - - - - {/* {docsConfig.sidebarNav?.map( - (item, index) => - item.href && ( - - {item.title} - - ) - )} - {docsConfig.sidebarNav.map((item, index) => ( - - - {item.title} - - {item?.items?.length && - item.items.map((item) => ( - - {item.href ? ( - {item.title} - ) : ( - item.title - )} - - ))} - - ))} */} - - - - ); -} diff --git a/components/page-header.tsx b/components/page-header.tsx deleted file mode 100644 index 1997a71..0000000 --- a/components/page-header.tsx +++ /dev/null @@ -1,27 +0,0 @@ -type Props = { - title: string; - description?: string; - actions?: React.ReactNode[]; -}; - -export const PageHeader: React.FC = ({ - title, - description, - actions, -}) => { - return ( -
-
-

{title}

-

- {description} -

-
-
    - {(actions ?? []).map((action, i) => ( -
  • {action}
  • - ))} -
-
- ); -}; diff --git a/components/theme-provider.tsx b/components/theme-provider.tsx deleted file mode 100644 index b4e8e4f..0000000 --- a/components/theme-provider.tsx +++ /dev/null @@ -1,9 +0,0 @@ -"use client"; - -import * as React from "react"; -import { ThemeProvider as NextThemesProvider } from "next-themes"; -import { ThemeProviderProps } from "next-themes/dist/types"; - -export function ThemeProvider({ children, ...props }: ThemeProviderProps) { - return {children}; -} diff --git a/components/ui/avatar.tsx b/components/ui/avatar.tsx deleted file mode 100644 index f5eba05..0000000 --- a/components/ui/avatar.tsx +++ /dev/null @@ -1,50 +0,0 @@ -"use client"; - -import * as React from "react"; -import * as AvatarPrimitive from "@radix-ui/react-avatar"; - -import { cn } from "@/lib/utils"; - -const Avatar = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); -Avatar.displayName = AvatarPrimitive.Root.displayName; - -const AvatarImage = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); -AvatarImage.displayName = AvatarPrimitive.Image.displayName; - -const AvatarFallback = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); -AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName; - -export { Avatar, AvatarImage, AvatarFallback }; diff --git a/components/ui/button.tsx b/components/ui/button.tsx deleted file mode 100644 index aa257ef..0000000 --- a/components/ui/button.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import * as React from "react"; -import { VariantProps, cva } from "class-variance-authority"; - -import { cn } from "@/lib/utils"; - -const buttonVariants = cva( - "inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus:outline-none dark:hover:bg-zinc-800 dark:hover:text-zinc-100 disabled:opacity-50 disabled:pointer-events-none data-[state=open]:bg-zinc-100 dark:data-[state=open]:bg-zinc-800", - { - variants: { - variant: { - default: - "bg-zinc-900 text-white hover:bg-zinc-700 dark:bg-zinc-50 dark:text-zinc-900", - destructive: - "bg-red-500 text-white hover:bg-red-600 dark:hover:bg-red-600", - outline: - "bg-transparent border border-zinc-200 hover:bg-zinc-100 dark:border-zinc-700 dark:text-zinc-100", - subtle: - "bg-zinc-100 text-zinc-900 hover:bg-zinc-200 dark:bg-zinc-800 dark:text-zinc-100", - ghost: - "bg-transparent hover:bg-zinc-100 dark:hover:bg-zinc-800 dark:text-zinc-100 dark:hover:text-zinc-100 data-[state=open]:bg-transparent dark:data-[state=open]:bg-transparent", - link: "bg-transparent dark:bg-transparent underline-offset-4 hover:underline text-zinc-900 dark:text-zinc-100 hover:bg-transparent dark:hover:bg-transparent", - }, - size: { - square: "h-10 w-10", - default: "h-10 py-2 px-4", - sm: "h-9 px-2 rounded-md", - lg: "h-11 px-8 rounded-md", - }, - }, - defaultVariants: { - variant: "default", - size: "default", - }, - } -); - -export interface ButtonProps - extends React.ButtonHTMLAttributes, - VariantProps {} - -const Button = React.forwardRef( - ({ className, variant, size, ...props }, ref) => { - return ( - + + + + + diff --git a/src/lib/components/site/icons/apple.svelte b/src/lib/components/site/icons/apple.svelte new file mode 100644 index 0000000..159af9e --- /dev/null +++ b/src/lib/components/site/icons/apple.svelte @@ -0,0 +1,13 @@ + diff --git a/src/lib/components/site/icons/bitbucket.svelte b/src/lib/components/site/icons/bitbucket.svelte new file mode 100644 index 0000000..47c4a61 --- /dev/null +++ b/src/lib/components/site/icons/bitbucket.svelte @@ -0,0 +1,13 @@ + diff --git a/src/lib/components/site/icons/discord.svelte b/src/lib/components/site/icons/discord.svelte new file mode 100644 index 0000000..b2e6ba4 --- /dev/null +++ b/src/lib/components/site/icons/discord.svelte @@ -0,0 +1,13 @@ + diff --git a/src/lib/components/site/icons/facebook.svelte b/src/lib/components/site/icons/facebook.svelte new file mode 100644 index 0000000..26feaea --- /dev/null +++ b/src/lib/components/site/icons/facebook.svelte @@ -0,0 +1,13 @@ + diff --git a/src/lib/components/site/icons/github.svelte b/src/lib/components/site/icons/github.svelte new file mode 100644 index 0000000..47cce76 --- /dev/null +++ b/src/lib/components/site/icons/github.svelte @@ -0,0 +1,4 @@ + + + + diff --git a/src/lib/components/site/icons/gitlab.svelte b/src/lib/components/site/icons/gitlab.svelte new file mode 100644 index 0000000..690894c --- /dev/null +++ b/src/lib/components/site/icons/gitlab.svelte @@ -0,0 +1,13 @@ + diff --git a/src/lib/components/site/icons/google.svelte b/src/lib/components/site/icons/google.svelte new file mode 100644 index 0000000..a09496c --- /dev/null +++ b/src/lib/components/site/icons/google.svelte @@ -0,0 +1,13 @@ + diff --git a/src/lib/components/site/icons/index.ts b/src/lib/components/site/icons/index.ts new file mode 100644 index 0000000..b1ab459 --- /dev/null +++ b/src/lib/components/site/icons/index.ts @@ -0,0 +1,35 @@ +import type { Icon as LucideIcon } from 'lucide-svelte'; +import { ArrowRight, Loader2 } from 'lucide-svelte'; +import { GithubLogo, VercelLogo, LinkedinLogo } from 'radix-icons-svelte'; +import Logo from './logo.svelte'; +import Svelte from './svelte.svelte'; +import MicrosoftLogo from './microsoft.svelte'; +import AppleLogo from './apple.svelte'; +import GitLabLogo from './gitlab.svelte'; +import BitBucketLogo from './bitbucket.svelte'; +import DiscordLogo from './discord.svelte'; +import FacebookLogo from './facebook.svelte'; +import GoogleLogo from './google.svelte'; +import InstagramLogo from './instagram.svelte'; +import TwitterLogo from './twitter.svelte'; + +export type Icon = LucideIcon; + +export const Icons = { + logo: Logo, + gitHub: GithubLogo, + microsoft: MicrosoftLogo, + svelte: Svelte, + vercel: VercelLogo, + linkedIn: LinkedinLogo, + spinner: Loader2, + arrowRight: ArrowRight, + apple: AppleLogo, + bitBucket: BitBucketLogo, + gitLab: GitLabLogo, + discord: DiscordLogo, + facebook: FacebookLogo, + google: GoogleLogo, + instagram: InstagramLogo, + twitter: TwitterLogo +}; diff --git a/src/lib/components/site/icons/instagram.svelte b/src/lib/components/site/icons/instagram.svelte new file mode 100644 index 0000000..4e4c286 --- /dev/null +++ b/src/lib/components/site/icons/instagram.svelte @@ -0,0 +1,13 @@ + diff --git a/src/lib/components/site/icons/logo.svelte b/src/lib/components/site/icons/logo.svelte new file mode 100644 index 0000000..199d086 --- /dev/null +++ b/src/lib/components/site/icons/logo.svelte @@ -0,0 +1,24 @@ + + + + + + + + diff --git a/src/lib/components/site/icons/microsoft.svelte b/src/lib/components/site/icons/microsoft.svelte new file mode 100644 index 0000000..7f0a16c --- /dev/null +++ b/src/lib/components/site/icons/microsoft.svelte @@ -0,0 +1,12 @@ + diff --git a/src/lib/components/site/icons/svelte.svelte b/src/lib/components/site/icons/svelte.svelte new file mode 100644 index 0000000..a0694cf --- /dev/null +++ b/src/lib/components/site/icons/svelte.svelte @@ -0,0 +1,15 @@ + diff --git a/src/lib/components/site/icons/twitter.svelte b/src/lib/components/site/icons/twitter.svelte new file mode 100644 index 0000000..3655795 --- /dev/null +++ b/src/lib/components/site/icons/twitter.svelte @@ -0,0 +1,13 @@ + diff --git a/src/lib/components/site/icons/vercel.svelte b/src/lib/components/site/icons/vercel.svelte new file mode 100644 index 0000000..b406fae --- /dev/null +++ b/src/lib/components/site/icons/vercel.svelte @@ -0,0 +1,4 @@ + + + + diff --git a/src/lib/components/site/index.ts b/src/lib/components/site/index.ts new file mode 100644 index 0000000..3c1fc16 --- /dev/null +++ b/src/lib/components/site/index.ts @@ -0,0 +1,9 @@ +export { default as Metadata } from './metadata.svelte'; +export { default as SiteFooter } from './site-footer.svelte'; +export { default as SiteNavBar } from './site-navbar.svelte'; +export { default as TailwindIndicator } from './tailwind-indicator.svelte'; +export { default as ModeToggle } from './mode-toggle.svelte'; +export { default as Particles } from './particles.svelte'; + +export * from './icons'; +export * from './nav'; diff --git a/src/lib/components/site/metadata.svelte b/src/lib/components/site/metadata.svelte new file mode 100644 index 0000000..03ff039 --- /dev/null +++ b/src/lib/components/site/metadata.svelte @@ -0,0 +1,36 @@ + + + + {title} + + + + + + + + + + + + + + + + + + + + + diff --git a/src/lib/components/site/mode-toggle.svelte b/src/lib/components/site/mode-toggle.svelte new file mode 100644 index 0000000..afffe5c --- /dev/null +++ b/src/lib/components/site/mode-toggle.svelte @@ -0,0 +1,25 @@ + + + + + + + + setMode('light')}>Light + setMode('dark')}>Dark + resetMode()}>System + + diff --git a/src/lib/components/site/nav/index.ts b/src/lib/components/site/nav/index.ts new file mode 100644 index 0000000..c17dc31 --- /dev/null +++ b/src/lib/components/site/nav/index.ts @@ -0,0 +1,2 @@ +export { default as MainNav } from './main-nav.svelte'; +export { default as MobileNav } from './mobile-nav.svelte'; diff --git a/src/lib/components/site/nav/main-nav.svelte b/src/lib/components/site/nav/main-nav.svelte new file mode 100644 index 0000000..6d71287 --- /dev/null +++ b/src/lib/components/site/nav/main-nav.svelte @@ -0,0 +1,25 @@ + + + diff --git a/src/lib/components/site/nav/mobile-link.svelte b/src/lib/components/site/nav/mobile-link.svelte new file mode 100644 index 0000000..608aa0d --- /dev/null +++ b/src/lib/components/site/nav/mobile-link.svelte @@ -0,0 +1,23 @@ + + + (open = false)} + class={cn( + $page.url.pathname === href ? 'text-foreground' : 'text-foreground/60', + 'hover:text-foreground', + className + )} + {...$$restProps} +> + + diff --git a/src/lib/components/site/nav/mobile-nav.svelte b/src/lib/components/site/nav/mobile-nav.svelte new file mode 100644 index 0000000..5712101 --- /dev/null +++ b/src/lib/components/site/nav/mobile-nav.svelte @@ -0,0 +1,68 @@ + + + + + + + + + Logo icon (return home) +
+ +
+ {siteConfig.name} +
+
+
+ {#each navConfig.mainNav as navItem, index (navItem + index.toString())} + {#if navItem.href && (navItem.auth == authenticated || navItem.always)} + + {navItem.title} + + {/if} + {/each} +
+
+ {#each navConfig.sidebarNav as navItem, index (index)} +
+

{navItem.title}

+ {#if navItem?.items?.length} + {#each navItem.items as item} + {#if !item.disabled && item.href} + + {item.title} + {#if item.label} + + {item.label} + + {/if} + + {/if} + {/each} + {/if} +
+ {/each} +
+
+
+
diff --git a/src/lib/components/site/nav/user-nav.svelte b/src/lib/components/site/nav/user-nav.svelte new file mode 100644 index 0000000..4ebc05a --- /dev/null +++ b/src/lib/components/site/nav/user-nav.svelte @@ -0,0 +1,49 @@ + + +{#if authenticated} + + + + + + +
+

{user?.name || user?.username}

+

{user?.email}

+
+
+ + Dashboards + Connectors + + + Settings + + + Profile + Appearance + Notifications + + + + Log out + ⇧⌘Q + +
+
+{:else} + +{/if} diff --git a/src/lib/components/site/particles.svelte b/src/lib/components/site/particles.svelte new file mode 100644 index 0000000..675f3ab --- /dev/null +++ b/src/lib/components/site/particles.svelte @@ -0,0 +1,248 @@ + + + diff --git a/src/lib/components/site/site-footer.svelte b/src/lib/components/site/site-footer.svelte new file mode 100644 index 0000000..742db1c --- /dev/null +++ b/src/lib/components/site/site-footer.svelte @@ -0,0 +1,12 @@ + + +
+
+

+ © {new Date().getFullYear()} — {siteConfig.name} by {siteConfig.author}. Licensed + under GPL-3.0. +

+
+
diff --git a/src/lib/components/site/site-navbar.svelte b/src/lib/components/site/site-navbar.svelte new file mode 100644 index 0000000..bdfc3cb --- /dev/null +++ b/src/lib/components/site/site-navbar.svelte @@ -0,0 +1,28 @@ + + +
+ +
diff --git a/src/lib/components/site/tailwind-indicator.svelte b/src/lib/components/site/tailwind-indicator.svelte new file mode 100644 index 0000000..61f136d --- /dev/null +++ b/src/lib/components/site/tailwind-indicator.svelte @@ -0,0 +1,14 @@ + + + + + + + + diff --git a/src/lib/components/ui/alert/alert-description.svelte b/src/lib/components/ui/alert/alert-description.svelte new file mode 100644 index 0000000..1f45185 --- /dev/null +++ b/src/lib/components/ui/alert/alert-description.svelte @@ -0,0 +1,13 @@ + + +
+ +
diff --git a/src/lib/components/ui/alert/alert-title.svelte b/src/lib/components/ui/alert/alert-title.svelte new file mode 100644 index 0000000..03c6112 --- /dev/null +++ b/src/lib/components/ui/alert/alert-title.svelte @@ -0,0 +1,21 @@ + + + + + diff --git a/src/lib/components/ui/alert/alert.svelte b/src/lib/components/ui/alert/alert.svelte new file mode 100644 index 0000000..c97ce68 --- /dev/null +++ b/src/lib/components/ui/alert/alert.svelte @@ -0,0 +1,17 @@ + + + diff --git a/src/lib/components/ui/alert/index.ts b/src/lib/components/ui/alert/index.ts new file mode 100644 index 0000000..6a2deb0 --- /dev/null +++ b/src/lib/components/ui/alert/index.ts @@ -0,0 +1,32 @@ +import { tv, type VariantProps } from 'tailwind-variants'; + +import Root from './alert.svelte'; +import Description from './alert-description.svelte'; +import Title from './alert-title.svelte'; + +export const alertVariants = tv({ + base: 'relative w-full rounded-lg border px-4 py-3 text-sm [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground', + variants: { + variant: { + default: 'bg-background text-foreground', + destructive: + 'border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive' + } + }, + defaultVariants: { + variant: 'default' + } +}); + +export type Variant = VariantProps['variant']; +export type HeadingLevel = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6'; + +export { + Root, + Description, + Title, + // + Root as Alert, + Description as AlertDescription, + Title as AlertTitle +}; diff --git a/src/lib/components/ui/avatar/avatar-fallback.svelte b/src/lib/components/ui/avatar/avatar-fallback.svelte new file mode 100644 index 0000000..8939822 --- /dev/null +++ b/src/lib/components/ui/avatar/avatar-fallback.svelte @@ -0,0 +1,16 @@ + + + + + diff --git a/src/lib/components/ui/avatar/avatar-image.svelte b/src/lib/components/ui/avatar/avatar-image.svelte new file mode 100644 index 0000000..d1bc205 --- /dev/null +++ b/src/lib/components/ui/avatar/avatar-image.svelte @@ -0,0 +1,18 @@ + + + diff --git a/src/lib/components/ui/avatar/avatar.svelte b/src/lib/components/ui/avatar/avatar.svelte new file mode 100644 index 0000000..1610a75 --- /dev/null +++ b/src/lib/components/ui/avatar/avatar.svelte @@ -0,0 +1,18 @@ + + + + + diff --git a/src/lib/components/ui/avatar/index.ts b/src/lib/components/ui/avatar/index.ts new file mode 100644 index 0000000..9585f8a --- /dev/null +++ b/src/lib/components/ui/avatar/index.ts @@ -0,0 +1,13 @@ +import Root from './avatar.svelte'; +import Image from './avatar-image.svelte'; +import Fallback from './avatar-fallback.svelte'; + +export { + Root, + Image, + Fallback, + // + Root as Avatar, + Image as AvatarImage, + Fallback as AvatarFallback +}; diff --git a/src/lib/components/ui/badge/badge.svelte b/src/lib/components/ui/badge/badge.svelte new file mode 100644 index 0000000..836c73b --- /dev/null +++ b/src/lib/components/ui/badge/badge.svelte @@ -0,0 +1,18 @@ + + + + + diff --git a/src/lib/components/ui/badge/index.ts b/src/lib/components/ui/badge/index.ts new file mode 100644 index 0000000..863efc4 --- /dev/null +++ b/src/lib/components/ui/badge/index.ts @@ -0,0 +1,20 @@ +import { tv, type VariantProps } from 'tailwind-variants'; + +export { default as Badge } from './badge.svelte'; +export const badgeVariants = tv({ + base: '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 select-none', + variants: { + variant: { + default: 'border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80', + secondary: 'border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80', + destructive: + 'border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80', + outline: 'text-foreground' + } + }, + defaultVariants: { + variant: 'default' + } +}); + +export type Variant = VariantProps['variant']; diff --git a/src/lib/components/ui/button/button.svelte b/src/lib/components/ui/button/button.svelte new file mode 100644 index 0000000..fb61a87 --- /dev/null +++ b/src/lib/components/ui/button/button.svelte @@ -0,0 +1,25 @@ + + + + + diff --git a/src/lib/components/ui/button/index.ts b/src/lib/components/ui/button/index.ts new file mode 100644 index 0000000..e300752 --- /dev/null +++ b/src/lib/components/ui/button/index.ts @@ -0,0 +1,49 @@ +import type { Button as ButtonPrimitive } from 'bits-ui'; +import { tv, type VariantProps } from 'tailwind-variants'; +import Root from './button.svelte'; + +const buttonVariants = tv({ + base: 'inline-flex items-center justify-center rounded-md text-sm font-medium whitespace-nowrap transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50', + variants: { + variant: { + default: 'bg-primary text-primary-foreground shadow hover:bg-primary/90', + destructive: 'bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90', + outline: + 'border border-input bg-transparent shadow-sm hover:bg-accent hover:text-accent-foreground', + secondary: 'bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80', + ghost: 'hover:bg-accent hover:text-accent-foreground', + link: 'text-primary underline-offset-4 hover:underline' + }, + size: { + default: 'h-9 px-4 py-2', + sm: 'h-8 rounded-md px-3 text-xs', + lg: 'h-10 rounded-md px-8', + icon: 'h-9 w-9' + } + }, + defaultVariants: { + variant: 'default', + size: 'default' + } +}); + +type Variant = VariantProps['variant']; +type Size = VariantProps['size']; + +type Props = ButtonPrimitive.Props & { + variant?: Variant; + size?: Size; +}; + +type Events = ButtonPrimitive.Events; + +export { + Root, + type Props, + type Events, + // + Root as Button, + type Props as ButtonProps, + type Events as ButtonEvents, + buttonVariants +}; diff --git a/src/lib/components/ui/card/card-content.svelte b/src/lib/components/ui/card/card-content.svelte new file mode 100644 index 0000000..c87d58a --- /dev/null +++ b/src/lib/components/ui/card/card-content.svelte @@ -0,0 +1,13 @@ + + +
+ +
diff --git a/src/lib/components/ui/card/card-description.svelte b/src/lib/components/ui/card/card-description.svelte new file mode 100644 index 0000000..ffc81f9 --- /dev/null +++ b/src/lib/components/ui/card/card-description.svelte @@ -0,0 +1,13 @@ + + +

+ +

diff --git a/src/lib/components/ui/card/card-footer.svelte b/src/lib/components/ui/card/card-footer.svelte new file mode 100644 index 0000000..414ded9 --- /dev/null +++ b/src/lib/components/ui/card/card-footer.svelte @@ -0,0 +1,13 @@ + + +
+ +
diff --git a/src/lib/components/ui/card/card-header.svelte b/src/lib/components/ui/card/card-header.svelte new file mode 100644 index 0000000..8079df3 --- /dev/null +++ b/src/lib/components/ui/card/card-header.svelte @@ -0,0 +1,13 @@ + + +
+ +
diff --git a/src/lib/components/ui/card/card-title.svelte b/src/lib/components/ui/card/card-title.svelte new file mode 100644 index 0000000..d0d98c0 --- /dev/null +++ b/src/lib/components/ui/card/card-title.svelte @@ -0,0 +1,21 @@ + + + + + diff --git a/src/lib/components/ui/card/card.svelte b/src/lib/components/ui/card/card.svelte new file mode 100644 index 0000000..8bc551c --- /dev/null +++ b/src/lib/components/ui/card/card.svelte @@ -0,0 +1,22 @@ + + + +
+ +
diff --git a/src/lib/components/ui/card/index.ts b/src/lib/components/ui/card/index.ts new file mode 100644 index 0000000..86c5408 --- /dev/null +++ b/src/lib/components/ui/card/index.ts @@ -0,0 +1,24 @@ +import Root from './card.svelte'; +import Content from './card-content.svelte'; +import Description from './card-description.svelte'; +import Footer from './card-footer.svelte'; +import Header from './card-header.svelte'; +import Title from './card-title.svelte'; + +export { + Root, + Content, + Description, + Footer, + Header, + Title, + // + Root as Card, + Content as CardContent, + Description as CardDescription, + Footer as CardFooter, + Header as CardHeader, + Title as CardTitle +}; + +export type HeadingLevel = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6'; diff --git a/src/lib/components/ui/checkbox/checkbox.svelte b/src/lib/components/ui/checkbox/checkbox.svelte new file mode 100644 index 0000000..c892194 --- /dev/null +++ b/src/lib/components/ui/checkbox/checkbox.svelte @@ -0,0 +1,34 @@ + + + + + {#if isIndeterminate} + + {:else} + + {/if} + + diff --git a/src/lib/components/ui/checkbox/index.ts b/src/lib/components/ui/checkbox/index.ts new file mode 100644 index 0000000..5c27671 --- /dev/null +++ b/src/lib/components/ui/checkbox/index.ts @@ -0,0 +1,6 @@ +import Root from './checkbox.svelte'; +export { + Root, + // + Root as Checkbox +}; diff --git a/src/lib/components/ui/command/command-dialog.svelte b/src/lib/components/ui/command/command-dialog.svelte new file mode 100644 index 0000000..9fdddf3 --- /dev/null +++ b/src/lib/components/ui/command/command-dialog.svelte @@ -0,0 +1,23 @@ + + + + + + + + + diff --git a/src/lib/components/ui/command/command-empty.svelte b/src/lib/components/ui/command/command-empty.svelte new file mode 100644 index 0000000..aa18469 --- /dev/null +++ b/src/lib/components/ui/command/command-empty.svelte @@ -0,0 +1,12 @@ + + + + + diff --git a/src/lib/components/ui/command/command-group.svelte b/src/lib/components/ui/command/command-group.svelte new file mode 100644 index 0000000..a7a5dba --- /dev/null +++ b/src/lib/components/ui/command/command-group.svelte @@ -0,0 +1,18 @@ + + + + + diff --git a/src/lib/components/ui/command/command-input.svelte b/src/lib/components/ui/command/command-input.svelte new file mode 100644 index 0000000..3380773 --- /dev/null +++ b/src/lib/components/ui/command/command-input.svelte @@ -0,0 +1,23 @@ + + +
+ + +
diff --git a/src/lib/components/ui/command/command-item.svelte b/src/lib/components/ui/command/command-item.svelte new file mode 100644 index 0000000..daacb32 --- /dev/null +++ b/src/lib/components/ui/command/command-item.svelte @@ -0,0 +1,19 @@ + + + + + diff --git a/src/lib/components/ui/command/command-list.svelte b/src/lib/components/ui/command/command-list.svelte new file mode 100644 index 0000000..b8fd56a --- /dev/null +++ b/src/lib/components/ui/command/command-list.svelte @@ -0,0 +1,15 @@ + + + + + diff --git a/src/lib/components/ui/command/command-separator.svelte b/src/lib/components/ui/command/command-separator.svelte new file mode 100644 index 0000000..3fe5d37 --- /dev/null +++ b/src/lib/components/ui/command/command-separator.svelte @@ -0,0 +1,10 @@ + + + diff --git a/src/lib/components/ui/command/command-shortcut.svelte b/src/lib/components/ui/command/command-shortcut.svelte new file mode 100644 index 0000000..132fb77 --- /dev/null +++ b/src/lib/components/ui/command/command-shortcut.svelte @@ -0,0 +1,16 @@ + + + + + diff --git a/src/lib/components/ui/command/command.svelte b/src/lib/components/ui/command/command.svelte new file mode 100644 index 0000000..5961d8c --- /dev/null +++ b/src/lib/components/ui/command/command.svelte @@ -0,0 +1,22 @@ + + + + + diff --git a/src/lib/components/ui/command/index.ts b/src/lib/components/ui/command/index.ts new file mode 100644 index 0000000..03922d5 --- /dev/null +++ b/src/lib/components/ui/command/index.ts @@ -0,0 +1,37 @@ +import { Command as CommandPrimitive } from 'cmdk-sv'; + +import Root from './command.svelte'; +import Dialog from './command-dialog.svelte'; +import Empty from './command-empty.svelte'; +import Group from './command-group.svelte'; +import Item from './command-item.svelte'; +import Input from './command-input.svelte'; +import List from './command-list.svelte'; +import Separator from './command-separator.svelte'; +import Shortcut from './command-shortcut.svelte'; + +const Loading = CommandPrimitive.Loading; + +export { + Root, + Dialog, + Empty, + Group, + Item, + Input, + List, + Separator, + Shortcut, + Loading, + // + Root as Command, + Dialog as CommandDialog, + Empty as CommandEmpty, + Group as CommandGroup, + Item as CommandItem, + Input as CommandInput, + List as CommandList, + Separator as CommandSeparator, + Shortcut as CommandShortcut, + Loading as CommandLoading +}; diff --git a/src/lib/components/ui/dialog/dialog-content.svelte b/src/lib/components/ui/dialog/dialog-content.svelte new file mode 100644 index 0000000..7eff526 --- /dev/null +++ b/src/lib/components/ui/dialog/dialog-content.svelte @@ -0,0 +1,36 @@ + + + + + + + + + Close + + + diff --git a/src/lib/components/ui/dialog/dialog-description.svelte b/src/lib/components/ui/dialog/dialog-description.svelte new file mode 100644 index 0000000..84d4e5f --- /dev/null +++ b/src/lib/components/ui/dialog/dialog-description.svelte @@ -0,0 +1,16 @@ + + + + + diff --git a/src/lib/components/ui/dialog/dialog-footer.svelte b/src/lib/components/ui/dialog/dialog-footer.svelte new file mode 100644 index 0000000..da4f517 --- /dev/null +++ b/src/lib/components/ui/dialog/dialog-footer.svelte @@ -0,0 +1,16 @@ + + +
+ +
diff --git a/src/lib/components/ui/dialog/dialog-header.svelte b/src/lib/components/ui/dialog/dialog-header.svelte new file mode 100644 index 0000000..e4b3608 --- /dev/null +++ b/src/lib/components/ui/dialog/dialog-header.svelte @@ -0,0 +1,13 @@ + + +
+ +
diff --git a/src/lib/components/ui/dialog/dialog-overlay.svelte b/src/lib/components/ui/dialog/dialog-overlay.svelte new file mode 100644 index 0000000..22c6488 --- /dev/null +++ b/src/lib/components/ui/dialog/dialog-overlay.svelte @@ -0,0 +1,21 @@ + + + diff --git a/src/lib/components/ui/dialog/dialog-portal.svelte b/src/lib/components/ui/dialog/dialog-portal.svelte new file mode 100644 index 0000000..a0ba1f1 --- /dev/null +++ b/src/lib/components/ui/dialog/dialog-portal.svelte @@ -0,0 +1,9 @@ + + + + + diff --git a/src/lib/components/ui/dialog/dialog-title.svelte b/src/lib/components/ui/dialog/dialog-title.svelte new file mode 100644 index 0000000..c133166 --- /dev/null +++ b/src/lib/components/ui/dialog/dialog-title.svelte @@ -0,0 +1,16 @@ + + + + + diff --git a/src/lib/components/ui/dialog/index.ts b/src/lib/components/ui/dialog/index.ts new file mode 100644 index 0000000..2a1a048 --- /dev/null +++ b/src/lib/components/ui/dialog/index.ts @@ -0,0 +1,34 @@ +import { Dialog as DialogPrimitive } from 'bits-ui'; + +const Root = DialogPrimitive.Root; +const Trigger = DialogPrimitive.Trigger; + +import Title from './dialog-title.svelte'; +import Portal from './dialog-portal.svelte'; +import Footer from './dialog-footer.svelte'; +import Header from './dialog-header.svelte'; +import Overlay from './dialog-overlay.svelte'; +import Content from './dialog-content.svelte'; +import Description from './dialog-description.svelte'; + +export { + Root, + Title, + Portal, + Footer, + Header, + Trigger, + Overlay, + Content, + Description, + // + Root as Dialog, + Title as DialogTitle, + Portal as DialogPortal, + Footer as DialogFooter, + Header as DialogHeader, + Trigger as DialogTrigger, + Overlay as DialogOverlay, + Content as DialogContent, + Description as DialogDescription +}; diff --git a/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte b/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte new file mode 100644 index 0000000..d813949 --- /dev/null +++ b/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte @@ -0,0 +1,35 @@ + + + + + + + + + + diff --git a/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte b/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte new file mode 100644 index 0000000..c153606 --- /dev/null +++ b/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte @@ -0,0 +1,26 @@ + + + + + diff --git a/src/lib/components/ui/dropdown-menu/dropdown-menu-item.svelte b/src/lib/components/ui/dropdown-menu/dropdown-menu-item.svelte new file mode 100644 index 0000000..7d0faad --- /dev/null +++ b/src/lib/components/ui/dropdown-menu/dropdown-menu-item.svelte @@ -0,0 +1,31 @@ + + + + + diff --git a/src/lib/components/ui/dropdown-menu/dropdown-menu-label.svelte b/src/lib/components/ui/dropdown-menu/dropdown-menu-label.svelte new file mode 100644 index 0000000..f3ce65f --- /dev/null +++ b/src/lib/components/ui/dropdown-menu/dropdown-menu-label.svelte @@ -0,0 +1,19 @@ + + + + + diff --git a/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte b/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte new file mode 100644 index 0000000..dd67d44 --- /dev/null +++ b/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte @@ -0,0 +1,11 @@ + + + + + diff --git a/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte b/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte new file mode 100644 index 0000000..3fbe11a --- /dev/null +++ b/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte @@ -0,0 +1,35 @@ + + + + + + + + + + diff --git a/src/lib/components/ui/dropdown-menu/dropdown-menu-separator.svelte b/src/lib/components/ui/dropdown-menu/dropdown-menu-separator.svelte new file mode 100644 index 0000000..180fe79 --- /dev/null +++ b/src/lib/components/ui/dropdown-menu/dropdown-menu-separator.svelte @@ -0,0 +1,14 @@ + + + diff --git a/src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte b/src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte new file mode 100644 index 0000000..257a0e2 --- /dev/null +++ b/src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte @@ -0,0 +1,13 @@ + + + + + diff --git a/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte b/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte new file mode 100644 index 0000000..d86f063 --- /dev/null +++ b/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte @@ -0,0 +1,29 @@ + + + + + diff --git a/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte b/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte new file mode 100644 index 0000000..77cddb5 --- /dev/null +++ b/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte @@ -0,0 +1,32 @@ + + + + + + diff --git a/src/lib/components/ui/dropdown-menu/index.ts b/src/lib/components/ui/dropdown-menu/index.ts new file mode 100644 index 0000000..52c1f3c --- /dev/null +++ b/src/lib/components/ui/dropdown-menu/index.ts @@ -0,0 +1,48 @@ +import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui'; +import Item from './dropdown-menu-item.svelte'; +import Label from './dropdown-menu-label.svelte'; +import Content from './dropdown-menu-content.svelte'; +import Shortcut from './dropdown-menu-shortcut.svelte'; +import RadioItem from './dropdown-menu-radio-item.svelte'; +import Separator from './dropdown-menu-separator.svelte'; +import RadioGroup from './dropdown-menu-radio-group.svelte'; +import SubContent from './dropdown-menu-sub-content.svelte'; +import SubTrigger from './dropdown-menu-sub-trigger.svelte'; +import CheckboxItem from './dropdown-menu-checkbox-item.svelte'; + +const Sub = DropdownMenuPrimitive.Sub; +const Root = DropdownMenuPrimitive.Root; +const Trigger = DropdownMenuPrimitive.Trigger; +const Group = DropdownMenuPrimitive.Group; + +export { + Sub, + Root, + Item, + Label, + Group, + Trigger, + Content, + Shortcut, + Separator, + RadioItem, + SubContent, + SubTrigger, + RadioGroup, + CheckboxItem, + // + Root as DropdownMenu, + Sub as DropdownMenuSub, + Item as DropdownMenuItem, + Label as DropdownMenuLabel, + Group as DropdownMenuGroup, + Content as DropdownMenuContent, + Trigger as DropdownMenuTrigger, + Shortcut as DropdownMenuShortcut, + RadioItem as DropdownMenuRadioItem, + Separator as DropdownMenuSeparator, + RadioGroup as DropdownMenuRadioGroup, + SubContent as DropdownMenuSubContent, + SubTrigger as DropdownMenuSubTrigger, + CheckboxItem as DropdownMenuCheckboxItem +}; diff --git a/src/lib/components/ui/form/form-button.svelte b/src/lib/components/ui/form/form-button.svelte new file mode 100644 index 0000000..a6638e4 --- /dev/null +++ b/src/lib/components/ui/form/form-button.svelte @@ -0,0 +1,9 @@ + + + + + diff --git a/src/lib/components/ui/form/form-checkbox.svelte b/src/lib/components/ui/form/form-checkbox.svelte new file mode 100644 index 0000000..d667441 --- /dev/null +++ b/src/lib/components/ui/form/form-checkbox.svelte @@ -0,0 +1,26 @@ + + + { + onCheckedChange?.(v); + setValue(v); + }} + {...$$restProps} + on:click + on:keydown +/> + diff --git a/src/lib/components/ui/form/form-description.svelte b/src/lib/components/ui/form/form-description.svelte new file mode 100644 index 0000000..0ba02ca --- /dev/null +++ b/src/lib/components/ui/form/form-description.svelte @@ -0,0 +1,16 @@ + + + + + diff --git a/src/lib/components/ui/form/form-input.svelte b/src/lib/components/ui/form/form-input.svelte new file mode 100644 index 0000000..5575015 --- /dev/null +++ b/src/lib/components/ui/form/form-input.svelte @@ -0,0 +1,28 @@ + + + diff --git a/src/lib/components/ui/form/form-item.svelte b/src/lib/components/ui/form/form-item.svelte new file mode 100644 index 0000000..11b9079 --- /dev/null +++ b/src/lib/components/ui/form/form-item.svelte @@ -0,0 +1,12 @@ + + +
+ +
diff --git a/src/lib/components/ui/form/form-label.svelte b/src/lib/components/ui/form/form-label.svelte new file mode 100644 index 0000000..8608970 --- /dev/null +++ b/src/lib/components/ui/form/form-label.svelte @@ -0,0 +1,17 @@ + + + diff --git a/src/lib/components/ui/form/form-native-select.svelte b/src/lib/components/ui/form/form-native-select.svelte new file mode 100644 index 0000000..2e04aa7 --- /dev/null +++ b/src/lib/components/ui/form/form-native-select.svelte @@ -0,0 +1,26 @@ + + +
+ + + + +
diff --git a/src/lib/components/ui/form/form-radio-group.svelte b/src/lib/components/ui/form/form-radio-group.svelte new file mode 100644 index 0000000..c3f70bc --- /dev/null +++ b/src/lib/components/ui/form/form-radio-group.svelte @@ -0,0 +1,22 @@ + + + { + onValueChange?.(v); + setValue(v); + }} + {...$$restProps} +> + + + diff --git a/src/lib/components/ui/form/form-select-trigger.svelte b/src/lib/components/ui/form/form-select-trigger.svelte new file mode 100644 index 0000000..f389a02 --- /dev/null +++ b/src/lib/components/ui/form/form-select-trigger.svelte @@ -0,0 +1,18 @@ + + + + + + + diff --git a/src/lib/components/ui/form/form-select.svelte b/src/lib/components/ui/form/form-select.svelte new file mode 100644 index 0000000..b884f92 --- /dev/null +++ b/src/lib/components/ui/form/form-select.svelte @@ -0,0 +1,20 @@ + + + { + onSelectedChange?.(v); + setValue(v ? v.value : undefined); + }} + {...$$restProps} +> + + + diff --git a/src/lib/components/ui/form/form-switch.svelte b/src/lib/components/ui/form/form-switch.svelte new file mode 100644 index 0000000..ff9d0a6 --- /dev/null +++ b/src/lib/components/ui/form/form-switch.svelte @@ -0,0 +1,24 @@ + + + { + onCheckedChange?.(v); + setValue(v); + }} + {...$$restProps} + on:click + on:keydown +/> + diff --git a/src/lib/components/ui/form/form-textarea.svelte b/src/lib/components/ui/form/form-textarea.svelte new file mode 100644 index 0000000..5d34734 --- /dev/null +++ b/src/lib/components/ui/form/form-textarea.svelte @@ -0,0 +1,29 @@ + + +